目錄
數組類型強制類型轉換爲結構體
先看一個例子:
#include <stdio.h>
int main(void) {
unsigned char arr[] = "0123456789abcdefghijk";
struct A {
int a;
char b;
char c;
char d;
int e;
} p, *pp;
struct B {
int a;
char b;
int c;
};
p.a = 1;
p.b = '2';
p.c = '3';
p.d = '4';
p.e = 5;
pp = &p;
printf("pp->a: %d\npp->b: %c\npp->c: %c\npp->d: %c\npp->e: %d\n", pp->a, pp->b, pp->c, pp->d, pp->e);
printf("**********\n");
pp = (struct A *)arr;
printf("pp->a: %d\npp->b: %c\npp->c: %c\npp->d: %c\npp->e: %d\n", pp->a, pp->b, pp->c, pp->d, pp->e);
return 0;
}
運行結果:
pp->a: 1
pp->b: 2
pp->c: 3
pp->d: 4
pp->e: 5
**********
pp->a: 858927408
pp->b: 4
pp->c: 5
pp->d: 6
pp->e: 1650538808
上述是一個將數組類型變量強制類型轉換爲 struct A 的例子,結合結構體內存分佈的內容我們可以看出:結構體數據類型轉換的本質就是對結構體內存空間的填充。通過這種方式,可以把某一起始地址的數據類型與結構體成員相對應。
結構體之間的強制類型轉換
要理解結構體之間的強制類型轉換,需要明白以下幾點原理:
- 結構體變量是如何分佈內存的。
- 結構體變量的內存首地址。
- 結構體成員在結構體內存中的偏移地址。
實際上在上述的內容中,我們已經提到了這 3 點內容。
先看一個例子:
#include <stdio.h>
struct A {
int x;
char y;
} a, *pa;
struct B {
char x;
int y;
} b, *pb;
int main(void) {
a.x = 1;
a.y = 'A';
pa = &a;
printf("pa->x: %d, pa->y: %c\n", pa->x, pa->y);
b.x = 'A';
b.y = 1;
pb = &b;
printf("pb->x: %c, pb->y: %d\n", pb->x, pb->y);
struct B z;
z.x = ((struct B *)pa)->x;
printf("z.x: %c, z.y: %d\n", z.x, z.y);
}
輸出結果:
pa->x: 1, pa->y: A
pb->x: A, pb->y: 1
z.x: , z.y: 32766
上述例子爲結構體之間的強制類型轉換,根據結構體內存分佈的內容,並且我們暫不考慮內存對齊的話,我們知道:
- a 的內存分佈爲:前 4B,後 1B
- b 的內存分配爲:前 1B,後 4B
當我們執行強制類型轉換時,本質是就是 C 語言會對結構體變量 a 的空間,按照 struct B 的佈局進行解釋:也就是說,將 a 的第一個字節看成 struct B 的第一個成員,且按 ASCII 碼處理數據,而將後面的 4B 看成 struct B 的第二個成員,並按補碼格式解釋數據。
需要注意的是,C 語言中的結構體強制類型轉換本質是對指針進行轉換,所以轉換的對象必須爲一個指針類型:
struct str1 a;
struct str2 b;
a = (struct str1)b; // this is wrong
a = ((struct str1)&b); // this is correct
通過數組強制類型轉換爲結構體以及結構體之間互相轉換的內容,我們可以總結到:C 語言中結構體變量之間直接的賦值和轉換本質是將右值的內存數據直接覆蓋到左值所佔用內存空間中,然後再根據 C 語言對這塊內存的理解(類型定義)表達出來。
struct in_addr {
unsigned long a_addr;
}
struct sockaddr_in {
unsigned short sin_family; // 地址類型(2B)
unsigned short int sin_port; // 端口號(2B)
struct in_addr sin_addr; // IP 地址(4B)
unsigned char sin_zero[8]; // 填充空間(8B)
}
struct sockaddr {
unsigned short sa_family; // 地址類型(2B)
char sa_data[14]; // 協議地址(14B)
}
在實際的網絡編程中,通常會先初始化 sockaddr_in,再將它強制轉化成 sockaddr 來使用。這兩個結構體,長度都爲 16 字節,sockaddr_in.sin_family 的數據存入 sockaddr.sa_family,剩下的 14 個字節存入 sockaddr.sa_data,這樣在各種操作中可以方便的處理端口號和 IP 地址。