C语言结构体指针强转

1.结构体指针强转

C语言中结构体无法进行强转,但是结构体指针是可以进行间接强转的

  • eg:
先定义4个结构体
typedef struct {
    int array[4];
}Struct_A;

typedef struct {
    int *ptr_b;
} Struct_B;

typedef struct {
    int int1;
    int int_array1[2];
    float float_array2[2];
}Struct_C;

typedef struct{
    char char1;
    char char_array[2];
}Struct_D;


不允许操作:
Struct_B b;
Struct_A a = (Struct_A)b;

允许操作:
Struct_B *b;
Struct_A *a = (Struct_A *)b;
  • eg:强转测试
#include <stdio.h>
#include <malloc.h>
#include <string.h>
//#include <bits/wordsize.h>

typedef struct {
	int array[4];
}Struct_A;

typedef struct {
	int *ptr_b;
} Struct_B;

typedef struct {
	int int1;
	int int_array1[2];
	float float_array2[2];
}Struct_C;

typedef struct{
	char char1;
	char char_array[2];
}Struct_D;

void printf_a(Struct_A *a){
	printf("Struct_A:\n");
	for (int i = 0; i<4; i++){
		printf("%p: a->array[%d] = %d\n", &((a->array)[i]), i, (a->array)[i]);
	}
}

void printf_b(Struct_B *b){
	printf("Struct_B:\n");
	printf("&(b->ptr_b): %p\n", &(b->ptr_b));
	printf("b->ptr_b: %p\n", b->ptr_b);
}

void printf_c(Struct_C *c){
	printf("Struct_C:\n");
	printf("%p: c->int1: %d\n", &(c->int1), c->int1);
	printf("%p: c->int_array1[0]: %d\n", &(c->int_array1[0]), c->int_array1[0]);
	printf("%p: c->int_array1[1]: %d\n", &(c->int_array1[1]), c->int_array1[1]);
	printf("%p: c->float_array2[0]: %f\n", &(c->float_array2[0]), c->float_array2[0]);
	printf("%p: c->float_array2[1]: %f\n", &(c->float_array2[1]), c->float_array2[1]);
}

void printf_d(Struct_D *d){
	printf("Struct_D:\n");
	printf("%p: d->char1: %s\n", &(d->char1), d->char1);
	printf("%p: d->char_array[0]: %s\n", &(d->char_array[0]), d->char_array[0]);
	printf("%p: d->char_array[1]: %s\n", &(d->char_array[1]), d->char_array[1]);
}

void print_all_struct(Struct_A *a, Struct_B *b, Struct_C *c, Struct_D *d){
	printf("Show points\n");
	printf(" -----------------------------------------------------------------------\n");
	printf("|Pointer of a     |Pointer of b     |Pointer of c     |Pointer of d     |\n");
	printf(" -----------------------------------------------------------------------\n");
	printf("|%p   |%p   |%p   |%p   |\n", a, b, c, d);
	printf(" -----------------------------------------------------------------------\n");
}

void main(){
	Struct_A *a = (Struct_A *)malloc(sizeof(Struct_A));
	if (a == NULL){ return; };
	int temp_a[4] = { 0, 1, 2, 3 };
	memcpy(a->array, &temp_a, 4 * sizeof(int));

	Struct_B *b = (Struct_B *)a;
	Struct_C *c = (Struct_C *)a;
	Struct_D *d = (Struct_D *)a;

	//printf("The system bits is %d\n", __WORDSIZE);
	printf("The Size of int: %d Byte\nThe Size of int poniter: %d Byte\n", sizeof(int), sizeof(int*));
	printf("The Size of float:%d\n", sizeof(float));
	printf("The Size of char: %d Byte\nThe Size of char pointer: %d Byte\n", sizeof(char), sizeof(char*));
	print_all_struct(a, b, c, d);

	printf_a(a);
	printf_b(b);
	printf_c(c);
	printf_d(d);
	return;
}
  • 结果:
    在这里插入图片描述
    说明:
The system bits is 32
The Size of int: 4 Byte     #32 位系统int占4个字节
The Size of int poniter: 4 Byte     #32 位系统指针占4字节
The Size of float: 4 Byte   #32 位系统float占4字节
The Size of char: 1 Byte    #32 位系统char占1个字节
The Size of char pointer: 4 Byte     #32 位系统指针占4字节

Show points #四个结构体的指针相等,说明指针赋值成功
 -----------------------------------------------------------------------
|Pointer of a     |Pointer of b     |Pointer of c     |Pointer of d     |
 -----------------------------------------------------------------------
|001BAD90   |001BAD90   |001BAD90   |001BAD90   |
 -----------------------------------------------------------------------
Struct_A: #包含一个数组array[4]
001BAD90: a->array[0] = 0 #结构体中第1个元素(array)起始地址与结构体指针(a)相等
001BAD94: a->array[1] = 1 #结构体指针(a)+int长度(4字节) 得array中第2个元素地址
001BAD98: a->array[2] = 2
001BAD9C: a->array[3] = 3
Struct_B:#包含一个int指针ptr_b
&(b->ptr_b): 001BAD90 #结构体中第1个元素int指针ptr_b的 地址 等于结构体指针(b)
b->ptr_b: 00000000 #没有为指针ptr_b分配内存,故地址为0x00000000
Struct_C:
001BAD90: c->int1: 0#第1个元素地址等于指针c,该地址有值,元素类型与内存的值中一致,"赋值"成功
001BAD94: c->int_array1[0]: 1#指针c+int长度(4字节)得到元素 c->int_array1[0]的地址,该地址有值,值类型与内存的值中一致,"赋值"成功
001BAD98: c->int_array1[1]: 2
001BAD9C: c->float_array2[0]: 0.000000 #地址有值,但类型不是float,”赋值“失败
001BADA0: c->float_array2[1]: -42201683186052844000000000000000000000.000000#这个地址没有值
Struct_D:
001BAD90: d->char1: (null) #第1个元素地址等于指针d, 但值的类型与内存中不同,”赋值“失败
001BAD91: d->char_array[0]: (null) #值的类型不同,导致长度不同,得到的地址中无值
001BAD92: d->char_array[1]: (null)#同上

在这里插入图片描述
结论:

  • 结构体指针等于第一个元素的起始地址
    如果第一个元素是指针p,请注意是指针变量的地址&p,而不是指针本身p
  • 结构体内的元素在内存中顺序排列,赋值时也是顺序”赋值“。满足两个条件即可”赋值“成功:
    元素起始地址上有值;
    元素类型与内存中的值类型一致;
  • 严格来说其实并不是赋值,而是指定了结构体的起始地址,根据元素的长度可以顺序得到各个元素的地址,如果元素地址上有值,且元素类型与内存中的值一致,则可以直接使用,否则无法使用,自动转为默认值。
  • 内存中并没有存储数据类型,为什么笔者一直在强调元素类型与内存中的值类型是否一致呢?
    原因是不同类型的值在内存中的编码存储方式不一样,一个按int编码值是无法按float方式直接读取的。

2.结构体指针强转在链表中的使用

每一个链表的节点都是一种结构体,通过将节点强制转换为某个结构体,就可以方便的使用该结构体的其他成员了。
结构体之间的强制转换需要基于以下的原理:

  • 结构体声明内存如何分布,

  • 结构体指针声明结构体的首地址,

  • 结构体成员声明该成员在结构体中的偏移地址

  • eg:

typedef struct _General_Node
{
//无论任何结点一定包含虚线内的部分
//--------------------------------
DoubleNode node; //结点
S32 Nodetype; //结点类型 不同类型的结点对应不同的处理方法
S32 subType; //结点子类型(多种灵活用途)

U32 NodeFlag; //标志

PDoubleNode pNode; //本结点指向的上\下层段中的结点
PDoubleList pList; //本结点指向的上\下层段指针

S32 Pid; //参数ID
S32 Caption; //标题ID

// PTOUCHAREA Touch; //结点的触摸区指针

WNDPROC DrawFunc; //绘制程序
WNDPROC ProcFunc; //焦点处理程序
//--------------------------------

}GENERALNODE, *PGENERALNODE;

这是一个大的结构体其中的DoubleNode node是一个链表中的节点定义为:
typedef struct _DoubleNode {
U32 ID; //结点的ID 每一个结点有它唯一的ID
struct _DoubleNode *next;
struct _DoubleNode *previous;
} DoubleNode, *PDoubleNode;

说明:

  • 这两个结构体他们的首地址是一样的,且struct_DoubleNode为struct _General_Node子关系;
  • 我们知道每一种类型在内存所占的空间是不一样的,比如int型在内存的读取方式为从首地址开始读取32位的数据。
    而类型转换可以理解为首地址不变,我们把其读取方式改变。
  • 上面的两个结构体,他们的首地址是一样的,其第一个成员偏移也是一样,所以可以进行强制类型转换

结构体指针之间的强制转换

  • 很重要的一点就是字节对齐方式
  • 下面两个结构体式可以进行强制转换的,因为他们的对齐方式是一样的
  • 强制类型转换就是在内存地址上的赋值,如果其强制转换,破坏了结构体的原有结构,则不行
struct A{
char a;
int b;
};

struct B{
int a;
char b;
};

3.结构体指针强转在链表中使用出现的问题

代码位置:链接

  • 15
    点赞
  • 83
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢打篮球的普通人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值