p.s. 從十四章寫起,前面的一些章節(jié)不想補(二周目再說)
因為是基于書上的東西,我會做一些筆記,還有一些自己的理解(畢竟不是看原著只是看中文譯本),略過的部分讀者自行看書,有些是比較簡單讀者看書就懂,我就不再贅述。絕對不是懶[微笑]。
14.2 建立結(jié)構(gòu)聲明
14.3定義結(jié)構(gòu)變量
14.3.1初始化結(jié)構(gòu)
14.3.2訪問結(jié)構(gòu)體成員
14.3.3結(jié)構(gòu)體初始化器
14.4結(jié)構(gòu)體數(shù)組
14.5嵌套結(jié)構(gòu)
14.6指向結(jié)構(gòu)的指針
14.6.1聲明和初始化結(jié)構(gòu)指針
14.6.2用指針訪問成員
14.7.1傳遞結(jié)構(gòu)成員
14.7.2傳遞結(jié)構(gòu)的地址
14.7.3傳遞結(jié)構(gòu)
14.7.4其他結(jié)構(gòu)特性
14.7.5 結(jié)構(gòu)和結(jié)構(gòu)指針的選擇
14.7.6結(jié)構(gòu)中的字符數(shù)組和字符指針
14.8把結(jié)構(gòu)內(nèi)容保存到文件中
14.10聯(lián)合介紹(union)
14.2(定義)
結(jié)構(gòu)的定義語法,struct可以不跟名字(不推薦),然后是變量(成員)的定義,變量的定義和平常我們的定義變量沒有區(qū)別。
struct name{
data_type value_name;
};
struct { //不推薦
data_type value_name;
};
14.3(結(jié)構(gòu)體變量)
這是兩種定義的方法,在實際情況下我們我們會把struct放在.h里定義變量并不是一個好習(xí)慣,我們應(yīng)該在使用的時候再定義,并注意其它的生存周期。
struct name{ //可以沒有name 對照上面
data_type value_name;
}joe;
struct name joe,*Moon,stu[10];

14.3.1(初始化)
數(shù)值,字符或字符串之間,只需要用逗號隔開即可,如果是更復(fù)雜的結(jié)構(gòu)我們通過換行來提高閱讀性。(ANSI之前,不能用自動變量初始化結(jié)構(gòu):ANSI之后可以同任意存儲類型),初始化與初始化數(shù)組類似。
struct rectangle{
float length;
float width;
};
struct rectangle r1={4.0,2.0};
注意 初始化結(jié)構(gòu)體和類型存儲期
第12章中提到,如果初始化一個具有靜態(tài)存儲時期的變量,只能使用常量。這條規(guī)則同樣使用與結(jié)構(gòu)體。如果存儲時期是自動的,列表的值就不必是常量了。
14.3.2(訪問結(jié)構(gòu)成員)
結(jié)構(gòu)本身類似于本質(zhì)上是一個超級數(shù)組,我們可以這樣訪問成員(變量)
r1.length=2;
printf("%f\n",r1.length);
scanf("%f",&r1.width);
這里由于 . 的運算優(yōu)先級比 & 更高所以&r1.width等同于&(r1.width)
14.3.3(結(jié)構(gòu)體初始化器)
#include "stdio.h"
struct man{
int length;
int width;
int weight;
};
int main(void){
struct man p1={.weight=10,
.length=21,
.width=2,
3
};
printf("%d%2d%2d\n",p1.length,p1.width,p1.weight);
}
最后一次賦值才是它真正的值,但賦值其實是按順序的賦值是根據(jù)前一個指向的成員的位置確認(rèn)下一個成員即便沒有成員名。
struct man p1={
.length=21,
.width=2,
.weight=10,
3
};
這樣的一個寫法是3只會警告為無效,而不會出錯,由此可見編譯器是按照指向去初始化的,如果只是簡單的struct man p1={21,2,10}初始化編譯器會順序初始化,而我們使用騷操作也是可行的
struct man p1={.length=21,
.width=2,
.weight=10,
.width=3,
3,
.length=10,
9
};
//為騷而騷了,實際上并不會這么使用
14.4結(jié)構(gòu)體數(shù)組
struct man people[100]這不是一個合適的做法,因為是自動存儲類別變量,其中的信息會存到棧中,如果數(shù)量過大可能會導(dǎo)致溢出,這時可以設(shè)置編譯器中棧的大小,又或者可以創(chuàng)建靜態(tài)或者外部數(shù)組。(我們并不建議過大的數(shù)組)。

代碼14.2(部分代碼)
//只是大概情況,絕對沒偷懶[微笑]
#define len 40
struct book{
char title[len];
char author[len];
float value;
};
int main(){
......
scanf("%f",&library[count++].value);
......
return 0;
}
14.5嵌套結(jié)構(gòu)
結(jié)構(gòu)嵌結(jié)構(gòu)其實是十分常見的,不過既然是結(jié)構(gòu)體,那自然可以套用結(jié)構(gòu)體聲明的規(guī)則了。同時我們用 . 或者->來訪問結(jié)構(gòu)體變量的結(jié)構(gòu)體成員的成員(該用哪個我們視情況而定)。
#define len 30
struct name{
char first_name[len];
char last_name[len];
};
struct book{
char title[len];
struct name author;
float value;
};
int main(){
struct book mybook={"c語言從入門到放棄",
{"蝦說","吳"},
54.32
};
printf("hello %s%s",mybook.author.last_name,mybook.author.first_name);
return 0;
}
14.6(指向結(jié)構(gòu)的指針)
喜歡使用指針的人如果得知能夠最使用指向結(jié)構(gòu)的指針會十分高興。原因有四
第一: 就像指向數(shù)組的指針比數(shù)組本身更容易操作(排序中會很明顯),指向結(jié)構(gòu)的指針通常比結(jié)構(gòu)本身更容易操作。
第二: 在早期的c實現(xiàn)中,結(jié)構(gòu)不能作為參數(shù)被傳遞給函數(shù)。但指向結(jié)構(gòu)的指針可以。
第三:即便可以傳遞結(jié)構(gòu),傳遞指針也更有效率。
第四:許多的數(shù)據(jù)表示,都使用了包含指向其他結(jié)構(gòu)的指針的結(jié)構(gòu)。
14.6.1(聲明和初始化結(jié)構(gòu)指針)
struct book mybook;
struct book papers[10];
struct book *p=NULL; //置空是個好習(xí)慣
p=&mybook;
p=&papers[0];
book結(jié)構(gòu)的大小的94(假設(shè)float是4)字節(jié),但是在一些系統(tǒng)中,一個結(jié)構(gòu)的大小可能比各成員大小的總和還大一點,這是因為系統(tǒng)對數(shù)據(jù)進行校準(zhǔn)過程中產(chǎn)生了“間隙”,例如有些系統(tǒng)必須把每個成員放在偶數(shù)地址上,或是4的倍數(shù)的地址上。
14.6.2(用指針訪問成員)
常見的訪問方式.和->,這兩者區(qū)別,我們可以通俗的理解,一個實際的變量如 papers[0],*p我們用.訪問成員,而一些地址如p, papers, (&mybook)我們使用->來訪問成員。
p=&mybook;
mybook.value == p->value ==(*p).value ==(&mybook).value;
//*的優(yōu)先級低于.,必須先用括號,同理&也要加括號
14.7.1-4(傳遞成員、地址、結(jié)構(gòu)和其他特性)
char* sum1(char *,char *); //傳遞成員
struct book *sum2(struct book *); //傳遞地址
struct book sum3(struct book); //傳遞結(jié)構(gòu)
struct book mybook={"c語言從入門到放棄",
{"蝦說","吳"},
54.32
};
full_name1(mybook.names.first_name,mybook.names,last_name);
full_name2(&mybook);
full_name3(mybook);
struct book youbook=mybook;
現(xiàn)在的c(第六版的出版年)包括ANIS C,允許結(jié)構(gòu)作為參數(shù)傳遞,還能作為參數(shù)傳遞。這里還是講究數(shù)據(jù)類型和函數(shù)的內(nèi)容
14.7.5(結(jié)構(gòu)和結(jié)構(gòu)指針的選擇)
把指針作為參數(shù)有兩個優(yōu)點:任何C都可以實現(xiàn),執(zhí)行速度快,只要地址。缺點是無法保護數(shù)據(jù)在調(diào)用函數(shù)的某些操作可能使數(shù)據(jù)發(fā)生改變,ANSI C用const解決了這個問題??梢哉f是克服了缺點但是實際應(yīng)用難免修改某個成員的值。
而傳遞結(jié)構(gòu)的優(yōu)點是,只是在修改副本,保護了原始數(shù)據(jù),當(dāng)然也更易閱讀。缺點是增加了內(nèi)存空間和傳遞時間。當(dāng)他只使用少數(shù)的參數(shù)時會顯得特別不討好。
這樣說來,指針樣樣都好,但是程序的可閱讀性和維護性是你應(yīng)該想的(可能你在寫一個簡單的算法和demo時想不到這些因素)。
14.7.6結(jié)構(gòu)的字符數(shù)組和指針
如果編譯器比較寬容的話,代碼在運行時不會出錯。我用的是gcc 版本 6.4.1 2017 07 27 (Red Hat 6.4.1-1) (GCC)
#define len 30
struct name1{
char first_name[len];
char last_name[len];
};
struct name2 {
char *first;
char *last;
};
struct name1 programer;
name2 coder;
scanf("%s",programer.first_name);
scanf("%s",coder.first);
printf("pro first %s in %p\n",programer.first_name,&programer.first_name);
printf("cod first %s in %p\n",coder.first,coder.first);

如果你知道字符常量,我們簡單的測了一下就出了問題
int main() {
char *str="abc";
struct name1 programer;
struct name2 coder;
scanf("%s", programer.first_name);
scanf("%s", coder.first);
printf("str %s in %p\n",str,str);
printf("pro first %s in %p\n",programer.first_name,&programer.first_name);
printf("cod first %s in %p\n",coder.first,coder.first);
return 0;
}

指針太騷還是要慎用又或是我們用
coder.first=(char *)malloc(···);給他一塊專屬內(nèi)存,不需要的時候free掉free(coder.first)。當(dāng)然指針管理數(shù)據(jù)還是十分收到青睞。
14.7.8(復(fù)合型字面量 c99 )
這相當(dāng)于即用即定義,和尋常的變量一樣,但是它沒有名字并不一個好事。如果它是全局變量則是靜態(tài)存儲期,在塊中則是自動存儲期。復(fù)合字面量和普通初始化列表采取一樣的規(guī)則,騷操作指定初始化器一樣可以使用。
14.8(把結(jié)構(gòu)內(nèi)容保存到文件中)
以二進制表示法儲存數(shù)據(jù)的缺點是,不同的系統(tǒng)可能使不同的二進制表示法,所以數(shù)據(jù)文件可能不具有移值性。甚至在同一個系統(tǒng),不同編譯器設(shè)置也可能導(dǎo)致不同的二進制布局。
14.10(聯(lián)合簡介)
#include "stdio.h"
union all {
int a;
_Bool b;
char c;
};
int main() {
union all test;
scanf("%d", &test.a);
printf("%d,%d,%c\n", test.a, test.b, test.c);
scanf("%d", &test.b);
printf("%d,%d,%c\n", test.a, test.b, test.c);
scanf("%d", &test.c);
printf("%d,%d,%c\n", test.a, test.b, test.c);
}

即
&test==&test.a==&test.b==&test.c使用初始化結(jié)構(gòu)器的情況,有區(qū)別于結(jié)構(gòu)體的初始化(沒騷起來…)
int main() {
union all test = {.b=3,89,.a=77,0};
printf("%d",test);
return 0;
}
