对C/C++的结构体指针强制类型转换的理解

文章探讨了C语言中结构体的内存占用规则,例如,尽管结构体A1和A2的成员类型和顺序不同,但在VisualStudio2019环境下它们都占8个字节。作者通过代码验证了结构体成员的地址和类型转换的效果,指出char和int之间的转换在某些情况下可能产生预期之外的结果,而转换为float或double类型则可能导致数据丢失或错误的输出。此外,文章提到了C++中更安全的强制类型转换方式。

printf 是指格式化输出函数,主要功能是向标准输出设备按规定格式输出信息。

当遇到不确定知识时,最好的做法就是写点代码来验证。定义几个结构体如下:

struct A1 {
	int a;
	char b;
};
struct A2 {
	char a;
	int b;
};
struct A3 {
	float a;
	long b;
};
struct A4 {
	double a;
};

我的开发环境是Visual Studio 2019社区版,查看结构体所占的内存,会发现这4种类型的结构体都占8个字节的内存,其中char占1个字节,int占4个字节,A1和A2所占内存一样大,这涉及到结构体成员的对齐。float占4个字节,long占4个字节,double占8个字节:

	printf("sizeof(A1) = %d\n", sizeof(A1));		//结果:8
	printf("sizeof(A2) = %d\n", sizeof(A2));		//结果:8
	printf("sizeof(A3) = %d\n", sizeof(A3));		//结果:8
	printf("sizeof(A4) = %d\n", sizeof(A4));		//结果:8

先验证:结构体变量名指向的地址表示结构体第一个元素的地址

	struct A1 structA1 = { 10, 'a' };
	struct A2 structA2 = { 'a', 10 };
	printf("structA1.a: %d\n", structA1.a);			//结果:10
	printf("structA1.b: %c\n", structA1.b);			//结果:'a'
	printf("structA1.a: %c\n", structA2.a);			//结果:'a'
	printf("structA1.b: %d\n", structA2.b);			//结果:10

	printf("structA1: %d\n", structA1);				//结果:10
	printf("structA1.a: %d\n", structA1.a);			//结果:10
	printf("&structA1: %p\n", &structA1);			//结果:00B3F884,且每次运行结果不同
	printf("&(structA1.a): %p\n", &(structA1.a));	//结果:00B3F884

将结构体A2的实例structA2强制转换成类型为A1的实例structA1Z1的结果:

	//强制转换
	struct A1 structA1Z1;
	structA1Z1 = *((struct A1*)&structA2);
	printf("structA1Z1.a: %d\n", structA1Z1.a);			//结果:-8589993567
	printf("structA1Z1.a: %c\n", structA1Z1.a);			//结果:'a'
	printf("structA1Z1.b: %c\n", structA1Z1.b);			//结果:空
	printf("structA1Z1.b: %d\n", structA1Z1.b);			//结果:10

前面已经知道类型为A1和A2的结构体,都占8字节内存,以字符格式%c输出structA1Z1.a到控制台,结果是字符'a',以整形格式%d输出structA1Z1.b,结果是10,这跟结构体实例structA2是一致的。如果以A1来输出实例structA1Z1的两个成员变量,结果显示的不正确。

其实structA1Z1能打印出跟structA2一样的结果,说明char类型和int类型能相互转换。如果将structA2强制转换成A3或A4,结果就不正确了,如下所示:

	struct A3 structA3Z1;
	structA3Z1 = *((struct A3*)&structA2);
	printf("structA3Z1.a: %f\n", structA3Z1.a);			//结果:-107373320.000000
	printf("structA3Z1.a: %c\n", structA3Z1.a);			//结果:空
	printf("structA3Z1.b: %l\n", structA3Z1.b);			//结果:空
	printf("structA3Z1.b: %d\n", structA3Z1.b);			//结果:10

	struct A4 structA4Z1;
	structA4Z1 = *((struct A4*)&structA2);
	printf("structA4Z1.a: %f\n", structA4Z1.a);			//结果:0.000000
	printf("structA4Z1.a: %c\n", structA4Z1.a);			//结果:a
	printf("structA4Z1.a: %d\n", structA4Z1.a);			//结果:-858993567

上面的代码说明char强制转换成float会导致打印不出字符'a',将structA4Z1转成A4型的structA4Z1,只能输出'a'。其实structA3Z1和structA4Z1都是有值的,把鼠标放到这两个变量上,结果如下图所示:

先解释下语法上为什么能对结构体实例进行强制类型转换。&structA2表示结构体第一个元素的地址,占4个字节。C语言中,表示地址的是指针变量,一个结构体指针指向结构体的首地址,通过强制类型转换(struct A1*)(&structA2),这个指针现在指向8字节大小的内存。由于(struct A1*)(&structA2)是地址,不能直接赋给structA1Z1这个实例,通过解引用符*把内存中的值取出来就可以赋值给structA1Z1了。

变量的类型(名)表示一个变量占多大内存。

一个变量的变量名表示地址,通过取地址符&可以查看该变量的内存地址(这块内存的首地址)。

取到的一个变量的地址可以赋给一个指针变量,因为指针代表的就是地址。*是解引用,能把一段地址的值取出来,*((struct A1*)(&structA2))就把这块内存的值取出来,赋给实例structA1Z1就没问题了,实例structA2和实例structA1Z1都占8个字节的内存,且char和int可以转换。

当然,本博文只是简单验证了下结构体类型的强制转换。更多类型转换问题,可参考C++强制类型转换,这篇文章提到,C语言中的强制转换主要用于普通数据类型、指针的强制转换,没有类型检查,转换不安全,实验发现有些类型转换后,输出不了正确结果了;C++除了能使用c语言的强制类型转换外,还新增了四种强制类型转换:static_cast、dynamic_cast、const_cast、reinterpret_cast,主要运用于继承关系类间的强制转化,其实结构体也可以被认为是一种特殊的类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值