23一.指针运算
1.算术运算
①+,-
指针与整数相加:表示指针指向下个变量。
指针与整数相减:表示指针指向上个变量。
指针与指针相减:两个指针的元素间隔个数。
指针与指针不可以相加
②自增自减++,--
前缀自增自减++、--和解引用*的结合律是自右向左。
*p++/*p-- --> *(p++)
*++p/*--p --> *(++p)
p++:先引用后增加,先在p所在的表达式中使用p的当前值,后让p加1
++p:先增加后引用,让p先加1,然后在i所在的表达式中使用i的新值
++*p --> ++(*p)
++/--一定会改变变量的值,前两种改变指针的值,后一种改变元素的值。
简化遍历:用*p++
2.比较运算
==、!=、<、<=、>、>= 本质是比较内存中的地址。
指针的算术运算表示在一片连续空间上的移动。
指针的比较运算也是用于一片连续空间的地址比较。
二.指针类型
- 无论指向什么类型,所有指针的大小都是一样的,都是地址的大小。
- 指针类型转换
指向不同类型的指针不能直接相互赋值(特例void*),需要强制类型转换。
#includeint main(){ char* str = "abcdef"; int* p=(int*)str; p++; char* q = (char*)p; //强制类型转换 printf("%cn",*q); }
指针类型转换没有改变指针内的地址,也没有改变指针指向的值,只是改变了移动的单位长度。
3.void类型的指针
void*是一种很特别的指针,表示指向未知类型的指针,并不指定它是指向哪一种类型的数据,而是根据需要转换为所需数据类型。
int n = 0; int* p = &n; void* q = p; //直接转换 int* k = (int*) q; //强制转换
三.二维指针
1.一维指针与二维指针
指针是变量,变量有地址,指针也有地址。(三段论)
int n=10;
int *p = &n; // *p是指向int变量的指针
int* *pp = &p; // **p是指向int指针的指针
printf("&p:%p p:%p *p:%d",&p,p,*p);
printf("pp:%p *pp:%p **pp:%d",pp,*pp,**pp);
2.数组指针与指针数组
指向一个数组指针称为数组指针。
指针是一个类型,也可以组成一个数组,这样的数组称为指针数组。指针数组里面存放的是地址,指针数组是多个指针变量集合。
int* p={&a,&b,&c};
[]的优先级高于*,那么p先和[]结合,说明这是一个数组。再和int*结合,说明这个数组里的每个元素都是一个指针,每个元素都能保存一个地址。
3.二维数组与指针数组
指针数组与二维数组在遍历元素上是相同的。但是存在本质区别。arr[0],arr[1],arr[2]是数组名,parr[0],parr[1],parr[2]是指针,所以只是形式上的一直,内存表示上是不同。
4 二维指针与二维数组
int arr[] = {1,2,3,4,5};
int *p = arr; // OK(数组名是数组第一个元素的地址)
int* parr[] = {arr,arr+1,arr+2,arr+3,arr+4,arr+5};
int* *q = parr; // OK
指针数组数组名可以直接赋值给二维指针。
int arr[][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int** p = arr; // Warning
int* parr[] = {arr[0],arr[1],arr[2]};
int** q = arr; // OK
二维数组数组名不可以直接赋值给二维指针。
二维指针操作与指针数组可以认为完全一致;二维数组操作与指针数组部分一致(访问元素上一致;但是指针数组内存放地址可以修改,二维数组数组名表示地址不能修改);
四.常量指针 vs 指针常量
1.常量指针 const int *p / int const *p
const修饰的是*p,所以*p是常量,表示p指向的地址里的值不可修改,也就是说,*p里的值不能再重新赋值了,但是可以修改p指向的地址。
2.指针常量 int * const p
const修饰的是p,所以p是常量,表示p指向的地址不可修改,即p不能再指向别的地方了,但是可以修改p指向的这个地址里的值。
3.常量指针常量 const int * const p
两个const分别修饰了p和*p, 所以p和*p都是常量,表示p指向的地址不可修改,同时p指向的地址里的值也不可修改。
五.0地址
#includeint main(){ int *p = 0; printf("%dn",*p); }
0地址是内存中不能访问的地址。在C语言中,标准库定义NULL表示0地址。



