1. 結構體聲明
學習C#/Java的我們,結構體相信都非常熟悉了。簡單先來介紹下文法,聲明一個結構體:
struct{ char *name; int age;}person1,person2;int main (void){ person1.name="kym"; person1.age=21; printf("%s",person1.name); return 0;}
當然,我們也可以在聲明時直接初始化:
struct{ char *name; int age;}person1={"kym",21}, person2={"test",30};int main (void){ printf("%s",person2.name); return 0;}
但是我們在這裡可以發現一點,聲明的結構體是不能重用的,也就是說,必須要在結構體後一次性地把所有需要用到的結構體變數全部初始化,於是,我們更多的是這麼做。
struct person{ char *name; int age;};int main (void){ struct person person1={"kym",21}; printf("%s",person1.name); return 0;}
如果你寫習慣了Person person1這樣的代碼而對在結構體名字前跟著struct而感到不爽,那你也可以這樣來寫:
typedef struct{ char *name; int age;}Person;int main (void){ Person person1={"kym",21}; printf("%s",person1.name); return 0;}
2. 結構體瑣碎
首先,無需我多言,C語言的結構體複製時,兩份結構體分別有著自己的地址,因此,當我對其賦值時,兩個結構體沒什麼關係。說不明白了…….看下代碼吧
typedef struct{ char *name; int age;}Person;int main (void){ Person person1={"kym",21}; Person person2=person1; printf("person2:%s\n",person2.name); person1.name="huangxin"; printf("person2:%s",person2.name); return 0;}
我們可以看到,對person1的改變並沒有引起person2的隨之改變。
再來看一個例子:
typedef struct{ char *name; int age;}Person;void ChangePerson(Person);int main (void){ Person person1={"kym",21}; printf("person1:%s\n",person1.name); ChangePerson(person1); printf("person1:%s",person1.name); return 0;}void ChangePerson(Person person1){ person1.name="huangxin";}
我們可以看到 ,person1的值並沒有發生任何改變,由此說明,在傳參時是傳遞了一份新的拷貝,因此當結構體比較大時,產生結構所有成員的副本,這樣給系統增加了一定的開銷,因此我們可以利用指標來傳遞結構體。
typedef struct{ char *name; int age;}Person;void ChangePerson(Person *);int main (void){ Person person1={"kym",21}; printf("person1:%s\n",person1.name); ChangePerson(&person1); printf("person1:%s",person1.name); return 0;}void ChangePerson(Person *person1){ (*person1).name="huangxin";}
現在來看一下結構體所佔的位元組數:
typedef struct{ double name; int age;}Person;int main (void){ printf("int:%d\n",sizeof(int)); printf("double:%d\n",sizeof(double)); printf("Person:%d",sizeof(Person)); return 0;}
為什麼會出現這樣的情況呢?那是因為有些電腦要求特定資料項目的地址比如是某個位元組數(可能是2,4或者是8)的倍數,於是為了滿足這個要求,編譯器會在臨近的成員,或者成員後添加一些記憶體空洞,用來使成員對齊。
3. 聯合體
曾經學聯合體時,我一直不知道他的應用情境,這次終於搞明白了。
先來看一下聯合體的特徵,就是編譯器只為聯合中最大的成員分配記憶體空間。看段代碼吧:
typedef union{ double name; int age;}Person;int main (void){ printf("int:%d\n",sizeof(int)); printf("double:%d\n",sizeof(double)); printf("Person:%d",sizeof(Person)); return 0;}
那麼他應用於什麼應用情境呢?我們來考慮一次考試的情況。英語不好,就漢語編程了……
typedef struct{ char *name; int totalScore; ///begin Math int 選擇題分數; int 填空題分數; int 解答題分數; int 證明題分數; ///end Math ///begin Chinese int 選擇題分數; int 閱讀題分數; int 作文分數; ///end Chinese}Exam;
這樣本身沒什麼問題,但是如果說,每次我只想訪問某一學科,這樣的結構體就比較浪費空間,那麼我們就可以用聯合體來解決這個問題:
typedef struct{ char *name; int totalScore; union { struct { int 選擇題分數; int 填空題分數; int 解答題分數; int 證明題分數; }Math; struct { int 選擇題分數; int 閱讀題分數; int 作文分數; }Chinese; }; ///end Chinese}Exam;int main (void){ Exam exam={"kym",100}; exam.Math.解答題分數 = 30; exam.Math.填空題分數 = 20; exam.Math.選擇題分數= 20; exam.Math.證明題分數 = 30;}
當然,我們在這裡要注意,我們只能為Math和Chinese的一者複製,因為兩者是公用一塊記憶體空間,如果同時賦值,會出現覆蓋的情況。
4. 用聯合體來構造混合類型數組
我們來巧用一下聯合體,我們知道,數組必須要是同一類型的元素。但是我們可以用聯合體來構造混合類型數組。
typedef union{ int i; double d;}Number;int main (void){ Number numberArray[2]; numberArray[0].i=10; numberArray[1].d=3.4; return 0;}
也許看到這裡,有人會說,如果這樣也算的話,那我用結構體也可以實現啊,可是別忘了,結構體要比聯合體耗記憶體的多,尤其是我們想做個類似於Object類型數組的時候。
接下來我們繼續想,當我們要獲得這個混合類型的某個元素時怎麼辦,你怎麼能知道這個元素的類型呢?那麼這個時候,我們就需要一個標誌位。
#define NUMBER_INT 0#define NUMBER_DOUBLE 1typedef struct{ int flag; union { int i; double d; }Temp;}Number;int main (void){ int i ; Number numberArray[2]; numberArray[0].flag=NUMBER_INT; numberArray[0].Temp.i=10; numberArray[1].flag=NUMBER_DOUBLE; numberArray[1].Temp.d=3.14; for(i=0;i<sizeof(numberArray)/sizeof(Number);i++) { if(numberArray[i].flag==NUMBER_INT) { printf("%d:%d\n",i,numberArray[i].Temp.i); } else if (numberArray[i].flag==NUMBER_DOUBLE) { printf("%d:%f\n",i,numberArray[i].Temp.d); } }}
這樣我們就完成了一個混合類型數組的編寫。