C语言:指针的进阶

一、字符指针

(一)字符指针

字符指针指向的是字符串的首元素地址。

(二)常量字符串和字符数组

常量字符串不可以被修改并且在内存中(只读数据区)只会存在一份,而字符数组可以被修改。
在这里插入图片描述

二、指针数组和数组指针

(一)指针数组 int *p1[10]

指针数组是存放指针的数组。
代码中,arr数组就是指针数组。
使用指针数组,传参打印时使用二级指针接收。
在这里插入图片描述

(二)数组指针 int (*p2)[10]

数组指针是存放数组地址的指针 &arr
二位数组的数组名是第一行元素的地址
传参打印时使用数组指针接收。
在这里插入图片描述

三、函数指针

(一)函数指针

1、概念

指向函数的指针,记得要给指针加上()否则指针变量会和后面的函数调用操作符相结合,编译错误。
在这里插入图片描述

2、两段有趣的代码

(1)( * ( void(*)()0) )();

本质上是调用了0地址处的那个函数。
在这里插入图片描述

(2)void (signal(int, void ()(int)))(int);

在这里插入图片描述

(二)函数指针数组

存放函数指针的数组叫函数叫做函数指针数组。

void test(const char* str)
{
 printf("%s\n", str);
}
int main()
{
 //函数指针pfun
 void (*pfun)(const char*) = test;
 //函数指针的数组pfunArr
 void (*pfunArr[5])(const char* str);
 pfunArr[0] = test;
 //指向函数指针数组pfunArr的指针ppfunArr
 void (*(*ppfunArr)[5])(const char*) = &pfunArr;
 return 0;
}

转移表

利用函数指针数组,我们可以将switch语句改成转移表,这样的代码往往是比较简洁的。
在这里插入图片描述

(三)指向函数指针数组的指针

四、回调函数

(一)定义

当把一个函数的地址作为参数传递给另外一个函数,另一个函数用函数指针类型来接收,被调用的函数就是回调函数。
利用回调函数,我们往往可以简化代码,达到泛型编程。
在这里插入图片描述

(二)qsort函数

void qsort (void* base, size_t num, size_t size,
 int (*compar)(const void*,const void*));

这里我们使用了回调函数,只需要按需求写一个比较方式,qsort将会可以给任何类型的数据进行排序。

struct stu
{
	char name[20];
	int age;
};
int cmp_stu_by_name(const void* p1, const void* p2)
{
	return strcmp(((struct stu*)p1)->name,
	 ((struct stu*)p2)->name);
	 //记得要强制类型转化,指针才会按照字节数进行访问对应地址
}

(三)qsort自定义实现

在这里插入图片描述

五、sizeof 和 strlen 的剖析

(一)概念

sizeof :是操作符,统计的是字节数量,包括字符串末尾的’\0’。
strlen:字符串函数,遇到’\0’结束,统计字符串长度,如果没有’\0’,结果是随机值。
在这里插入图片描述

在这里插入图片描述

(二)sizeof例题

8/4 表示在32位平台是4个字节,64位平台是8个字节。
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%zd\n", sizeof(a));//        16
	printf("%zd\n", sizeof(a + 0));//    8/4
	printf("%zd\n", sizeof(*a));//       4
	printf("%zd\n", sizeof(a + 1));//    8/4
	printf("%zd\n", sizeof(a[1]));//     4
	printf("%zd\n", sizeof(&a));//       8/4
	printf("%zd\n", sizeof(*&a));//      16
	printf("%zd\n", sizeof(&a + 1));//   8/4
	printf("%zd\n", sizeof(&a[0]));//    8/4
	printf("%zd\n", sizeof(&a[0] + 1));//8/4


	char* p = "abcdef";
	printf("%zd\n", sizeof(p));//p是指针变量。计算的是指针变量p的大小,4/8个字节
	printf("%zd\n", sizeof(&p));//&p是指针变量p的地址,4/8个字节	
	//&p -- char** -- 二级指针
	return 0;
}

(三)strlen例题

int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%zd\n", strlen(arr));//随机值
	printf("%zd\n", strlen(arr + 0));//随机值
	//printf("%zd\n", strlen(*arr));//arr是数组名表示首元素的地址
	//*arr 是首元素 -- 'a' - 97 ,传递给strlen后,strlen 会认为97就是地址,然后去访问内存,err
	//printf("%zd\n", strlen(arr[1]));//'b' -98 //err
	printf("%zd\n", strlen((char*) & arr));//随机值
	printf("%zd\n", strlen((char*) & arr + 1));//随机值
	printf("%zd\n", strlen(&arr[0] + 1));//随机值
	return 0;
}

六、指针杂论

(一)指针大小

指针的大小只和环境有关系。在32位平台下是4个字节,64位平台下是8个字节。

(二)指针类型的作用

不同的指针类型在指针的加减,*(解引用),和访问权限上面有差异

1、指针的加减

我们发现指针相加减的规则是
(类型)*p = a;
p + n = p + n * (sizeof(类型))
在这里插入图片描述

在这里插入图片描述

2、解引用和访问权限

这里要将int类型的地址转换成char*操作,需要进行强制类型转换。
在这里插入图片描述

下面这串代码看似正确,实际上,char*指针的访问权限只有一个字节,所以如果数组中值变大,就会发生错误。
在这里插入图片描述

3、强大的void指针

void指针相当于万能指针,可以将任何类型的变量赋值给它,但是之后指针变量不可以再重新赋值或者直接相加减。
在这里插入图片描述

(三)const修饰指针

const放在后面,指针指向区域不能改动,而放在前面,指针的内容不可修改。
在这里插入图片描述

(四)数组名的理解

一维数组:数组名其实是数组的首元素地址。
二维数组:数组名是第一排元素的地址。
但是有两种情况除外。
1、sizeof(数组名) 代表的是整个数组的大小。
2、&(数组名)代表整个数组的地址。
在这里插入图片描述

八、结束语

相信通过这篇文章,能对指针有更深刻的理解,对指针的应用也有提升。这是小编所希望看到的。大家如果也觉得有帮助,那就动动小手支持下吧!
在这里插入图片描述

评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值