地表最强C语言汇总(十四)阅读程序(持续更新)

最新更新时间:2021.12.14
环境:vs2019,x86编译;64位win10

文章目录


地表最强C语言系列传送门:
地表最强C语言汇总(一)基本数据类型(持续更新)
地表最强C语言汇总(二)变量和常量(持续更新)
地表最强C语言汇总(三)字符串+转义字符+注释(持续更新)
地表最强C语言汇总(四)分支语句(持续更新)
地表最强C语言汇总(五)循环语句(持续更新)
地表最强C语言汇总(六)函数(持续更新)
地表最强C语言汇总(七)数组(持续更新)
地表最强C语言汇总(八)操作符(持续更新)
地表最强C语言汇总(九)关键字(持续更新)
地表最强C语言汇总(十)#define定义常量和宏(持续更新)
地表最强C语言汇总(十一)指针(持续更新)
地表最强C语言汇总(十二)结构体(持续更新)
地表最强C语言汇总(十三)一些自定义函数(持续更新)
地表最强C语言汇总(十四)阅读程序(持续更新)

十四、阅读程序

int i;//不初始化,默认为0
int main()
{
    
    
	i--;
	if (i > sizeof(i))//sizeof的计算结果为unsigned int,int与其运算时需要转换为unsigned int
		printf(">\n");
	else
		printf("<\n");
int a = 0;
int n = 0;
scanf("%d %d", &a, &n);
printf("%d", Sn(a, n));
	1.
	(*(void(*)())0)();//调用0地址处的函数,该函数无参,返回类型是void
	//void(*)()			 - 函数指针类型
	//(void(*)())0		 - 将0强制类型转换为函数地址
	//*(void(*)())0		 - 对0地址进行解引用操作
	//(*(void(*)())0)()  - 调用0地址处的函数
	2.
	void (*signal(int, void(*)(int)))(int);
	//signal先和()结合,说明signal是函数名
	//signal函数的第一个参数的类型是int型,第二个参数的类型是函数指针,该指针指向一个参数为int,返回类型为void的函数
	//signal函数的返回类型是一个函数指针,该指针指向一个参数为int,返回类型为void的函数
	//signal是一个函数的声明
	//分析时:去掉函数名和参数即为返回类型		数组去掉名字和[]剩下的即为类型
	//void(*)(int) signal(int, void(*)(int));	//这种写法不允许,若函数返回值为函数指针,则*必须和函数名写在一起。
	//typedef 对类型进行重定义
	typedef void (*pfun_t)(int);//对函数指针类型重命名为pfun_t,注意pfun_t必须和*写在一起而不能typedef void (*)(int) pfun_t
	pfun_t signal(int, pfun_t);//清晰地看出这是一个函数的声明

	int a[5] = {
    
     1,2,3,4,5 };
	int* ptr = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(ptr - 1));	//	2	5
	//*(a + 1),a为首元素地址,首元素为int型,所以a其实是一个int型的指针,因此+1即向后移动了一个int型的字节,指向了2。
	//而&a则取出了整个数组的地址,虽然在数值上和首元素的地址一样,但是其类型为int(*)[5],因此+1后向后移动了一个数组的长度,即5的后边;
	//这时解引用本能访问一个数组,但是他被强制转换成了int型的指针,赋值给了ptr,所以此时ptr-1只能向前移动一个int型的宽度,指向了5.
//结构体Test的大小是20个字节
struct Test
{
    
    
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;//p为结构体指针,指向Test
//假设p的值为0x100000

	//指针类型决定了指针的运算
	printf("%p\n", p + 0x1);					//跳过一个结构体大小:0x100000 + 0x000014 = 0x100014
	printf("%p\n", (unsigned long)p + 0x1);		//就是整型 + 整型:0x100000 + 0x1 = 0x100001
	printf("%p\n", (unsigned int*)p + 0x1);		//跳过一个整型大小:0x100000 + 0x000004 = 0x100004
	//加16进制数也只是普通加法而已,并不是加地址,地址+地址是没有意义的,因此指针+1后的偏移量还是取决于指针的类型
	int a[4] = {
    
     1,2,3,4 };
	int* ptr1 = (int*)(&a + 1);	
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);// 4,200000

ptr1[-1]等价于*(ptr1 - 1),与上边的4类型一样,不做重点解释,重点解释ptr2:
(int)a将数组a的首地址强制转换为int类型,很多人会有疑问,地址怎么转换成整数呢?其实很简单,我们假设数组a[]的首元素地址为0x00ffaa34,我们都知道,地址是用十六进制表示的,若不考虑前边的0x,剩下的就是一个十六进制数,因此将其强制类型转换为int时,就是将这个地址变成了00ffaa34,这是00ffaa34是一个整型的数字,将其+1变成00ffaa35,然后再将其强制类型转换为int*,其实就变成了0x00ffaa35而已;而内存中一个字节占一个存储单元,因此此时ptr2指向了int类型1的第二个字处,又由于已经被转换为int*型,因此又可以访问4个字节的大小,刚好可以访问到int型2的第一个字节(小端存储,不了解什么是小端存储的朋友可以看这篇文章的1.2节,有详细的介绍地表最强C语言汇总(一)基本数据类型(持续更新)
没看懂没关系,配合下边的图在看文字即可秒懂:

在这里插入图片描述

	int a[3][2] = {
    
     (0,1),(2,3),(4,5) };//逗号表达式	1	3	5
	int* p;
	p = a[0];
	printf("%d",p[0]);	// 1
	int a[5][5];// 0 0 0 0 0	0 0 0 0 0	0 0 0 0 0	0 0 0 0 0	0 0 0 0 0
	int(*p)[4];	// 0 0 0 0		0 0 0 0		0 0 0 0		0 0 0 0		0 0 0 0
	p = a;		// a:int(*)[5]
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);//	FFFFFFFC,-4

在这里插入图片描述
指针- 指针得到的是两个指针之间的元素个数,由上图可以看出,p[4][2]和a[4][2]的地址之间相差4个元素,而且数组在内存中是按照低地址到高地址分配空间的,因此结果为-4,在内存中的存储方式为补码,其补码为:
原码:10000000 00000000 00000000 00000100
反码:11111111 11111111 11111111 11111011
补码:11111111 11111111 11111111 11111100
存好之后,分别以%d的形式打印出来和%p的形式打印出来,而这两种形式,同一个数据,取出来是不同的结果。
对于%d的形式取出,要以整型的格式打印数据,将其转换为原码后就是-4;
对于%p的形式取出,要以地址的格式打印数据,而且地址并没有正负之分,因此直接将上述二进制序列以16进制打印:FF FF FF FC

	int aa[2][5] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	int* ptr1 = (int*)(&aa + 1);
	int* ptr2 = (int*)(*(aa + 1));//*(aa+1) <=>aa[1],即第二行首元素的地址,即6的地址;
	//或者这么理解,aa+1是一个数组指针,其类型为int(*)[5],对其解引用得到了一个数组aa[1],这个数组又是其首元素的地址类型为:int*
	//可以看出,其实ptr2不需要强制类型转换
	printf("%d,%d\n", *(ptr1 - 1), *(ptr2 - 1));//	10,5

注意:对(aa + 1)解引用得到的是一个数组而非数字6,因为aa + 1表示的是一个数组的地址。而这个数组,表示的是其说元素的地址即6的地址。

	char* a[] = {
    
     "study","at","fuda" };//a是一个指针数组,每个元素都是char*,为字符串首元素的地址
	char** pa = a;//pa指向a首元素,a首元素类型为char*
	pa++;
	printf("%s\n", *pa);//at
	char* c[] = {
    
     "ENTER","NEW","POINT","FIRST" };
	char** cp[] = {
    
     c + 3,c + 2,c + 1,c };
	char*** cpp = cp;
	printf("%s\n", **++cpp);		//POINT
	printf("%s\n", *--* ++cpp + 3);	//ER	注意--是操作在谁的身上
	printf("%s\n", *cpp[-2] + 3);	//ST
	printf("%s\n", cpp[-1][-1] + 1);//EW

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43143724/article/details/121899035
今日推荐