- 指针类型的大小为8字节.
1.运算符优先级
1.::{作用域}
2.(){函数调用,类型构造:type(exp)},[]{下标},.{成员选择},->{成员选择}
3.++{后置},--{后置},typeid{类型id},explicit_cast{四种类型转换}
4.++{前置},--{前置},~{取反},!{逻辑非},-{一元负},+{一元正},\*{指针指向值},&{取地址},(){老式类型转换},sizeof{对象大小}
5.sizeof{类型或参数包的大小},new{分配内存},delete{释放内存},noexcept{能否抛出异常}
6.->\*{指向成员中的指针},.\*{指向成员中的指针}
7.*,/,%
8.+,-
9.<<,>>
10.<,>,<=,>=
11.==,!=
12.&
2. 指针自身类型
从语法的角度看, 只要把指针声明语句里的指针名去掉, 剩下的部分就是这个指针的类型.这是指针本身所具有的类型.
int *ptr; //指针类型是 int *
int **ptr1; //指针类型是 int **
int (*ptr2)[3]; //指针类型是 int(*)[3]
int *(*ptr3)[4]; //指针类型是 int*(*)[4]
3. 指针所指向的类型
当通过指针来访问指针所指向的内存区域时, 指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待.
从语法角度看, 只需把指针声明语句中的指针名和名字左边的指针声明符*去掉, 剩下的就是指针所指向的类型.
int *ptr; //指针所指向的类型是 int
int **ptr1; //指针所指向的类型是 int *
int (*ptr2)[3]; //指针所指向的类型是 int()[3]
int *(*ptr3)[3]; //指针所指向的类型是 int *()[3]
在指针的算数运算中, 指针所指向的类型有很大的作用, 它将决定到步长.
4. 指针的算数运算
指针可以加上或者减去一个整数, 指针的这种运算的意义和通常的数值的加减运算的意义是不一样的, 它是以单元为单位的.
当一个指针 ptr 加/减一个整数 n 后.
- 结果是一个新的指针
ptr_new
,ptr_new
的类型和ptr_old
类型相同. -
ptr_new
所指向的类型和ptr_old
所指向的类型也相同. -
ptr_new
的值将比ptr_old
的值增加/减少n*sizeof(ptr_old所指向的类型)
个字节.
也就是说 ptr_new
所指向的内存区域将比 ptr_old
所指向的内存区域向高/低地址方向移动了 n*sizeof(ptr_old所指向的类型)
个字节.
指针和指针进行加减: 两个指针无法进行加法运算, 因为相加厚, 得到的结果指向一个不知所向的地方.
指针自身可以进行加减1操作, 不过一般是用在数组中.
例
int arr[4] = {1,2,3,4};
int *arr1 = arr;
printf("arr1++: %p \n", arr1);
arr1++;
printf("arr1++: %p \n", arr1);
输出结果, ++后增加了4个字节.
arr1++: 0x16b5132e0
arr1++: 0x16b5132e4
int arr1 指针指向的类型. 去掉,去掉指针名称, 为int类型, n=1, n*size(int)= 4.所以增加了4个字节.
例子:
int a[5] = {1,2,3,4,5}
int *p = &a;
int *p1 = a;
这两个得到的值是一致的, 但是含义不同
&a 取的是数组的地址, a 直接取的是数组首地址的地址。
所以, p++ 与 p1++ 得到的结果也不同。
p++ 是整个数组长度++, 既增加 20个字节。
p1++ 是指针移动到数组下一位。
int *p[5] = {0} // 指针数组,存放指针, 指向数组首元素地址.
int (*p)[5] = {0} //数组指针, 指向整个数组。可以理解为 p是一个指针, 指向一个数组, 数组有5个元素, 每个元素都是int类型
数组指针和指针数组的快速记忆技巧
数组指针:int (*p)[5]
数组在前, 强调了数组, 存放数组.
指针数组:int *p[5]
指针在前, 强调了指针, 存放指针, 多个指针组成数组.
指向整个数组的是数组指针,
指向单个元素的是指针数组.
对数组进行取址与对普通变量取址不同.
- 对普通变量取址得到的值是指针的地址.
- 对数组取址得到的值是数组首元素的地址, 指向类型是整个数组
int a1 = 10;
int a2 = 20;
int a3 = 30;
int *arr[3] = {&a1, &a2, &a3};
//输出内容相同, 都为数组首元素地址
printf("%p\n", arr); //第一个元素的地址
printf("%p\n", &arr); //对数组进行取址,值为第一个元素的, 但是指向类型为整个数组
//指向地址相同, 但是输出的大小不同. 因为指向类型不同
printf("%d\n", sizeof(*arr)); //输出 8, 指向单个元素,单个元素类型为 int*,
printf("%d\n", sizeof(*&arr)); //输出24, 指向整个数组, 3个元素,元素类型为 int* s
5. 使用指针操作数组
int main() {
int a = 10;
int *p = &a;
printf("%d\n", &a);
//指针指向地址的值
printf("%d\n", *p);
//指针存储的值, 也就是a的地址.
printf("%d\n", p);
//指针的地址
printf("%d --\n", &p);
printf("---------\n");
int a1 = 10;
int a2 = 20;
int a3 = 30;
int *arr[3] = {&a1, &a2, &a3};
//输出内容相同, 都为数组首元素地址
printf("%p\n", arr); //第一个元素的地址
printf("%p\n", &arr); //对数组进行取址,值为第一个元素的, 但是指向类型为整个数组
//指向地址相同, 但是输出的大小不同. 因为指向类型不同
printf("%d\n", sizeof(*arr)); //输出 8, 指向单个元素,单个元素类型为 int*,
printf("%d\n", sizeof(*&arr)); //输出24, 指向整个数组, 3个元素,元素类型为 int* s
printf("---------\n");
//二维数组
int p1[2][5] = {1,2,3,4,5,6,7,8,9,10};
//值为第一行的地址, 也就是第一个元素的首地址, 但是指向类型为数组第一行
printf("p1 = %p\n", p1);
//值为第一行的首元素地址, 但是指向类型为整个二维数组
printf("&p1 = %p\n", &p1);
//值为第一行的首元素地址, 但是指向类型为单个元素的类型
printf("*p1 = %p\n", *p1);
//40
printf("*&p1 的长度为: %d 个字节\n", sizeof(*&p1));
//4
printf("*p1 的长度为: %d 个字节\n", sizeof(**p1));
//20
printf("p1 的长度为: %d 个字节\n", sizeof(*p1));
//二维数组取值-方式1 - 下标法
printf("value = %d \n", p1[1][0]);
//二维数组取值-方式2 - 指针法, 将p1看做2个一维数组, 每个一维数组又包含了5个元素.
//(*value1_p)[5] 表示声明value1_p 是一个指向具有 5 个元素数组的指针。
//value1_p = p1:这表示 value1_p 被赋值为 p1,存储的值为第一个一维数组的地址. 因此指针 value1_p 存储的值是p1首行的地址。p1[0]
int (*value1_p)[5] = p1;
//对 value1_p 执行加 1 操作时,即 (value1_p + 1),不是简单地将指针移动一个字节,因为它的指向类型是 int [5].
//所以是将指针向后移动 5 个整数的大小 即移动到数组 p1 的下一行的起始地址。
//换句话说,(value1_p + 1) 后得到了一个指向p1[1]的指针, 指针存储的值为 p1[1]的地址
//第一次解引用: 对指向p1[1]的指针取值, 得到了p1[1]的地址.
//第二次解引用: 得到了第一个元素的值. 既p1[1][0]
printf("value = %d \n", **(value1_p+1));
printf("value = %d \n", *(*(value1_p+1)+1));
return 0;
}