指针
地址编号: 存的是值
指针: 存储的是地址编号值 的数据类型 是地址编号的数据类型,强调的是类型
指针变量: 数据类型为指针的变量,用于保存地址编号
地址编号
概述:
地址编号是内存每一个字节的编号统称。
如
int n = 10;
在内存分配了4个字节
每个进程(运行着的程序)都有寻址范围
32位机的寻址范围时4G,0x00 00 00 00~0xff ff ff ff.
系统会为每一个字节分配一个32位的地址编号。便于系统管理内存
32位机的地址编号占4字节
64位机的地址编号占8字节
指针与指针变量
指针:是地址编号的数据类型,强调的是类型
指针变量:数据类型为指针的变量,用于保存地址编号
注意
指针变量定义
语法:
数据类型 * 变量名;
注意:
数据类型: 指针变量指向的地址中存储的数据 的数据类型
1,不同的指针只能存储对应的数据类型的地址
如:
整型的指针变量,只能存放整型变量的地址
2,如果在一行中定义多个指针变量,每个指针变量前面都需要加 * 来修饰
如:
int *p1; char *p2; float *p3; double *p4; void *p5; ... int nums[] = {1,2,3,4,5}; int *p6 = nums; int *p1,*p2,*p3;
指针变量的初始化
注意
1,当指针变量为局部变量,并没有为其赋初始值,此时因为是局部变量,所以系统为其赋 值为随机数,此时我们并不知道指针变量存
储的地址是什么.这种指针我们称为野指针
2,指针变量没有指向合法的地址建议初始化为NULL,这种指针我们称为空指针
int *p = NULL; //空指针: 是NULL 不是0 0也有地址 而NULL 没有
int *p; //因为没有初始化所有初始化随机 所以就不知道指向哪里了 就是野指针
指针运算符
&
作用:取地址
int a = 10; int *p = &a; printf("%p\n",p); //此时打印的是p的值即a的地址
注意:
栈:静态全局区可取地址
堆,常量池,代码区不可取
*
作用:取值
printf("%d\n",*p); //将p中存储的地址中的值获取
作用**:改值**
*p = 值;
注意事项
野指针与空指针取值操作会出现段错误
哪怕用空指针也不用野指针 虽然空必然报错 但是错误是显性的 野指针对程序一定是坏的
因为 空指针 必报错 而野指针可能会在合法地址内也可能不在 所以 就很难排错
int *p = NULL; //空指针: 是NULL 不是0 0也有地址 而NULL 没有
int *p; //因为没有初始化所有初始化随机 所以就不知道指向哪里了 就是野指针
指针的数据类型
int *p;
char *p;
口诀:
将指针变量名的变量名去除,剩余的就是指针变量的数据类型。
int *p 去掉变量名p 那就是 int *
指针存储的数据类型
int *p;
char *p;
口诀:
将指针变量的 变量名与最近的一个* 去除,剩余的就是指针变量存储地址对应的数据类型int *p; 去掉 *p 就是int 型
作用:
分析等号两边数据类型一致
二级指针
概念:
又名 指针的指针因为指针变量本质也是一个变量,在32位机中占4字节,64位机占8字节
如:
int a = 10; //假设此时a在栈内存中的地址编号为0x00 00 00 01,长度4字节 int *p = &a; //将a的地址赋值给变量p,此时p存储的地址为a的地址即0x00 00 00 01 p本身也需要占用内存,其地址假设为 0x 00 00 00 06 int **p = &p; // 将p的地址 0x 00 00 00 06赋值给p2
1,二级指针使用 *取出的是什么?取出的是存储的一级指针本身的地址,不是一级指针存储的地址int num01=10;int num02=1;int *pl = &num01;int *p2 = &num02;*pl = 100; //通过 * 但核心还是地址来改变值printf("num01 = %d\n",num01); // 100 因为使用取值与改值 *将指针p存储的数据10进行了改值printf("num02 = %d\n",num02); // 1int **p3 =&p1; //将p1本身的地址赋值给p3 此时p3存储的是p1本身的地址*p3 = p2; // 将 p2 的地址赋值给 p3printf("**p3 = %d\n",**p3); // 1 **p 是* *p3 也就是取*p3地址存的值 还是num02
void与指针
void的作用:
万能指针的作用 核心作用就是作为函数的形参。
1 void 作为函数的返回值类型,表示没有返回值或返回值为NULL;2 void 与指针结合称为万能指针 ,可有存储任何一种类型变量的地址。但是不能直接对 void *p 中的p 直接取值 因为 无法确定p的取值宽度 。int num = 10; int *p1 = # char c = 'a'; char *p2 = &c; void *p3 = # void *p4 = &c; p3 = &c; //修改p3存储的地址
万能指针的作用:
- 核心作用就是作为函数的形参。
万能指针的注意事项:
- 不能直接对 void *p 中的p 直接取值 因为 无法确定p的取值宽度 。
const与指针
常量指针
可以修改其存储的地址 ,无法修改值。
常量指针 是指针 可以修改其存储的地址,但是无法修改其存储的值。
语法:
数据类型 const *指针名称; 或者 const 数据类型 *指针名称;
示例:
#include <stdio.h> int main(int argc, char const *argv[]) {int a = 10;int b = 1;//常量指针,本质是指针,可以修改其存储的地址,但是无法修改其存储的地址对应的值//const int *p = &a; 两种写法 写法aint const *p = &a; //写法bp = &b;//改变p存储的地址//*p = 100;//改变p存储的地址对应的值,此处报错return 0; }
指针常量
可以修改其存储的值 ,无法修改存储地址。
指针常量是常量 该指针存储的地址无法被修改,但是可以修改其存储的地址对应的值。
语法:
数据类型 * const 指针名称;int * const p;
常量指针常量
无法修改其存储的值 ,也无法修改存储地址。
概念: 常量指针指向常量
语法:
const 数据类型 * const 指针名; 或 数据类型 const * const 指针名;
示例
#include <stdio.h> int main(int argc, char const *argv[]) {int a = 10;int b = 1;//常量指针常量//const int * const p = &a;int const * const p = &a;p = &b;//不可以改变p存储的地址*p = 100;//也不可以改变p存储的地址对应的值printf("a = %d\n",a);return 0; }
数组与指针
数组名的本质
数组名 就是 数组中第一个元素的首地址
printf("arr的地址: %p\n",arr); // 求数组名地址
printf("arr[0]地址:%p\n",arr[0]); //求地址首地址// 发现结果一样
数组名 就是 数组中第一个元素的首地址
#include <stdio.h> int main(int argc, char const *argv[]) {// 证明数组名就是数组中第一个元素的地址int arr[] = {1, 3, 5, 7, 10};printf("arr的地址: %p\n", arr);// 使用 (void *) 强制转换为 十六进制地址数printf("arr[0]地址:%p\n", (void *)&arr[0]);// 一维数组怎么操作 二维完全可以复刻 先定义指针赋值// 指针可以通过下标访问数组元素,这与数组名的使用方式相同:// 定义一个指向int的指针p,初始化为数组arr的首地址int *p = arr;printf("arr[1] = %d\n", arr[1]); // 3 下标获取数组下标为1的元素printf("p[1] = %d\n", p[1]); // 3 指针访问数组下标为1的元素printf("*(p + 1)=%d\n", *(p + 3)); // 7 *(p+1) 等价于 p[1]printf("*P+1= %d\n", *p + 3); // 4 这里是对*p 也就是数字取值后加1 不是位移单位printf("p[0]= %d\n", p[0]); // 1 p[0] 等价*pprintf("数组arr的存储首地址为 %p\n", &arr); // 0x7ffd49bad8b0printf("p的地址为: %p\n", p); // 0x7ffd49bad8b0printf("p[0]的地址为: %p\n", &p[0]); // 0x7ffd49bad8b0printf(" p+1的地址为: %p\n", p + 1); // 0x7ffd49bad8b4// 这里的+1是加步长 步长取决数组数据类型的大小 比如是int 那就是加一步长 而此时的一步长就是4字节printf("*p+1=%d\n", *p + 1); // 2 核心思想 * 取值符 就是将此地址存储的数值拿出来// *p 就是将p存储的值拿出来了 也就是1 再后移一位 所以是2printf("*(p+1)=%d\n", *(p + 1)); // 3// 将当前位置+1个步长,取的是数组下标为1的元素return 0; }
事例2:
#include <stdio.h> int main(int argc, char const *argv[]) {char str[] = "hIc";char *p = str;// 此时数组中存储的元素为char,一个char的长度为1字节,顾此时1个步长=1字节printf("%c\n", *(p + 1));printf("%c\n", *(p + 2));printf("%c\n", *(p + 3));printf("%c\n", *p + 1); //*p获取的是其地址对应的值,其地址的值为h,h+1=iprintf("%c\n", (*p) + 1);return 0; }
指针数组与数组指针
概述
数组指针(数组的指针):指向数组,本质是指针
指针数组(存储指针的数组):本质是一个数组
示例1
#include <stdio.h> int main(int argc, char const *argv[]) {int nums[] = {1, 2, 3};// 数组指针(数组的指针,指向数组,本质是指针)int *p = nums;int a = 1;int b = 2;int c = 3;int *p11 = &a;int *p12 = &b;int *p13 = &c;// 指针数组(存储指针的数组,本质是一个数组)int *p2[] = {p11, p12, p13};return 0; }
示例2
#include <stdio.h> int main(int argc, char const *argv[]) {int nums01[] = {1, 2, 3, 4, 5};int nums02[] = {11, 22, 33, 44, 55};int nums03[] = {111, 222, 333, 444, 555};// 二维数组int nums04[][5] = {{1, 2, 3, 4, 5},{11, 22, 33, 44, 55},{111, 222, 333, 444, 555}};// 指针数组int *ps[] = {nums01, nums02, nums03};printf("nums04[0][1] = %d\n", nums04[0][1]);printf("ps[0][1] = %d\n", ps[0][1]);printf("nums04[1][1]的=%d\n", nums04[1][1]);printf("(*(ps+1))[1]=%d\n", (*(ps + 1))[1]);printf("*(*(ps+1)+1)=%d\n", *(*(ps + 1) + 1));return 0; }
示例3
#include <stdio.h> int main(int argc, char const *argv[]) {// char strs[][50] = {"GaoLei","WangChenHui","YueZheng"};char *strs[] = {"GaoLei", "WangChenHui", "YueZheng"};printf("strs[0] = %s\n", strs[0]); // GaoLeiprintf("strs[1] = %s\n", strs[1]); // WangChenHuiprintf("strs[2] = %s\n", strs[2]); // YueZhengprintf("*(strs+0) = %s\n", *(strs + 0)); //printf("*(strs+1) = %s\n", *(strs + 1));printf("*(strs+2) = %s\n", *(strs + 2));printf("(*(strs + 0))+3 = %s\n", (*(strs + 0)) + 3);printf("(*(strs + 0))[3] = %c\n", (*(strs + 0))[3]);return 0; }
示例4
#include <stdio.h> int main(int argc, char const *argv[]) {// char strs[][50] = {"GaoLei","WangChenHui","YueZheng"};char *strs[] = {"GaoLei", "WangChenHui", "YueZheng"};printf("strs[0]的地址=%p\n", &strs[0]);printf("strs的地址=%p\n", strs);printf("&strs的地址=%p\n", &strs);char *p;p = strs;// char **p2 = &p;// char **p2 = &strs;char **p2 = strs;printf("*p2 = %s\n", *p2);printf("**p2 = %c\n", **p2);printf("*(p2+1) = %s\n", *(p2 + 1));printf("*(p2+2) = %s\n", *(p2 + 2));printf("(*(p2+1))+4 = %s\n", (*(p2 + 1)) + 4);return 0; }
#include <stdio.h> int main(int argc, char const *argv[]) {char arr[] = "xxz";char *p = arr;// ASCII码值 是 97-122 也就是 小写 a-z// 大写字母的 ASCII 码值范围是 65 到 90printf("*p+1== %c\n", *p + 1); //*p得到的是 p[0] 也就是 x的ASCII 码值 120 然后加一return 0; }
步长:
通常指的是数组中元素的内存间隔。
步长 = 数组中存储的元素的数据类型所占的字节数 当为int 那加1 就是加一步长也就是加4 当为char 一步长就是1 加1 就是加1
函数与指针
函数名的本质
函数名本质上就是函数在代码区存储的首地址
指针作为形参
函数指针
作用:记录函数的地址语法:返回值类型 (*指针名称)(指向的函数的形参列表的数据类型) = 函数名;注意:形参列表的数据类型可有可无 函数指针调用函数指针名 (实参列表);变量名 = 指针名(实参列表);
事例:
#include <stdio.h> extern void add(int a, int b); extern void sub(int a, int b); extern void test(void (*p)(int, int)); int main(int argc, char const *argv[]) {printf("main函数的地址=%p\n", main);int (*p)(int, char *[]) = main;// 返回值类型 (*指针名称)(指向的函数的形参列表的数据类型) = 函数名;void (*p2)(int, int) = add;// 函数指针调用函数// 指针名(实参列表);// 变量名 = 指针名(实参列表);// p2 = sub;p2(1, 2);test(p2);return 0; } void test(void (*p)(int, int)) {p(10, 12); } void add(int a, int b) {printf("%d+%d=%d\n", a, b, a + b); } void sub(int a, int b) {printf("%d-%d=%d\n", a, b, a - b); }
指针作为形参
#include <stdio.h> extern void showArray(int nums[],int len); int main(int argc, char const *argv[]) {int nums[] = {1,2,3,4,5};showArray(nums,5);return 0; } void showArray(int *nums,int len) {for (int i = 0; i < len; i++){// printf("%d\t",nums[i]);printf("%d\t",*(nums+i));}printf("\n"); }
指针作为返回值
#include <stdio.h> #include <stdlib.h> #include <time.h> // extern int* getNums(); extern void getNums(int nums[],int len); int main(int argc, char const *argv[]) {srand(time(NULL));// int *p = getNums();// for (int i = 0; i < 10; i++)// {// printf("%d\t",p[i]);// }// printf("\n");int nums[10] = {0};getNums(nums,10); for (int i = 0; i < 10; i++) {printf("%d\t",nums[i]); }printf("\n");return 0; } // int* getNums() // { // static int nums[10] = {0}; // for (int i = 0; i < 10; i++) // { // nums[i] = rand() % 100; // } // return nums; // } void getNums(int nums[],int len) { for (int i = 0; i < len; i++){nums[i] = rand() % 100;} }