本文共 4434 字,大约阅读时间需要 14 分钟。
非法指针并不只是未分配的内存,而包括所有已经不能访问的内存,例如,指向已经返回的函数局部变量,越过数组边界的地址等。
//“[]”(后缀运算符,1级) > 指针取值*(一元运算符,2级)1、int *p1[10]; ≈ int* (p1[10]) 【内容是int*的数组p1[10]】 指针数组(储存指针的数组)原型定义:int* point[10]2、int (*p2)[10]; 理解为指针*p2→int[10] 指向数组的指针*p2 数组指针(指向数组的指针)原型定义:int (*)[10] 3、int* p(); 返回值为int型指针的函数指针p4、int **p 5、int *p=NULL; 空指针:没有定位内存的指针,值为null(0)没有访问权限 这就提醒别人不要对这个指针进行解引用的操作
int b[2][5]={ { 1,2,3,4,5},{ 6,7,8,9,10}};//&b的类型是 int(*)[2][5] 故&b+1是按照int(*)[2][5]的长度进行偏于int *p=(int *)(&b+1); *p👉b[2][0]显然越界了cout<<*(p-3)<
int a[3][3]={ { 1,2,3},{ 4,5,6},{ 7,8,9}}; foo(a);void foo(int b[][3]) 最初二维数组b[0][0]指向数组a[0][0]{ ++b; 执行++b后,b[0][0]指向a[1][0] b[1][1]=9; b[1][1]就是a[2][1],b[1][1]=9即a[2][1]=9}
#include#include int main(){ char a[10] = { 1,2,3,4,5,6,7,8,9,10 }; //类型char* //数组名:a 类型是cha* +1时按照char偏移 //数组首地址:&a[0] 类型是cha* +1时按照char偏移 //&a 类型是char(*)5]数组指针 +1时按照char[5]偏移 //char (*p)[5] = &a; 类型是char(*)5]数组指针 +1时按照char[5]偏移 //char *p = a; 类型是char* +1时按照char偏移 //char (*p)[5] = a; char*类型的值不能用于初始化char (*)[5] printf("数组a的首地址:%p\n", a); printf("a偏移:%p\n", a + 1); printf("首元素a[0]地址:%p\n", &a[0]); printf("首元素&a地址:%p\n", &a); //char(*p5)[5] = a; //char*类型的值不能用于初始化char (*)[5] char *p = a; //类型char* printf("%d\n", *p); //打印:1 此时p→a[1]=1 printf("后:%d 前:%d\n", *++p, *++p); //打印:后:3 前:3 此时p→a[3]=3 两个前缀++全部在入栈前生效 printf("%d\n", *p++); //打印:3 printf后执行++,故p→a[4]=4 *p++ == *(p++) 后缀++(后缀运算符,1级) 取值*(一元运算符,2级) printf("%d\n", *p); //打印:4 作证了上一行:后缀++的执行会放到整条语句结束后 printf("%d, %d\n", ++*p, *p); //打印:5,5 前缀++与所有数据入栈前生效 printf("%d, %d %d\n", ++*p, *p,*p++); //6 6 5 printf("%d\n", *p); //6 printf("%d, %d %d\n", *++p, *p, *p++); //7 7 6 printf("%d\n", *p); //7 //char *p2=(char*)a; //printf("%d",(int)strlen(p)); char ch; scanf_s("%c", &ch); return 0;}
1级运算符:后缀++为后缀运算符(1级) 从左向右2级运算符:前缀++和*均为一元运算符(2级),从右向左*p++、(*p)++、*++p、++*p 的区别int a[5]={ 1,2,3,4,5};int *p = a; 类型是char* +1时按照char偏移*p++ ≈ *(p++) 先取指针p指向的值(数组第一个元素1),再将指针指向自增1【从指向a[0]变为指向a[1]】; cout << *p++; // 结果为 1 cout <<(*p++); // 1(*p)++ 先取指针p指向的值(数组第一个元素1),再将该值自增1(数组第一个元素变为2 cout << (*p)++; // 1 cout <<((*p)++) //2*++p 先将指针p自增1(此时指向数组第二个元素),* 操作再取出该值 cout << *++p; // 2 cout <<(*++p) //2++*p 先取指针p指向的值(数组第一个元素1),再将该值自增1(数组第一个元素变为2) cout <<++*p; //2 cout <<(++*p) //2
=两侧类型必须一致
int a[4]={ 1,2,3,4};int *ptr=(int*)(&a+1);printf("%d",*(ptr-1)); //*(&a)=a[0] *ptr=*(&a+1)==a[5] *(ptr-1)=a[3] 1、a的类型是int * ++表现为:后一个成员(一维数组)or 后一行成员(二维数组) 2、&a[0]的类型是int * 指针 3、a[0]的类型是int +1表现为数值+1 int 4、&a的类型为int(*)[?] 以数组a[]的长度偏移 数组指针 5、ptr的类型是int* 以int=4字节偏移 指针若int *p1,*p2,m=5,n;则*p2=*p1; 错误 因为p2没有初始化(会存放任意的之前残留的地址,会报错,相当于野指针)【p2指向的值有了 但是p2指向的地址没有 无法把*p1存储起来】
①struct STRUCT p ,则p+1表示向右偏移sizeof(STRUCT) ② (int)p + 1则按照新类型int进行偏移 视作int 偏移一个int长度,比如:地址1000→地址1004
③(int)p + 1则按照新类型int*进行偏移 视作int 并增加1,如数据1000→数据1001#includeint main(){ //=两侧类型必须一致 int a[5]={ 1,2,3,4,5}; //a和&a[0]的类型是int * 只能赋值给int * 【类型必须一致】 //&a的类型是int(*)[5] 只能赋值给int(*)[5] 【类型必须一致】 int *p1=a; 或者 int *p2=&a[0]; //=两侧类型都是int * a+1、&a[0]、p1、p2均按照类型int *的长度+1 int (*p3)[5]=&a; //=两侧类型都是int (*)[?] &a、3均按照类型int (*)[?]的长度+1//=两侧类型如果不一致→则需要强制类型转换【但是还是按照各自当前的类型偏移】int *ptr=(int *)(&a+1);//右侧&a的类型是int(*)[5] +1表示增加int(*)[5] 的长度【后转化为int *】//左侧ptr 的类型是int * 则ptr的+1 -1等都是按照int *类型变化 //int *ptr2=a+1;//则指针p指向数组a的第二个元素 即a[5]的第二个元素地址 //int *ptr2=(int *)a+1; //int *ptr2=(int *)(a+1);//这2行的(int *)有没有都一样,强制类型转换成自己没有任何区别 return 0;}
(++p) ->n
(a+1) ->n (p+1) ->n 这3个均对应结构体a[1] 对应5
const的修饰目标:
1、修饰int(形式:const int 或者int const) 效果:变量数值不能变 2、修饰指针*p(形式:*const p) 效果:指针指向的变量不能变(指针指向固定内存地址) 声明时必须初始化,不然固定指向NULL就很蠢
1、const int *p; ≈≈ int const* p; 指向整形常量 的指针,它指向的值不能修改 示例: const char* name=" chen " ; 常量指针(指向常量的指针) name[3]='q';错误 常量内容不可修改,指向可以修改 name="lin";正确2、int * const p; 指向整形的常量指针 ,它不能在指向别的变量,但指向(变量)的值可以修改。 3、int const * const p; ≈≈ const int * const p; 指向整形常量的常量指针。它既不能再指向别的常量,指向的值也不能修改。
#includeusing namespace std; int main(void) { const int a = 10; int * p = (int *)(&a); *p = 20; cout<<"a = "< <<", *p = "<<*p<
转载地址:http://tyhrn.baihongyu.com/