一、数组和指针
1.理解数组名
2.sizeof和数组名
3.&数组名
&arr在值上和arr、&arr[0]一样,但是从意义来说,&arr表示整个数组的地址,取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的),类型是 int (*)[4]。
下列代码结果上是一样的
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("&arr[0] = %p\n", &arr[0]);
printf("arr = %p\n", arr);
printf("&arr = %p\n", &arr);
return 0;
}
但是,arr+1、&arr[0]+1,都是移动四个字节,&arr+1则直接到数组末端
虽然&arr 和arr 的地址数值相同,但它们的类型和步长完全不同

int* p = &arr;
会有一个警报:“ int* ”与“int(*)[5]”的间接级别不同
p此时只能接收一个整型变量的地址,不能接收整个数组的地址

① printf("%p\n", p); → 输出 0057FE50
p 被赋值为 &arr,所以这里打印的是整个数组的地址,也就是数组首元素的地址。
② printf("%p\n", &arr); → 输出 0057FE50
&arr 就是数组本身的地址,和 p 的值完全一样,所以输出相同。
③ printf("%p\n", *(&arr)); → 输出 0057FE50
&arr 是 int (*)[4] 类型,对它解引用 *(&arr),就得到了数组 arr 本身。
数组名在表达式中会退化为首元素指针,所以 *(&arr) 等价于 arr,也就是 int* 类型,地址还是 0057FE50。
④ printf("%p\n", *(p)); → 输出 00000001
p 的类型是 int*(虽然它实际存的是数组地址),对它解引用 *(p),就会读取该地址上的4 字节数据。
数组首元素是 1,所以读取到的值是 1,用 %p 打印就显示为 00000001。
⑤ printf("%p\n", *(*(&arr))); → 输出 00000001
*(&arr) → 得到数组 arr(退化为 int*)。
0再对它解引用 *(*(&arr)) → 等价于 *arr,也就是数组的第一个元素 1。
用 %p 打印,结果就是 00000001。
二、数组指针
1.概念和定义
数据类型 (*指针名)[数组长度];例如 int(*p)[4] = &arr;(*p)表示p是一个指针[4]表示指向了一个长度为4的数组数组里面放的什么类型?int类型
2.指向二维数组的数组指针
二维数组数组名arr代表数组首行的地址


而&arr 代表整个二维数组的地址,*(&arr)——>arr ,整个数组首行的地址
*(*(&arr))——> 首行首元素的地址
3.通过数组指针打印二维数组

p + 1则能访问二维数组的下一行数组,(*p)+1访问数组首行的元素
所以 *(*(p+i)+j)等同于 p[i][j]
我们可以得到规律:
如arr[1][2] , 等同于*(*(arr+1)+2),其中arr代表数组首行的地址
三、一维数组传参
数组传参传递的是数组首元素的地址
void test(int arr[]) //参数写成数组形式,本质上还是指针
{
printf("%d\n", sizeof(arr));//计算一个指针变量大小
}
void test(int* arr) //参数写成指针形式
{
printf("%d\n", sizeof(arr)); //计算一个指针变量的大小
}
四、二级指针
语法:
数据类型** 指针名

五、指针数组
整型数组:int arr[ ]={1,2,3,4} int 是数组中元素的类型指针的类型是int* ,所以指针数组:int* arr[2] = {p1 , p2};int* arr[2] = {&a , &b};
六、函数指针
1.函数指针变量的定义
函数指针:指向函数的指针
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("test: %p\n", test);
printf("&test: %p\n", &test);
return 0;
}
所以,函数名代表的就是函数的地址,当然也可以通过&函数名的方式获得函数的地址。
函数指针的定义语法如下:

2.函数指针的使用
通过函数指针调用指针指向的函数

两种写法 p(参数) or (*p)(参数) 都可以
七、练习
1.强转什么意思?为什么要强转
#include <stdio.h>
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));
return 0;
// 输出的结果是多少?
}
(int*) 是什么意思?
(int*) 是强制类型转换:
&a的类型是int (*)[5](指向包含 5 个 int 的数组的指针)
&a + 1会跳过整个数组的长度(5 个 int,共 20 字节),得到数组末尾之后的地址这个地址的类型还是
int (*)[5],不能直接赋值给int* ptr所以用
(int*)把它强制转换成int*类型,让编译器允许赋值给ptr
如果没有强转,ptr是个数组指针,强转后相当于对地址的类型进行了改变,由数组指针类型变成了int*类型
&a + 1
&a是指向整个数组的指针,类型int (*)[5]指针加法
+1会按指向类型的大小步进:这里就是跳过5 * sizeof(int)字节所以
&a + 1指向数组a之后的第一个地址(即a[5]的位置,超出数组边界)再通过
(int*)转成int*后赋值给ptr,ptr就指向了数组末尾之后的位置
*(ptr - 1)
ptr是int*类型,ptr - 1会向前跳1 * sizeof(int)字节刚好指向数组最后一个元素
a[4]解引用
*(ptr - 1)就是a[4],值为5
所以输出结果是2,5
2.sizeof
int main()
{
int a[] = { 1,2,3,4 };
printf("%zu\n", sizeof(a));
printf("%zu\n", sizeof(a + 1));
printf("%zu\n", sizeof(a[1]));
printf("%zu\n", sizeof(&a));
printf("%zu\n", sizeof(&a + 1));
printf("%zu\n", sizeof(&a[0]));
printf("%zu\n", sizeof(&a[0] + 1));
return 0;
}
1.sizeof(a)
a 是数组名,在 sizeof() 中不会退化,代表整个数组。
数组有 4 个 int 元素,每个 int 占 4 字节。
计算:4 * 4 = 16
输出:16
2.sizeof(a + 1)
a 在表达式 a + 1 中退化为指向首元素的指针(int*)。
a + 1 仍是指针类型,指向数组第 2 个元素。
32 位下指针固定占 4 字节。
输出:4(64 位下为 8)
3.sizeof(a[1])
a[1]是数组的第 2 个元素,类型为int。int占 4 字节。- 输出:4
4.sizeof(&a)
&a 是指向整个数组的指针,类型为 int (*)[4]。
它本质仍是指针,32 位下所有指针大小都是 4 字节。
输出:4(64 位下为 8)
3.char*类型指针数组

结果:ork at
p的类型是char**,解引用后得到的是char*
(*p)+1 :这里的+1是针对char*类型的,不是针对char**!!!
所以+1只移动1个字节,所以从‘w’ 指向了‘o’ ,打印ork(直到读取到\0,打印才停止)
如果是(*p+1),运算逻辑完全不同
p +1 ,对char**做 +1,移动4个字节,也就是移动到了“at”的首地址
所以*(p+1)输出结果是at
总结:指针+1加几个字节?
技巧:去掉*,再求sizeof
如:int* ——> sizeof(int) = 4
char**——> sizeof(char*) = 4

2632

被折叠的 条评论
为什么被折叠?



