背景: 最近在重學(xué)C語言,目的是為了能看懂操作系統(tǒng)的底層代碼,也為后續(xù)使用C語言開發(fā)一個(gè)類似redis數(shù)據(jù)庫的中間件做準(zhǔn)備,于是又重新踏上了學(xué)習(xí)C語言的道路,早在上學(xué)期間就學(xué)習(xí)過C語言,但是很久都不用了,語法和技巧都忘記了。計(jì)算機(jī)的知識(shí)真是浩如煙海,用進(jìn)廢退。
本次學(xué)習(xí)struct,C語言沒有對(duì)象object也沒有類class,只有struct提供了對(duì)象object和類class的相似功能。 struct用于 封裝一個(gè)復(fù)雜物體的各個(gè)變量,例如: 學(xué)生,有姓名、學(xué)號(hào)、班級(jí)、年齡、性別等等參數(shù),他們非常相關(guān); 另外一個(gè)用途是 某些函數(shù)需要傳入多個(gè)參數(shù),我們把這些多個(gè)參數(shù)封裝到一個(gè)結(jié)構(gòu)體中,再進(jìn)行傳入這個(gè)結(jié)構(gòu)體或結(jié)構(gòu)體的指針,這樣方便快捷。 結(jié)構(gòu)體所表示的一條數(shù)據(jù),類似數(shù)據(jù)庫學(xué)生表的一行記錄。
1. 定義一個(gè)結(jié)構(gòu)體:
struct student {
char* name;
int num;
int grade;
int age;
char sex;
};
// 聲明變量的同時(shí),對(duì)變量進(jìn)行賦值
struct student {
char* name;
int num;
int grade;
int age;
char sex;
} s1 = {"xiaolong",1000,1,6,'m'},s2 = {"xiaopeng",1010,1,6,'m'};
2. 定義結(jié)構(gòu)體變量 并對(duì)結(jié)構(gòu)體進(jìn)行設(shè)置:
// 聲明自定義類型的變量時(shí),類型名前面,不要忘記加上struct關(guān)鍵字
struct student xiaoming;
xiaoming.name = "xiaoming";
xiaoming.num = 1001;
xiaoming.grade = 1;
xiaoming.age = 6;
xiaoming.sex = 'm';
// 這種方式需要注意 大括號(hào)里面 值的順序,必須與struct聲明時(shí)屬性的順序保持一致
struct student xiaohong = {"xiaohong",1002,1,6,'f'};
// 否則需要為每個(gè)值指定屬性名
struct student lihua = { .num=1003, .name="lihua", .sex='m', .age=7, .grade=2};
// 可以修改
lihua.age = 8;
// 也可以使用typedef命令
typedef struct student {
char* name;
int num;
int grade;
int age;
char sex;
} stu;
// stu就是 struct student的別名
stu xiaohua = {"xiaohua",1020,1,6,'m'};
還有就是 多個(gè)結(jié)構(gòu)體嵌套,嵌套的初始化和設(shè)置與上面一致,這里不再贅述。
3. 結(jié)構(gòu)體的復(fù)制
賦值運(yùn)算符( = )可將 struct 結(jié)構(gòu)每個(gè)屬性的值,一模一樣的復(fù)制一份,拷貝給另外一個(gè)struct變量,與數(shù)組不同(使用賦值運(yùn)算符復(fù)制數(shù)組,不會(huì)復(fù)制數(shù)據(jù),只會(huì)共享地址)
如果 結(jié)構(gòu)體里面 有指針類型的變量,那么復(fù)制的是 指針的值,也就是說 兩個(gè)結(jié)構(gòu)體 指向的是同一個(gè)內(nèi)容,修改一個(gè)會(huì)影響另外一個(gè)的值。
如果 結(jié)構(gòu)體里面 沒有指針類型的變量,那么 復(fù)制的是 數(shù)據(jù)的值,是兩份毫無關(guān)系的數(shù)據(jù),各自修改不影響另外一個(gè)的值。
char* name ; 這種字符指針?biāo)赶虻淖址遣荒苄薷牡模荒苤匦轮赶蛞粋€(gè)新的字符串。
struct student { char name[30]; short age; } a, b;
strcpy(a.name, "xiaohu");
a.age = 18;
b = a;
b.name[0] = ' ';
b.name[1] = 'l';
printf("%s\n", a.name); // xiaohu
printf("%s\n", b.name); // laohu
struct student2 { char name[30]; short age; } a2, b2;
a2.name = "xiaohu"
a2.age = 18;
b2 = a2;
b2.name = " laohu" // 內(nèi)存新建了" laohu"字符串,b2的指針這次指向" laohu"
// 也就是說 a2.name 和 b2.name 不再指向同一塊地址空間
printf("%s\n", a2.name); // xiaohu
printf("%s\n", b2.name); // laohu 這是為什么
struct student3 { char* name; short age; } a3, b3;
a3.name = "xiaohu";
a3.age = 18;
b3 = a3;
// 這是為了 修改字符串所做的測(cè)試, 結(jié)論是 無法修改,雖然能通過編譯,但是運(yùn)行失敗,顯示段錯(cuò)誤。
*(b3.name+0) = ' ';
*(b3.name+1) = 'l';
printf("%s\n", a3.name);
printf("%s\n", b3.name);
4. 結(jié)構(gòu)體數(shù)組 和 結(jié)構(gòu)體指針
如果將 struct 變量作為參數(shù) 傳入函數(shù),則進(jìn)行了 結(jié)構(gòu)體的復(fù)制,一是會(huì)占用內(nèi)存,二是函數(shù)內(nèi)修改只是修改副本,而不會(huì)影響函數(shù)外部的原始數(shù)據(jù),所以 一般情況下,我們 使用 struct 指針?作為參數(shù) 傳入函數(shù)中。?
void happy(struct student* s){
(*s).age = (*s).age + 1; // . 優(yōu)先級(jí)比 * 高,所以需要使用()
}
happy(&xiaoming);
void happy2(struct student* s){
s->age = s->age + 1 ; // 或 s->age++; 自增
}
void happy2(struct student* s){
s->age++; // 自增 , -> 優(yōu)先級(jí)比 ++ 高
}
對(duì)于 struct 的變量 使用 . 獲取屬性,對(duì)于 struct的變量指針 使用 -> 獲取屬性。
xiaoming.age? ==? (*ptr).age == ptr->age
5. 通過 malloc來構(gòu)造鏈表
struct node {
int data;
struct node* next;
};
struct node* head;
// 生成一個(gè)三個(gè)節(jié)點(diǎn)的列表 (11)->(22)->(33)
head = malloc(sizeof(struct node));
head->data = 11;
head->next = malloc(sizeof(struct node));
head->next->data = 22;
head->next->next = malloc(sizeof(struct node));
head->next->next->data = 33;
head->next->next->next = NULL;
// 遍歷這個(gè)列表
for (struct node *cur = head; cur != NULL; cur = cur->next) {
printf("%d\n", cur->data);
}
6. 空間對(duì)齊
struct所占用的空間不是各個(gè)屬性存儲(chǔ)空間的總和,而是最大內(nèi)存占用屬性的存儲(chǔ)空間倍數(shù),其他屬性會(huì)添加空位閾值對(duì)齊。
#include<stdio.h>
int main()
{
struct student {
char* name; // 8 字節(jié)
int num; // 4 字節(jié)
int grade; // 4 字節(jié)
int age; // 4 字節(jié)
char sex; // 1 字節(jié)
};
printf("student %d\n",sizeof(struct student));
struct student_other1 {
char sex; // 1 字節(jié)
int num; // 4 字節(jié)
int grade; // 4 字節(jié)
int age; // 4 字節(jié)
char* name; // 8 字節(jié)
};
printf("student_other1 %d\n",sizeof(struct student_other1));
struct student_other2 {
char sex; // 1 字節(jié)
char* name; // 8 字節(jié)
int num; // 4 字節(jié)
int grade; // 4 字節(jié)
int age; // 4 字節(jié)
};
printf("student_other2 %d\n",sizeof(struct student_other2));
return 0;
}
64位計(jì)算機(jī)?
student:? 8+ 4 + 4?+? 4 + 4 = 24? ?,最后一個(gè)?char sex 本身占用1個(gè)字節(jié),但是為了補(bǔ)齊,和前面的int age 組合成為了一個(gè) 8 字節(jié)。
student_other1 : 4 +4+4+4+8 =24 , 第一個(gè)char sex 本身占用1個(gè)字節(jié),但是為了補(bǔ)齊,和后面的int num?組合成為了一個(gè) 8 字節(jié)
student_other2:? 8 +? 8 + 8 + 8 =32 , 第一個(gè) char sex本身占用1個(gè)字節(jié),但是為了補(bǔ)齊7個(gè)字節(jié),這次單獨(dú)占用8個(gè)字節(jié);? 最后一個(gè) int age 4字節(jié)無法和其他人湊成 8字節(jié)的倍數(shù)了,只能被空位填充,補(bǔ)齊4個(gè)字節(jié),也就變成了8。
字節(jié)對(duì)齊是為了 提高讀寫效率,是存儲(chǔ)空間的倍數(shù), 示例中 最后一個(gè)是 把 占空間最大的指針放在了 屬性順序的中部,導(dǎo)致前面的屬性和后面的屬性都需要補(bǔ)空位才能實(shí)現(xiàn)對(duì)齊,而不是第一個(gè)和第二個(gè)可與其他屬性拼湊來實(shí)現(xiàn)字節(jié)對(duì)齊,所以 第三種是32個(gè)字節(jié)。
結(jié)論是: 不要把 本身所占空間大的屬性 寫在 中間,而是按照各屬性大小,從小到大進(jìn)行書寫,這樣最大的寫在尾部,即 存儲(chǔ)空間遞增的順序來書寫定義每個(gè)屬性,這樣能節(jié)省空間。
7.位字段
位字段其實(shí)就是 二進(jìn)制的位,比如: 在TCP/IP協(xié)議中,有一些標(biāo)志位如下圖所示:
?URG/ACK/RST/PSH/SYN/FIN 等標(biāo)志位
下面代碼僅做示例,不是真實(shí)的Linux tcp的代碼:
struct flag {
unsigned int urg:1;
unsigned int ack:1;
unsigned int rst:1;
unsigned int psh:1;
unsigned int syn:1;
unsigned int fin:1;
unsigned int :6;
unsigned int head:4;
} flag;
flag.urg = 0;
flag.ack = 1;
printf("flag %d\n",sizeof(struct flag));
每個(gè)屬性后面的:1 表示指定這些屬性只占用 一個(gè)二進(jìn)制位,? :6代表保留的6位,:4代表是頭部4位。
8. 彈性數(shù)組成員
如果在struct中聲明數(shù)組成員,但我一開始不知道有多少,如果給定一個(gè)很大的數(shù)會(huì)浪費(fèi)空間。文章來源:http://www.zghlxwxcb.cn/news/detail-469735.html
struct vstring {
int len;
char chars[];
};
struct vstring* str = malloc(sizeof(struct vstring) + n * sizeof(char));
str->len = n;
彈性成員的數(shù)組,必須是 struct 結(jié)構(gòu)的最后一個(gè)屬性。另外,除了彈性數(shù)組成員,struct 結(jié)構(gòu)必須至少還有一個(gè)其他屬性。文章來源地址http://www.zghlxwxcb.cn/news/detail-469735.html
到了這里,關(guān)于我記不住的那些C語言的struct知識(shí)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!