C语言:指针高阶

一、数组和指针

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

&arrint (*)[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;
}
输出结果如下: test: 005913CA
                        &test: 005913CA

所以,函数名代表的就是函数的地址当然也可以通过&函数名的方式获得函数的地址。

函数指针的定义语法如下:

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* 后赋值给 ptrptr 就指向了数组末尾之后的位置

*(ptr - 1)

ptrint* 类型,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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值