結構體概述 : 結構體是 多個 變量的集合, 變量的類型可以不同;
-- 可進行的操作 : 結構體可以進行 拷貝 賦值操作, 可以作為 函數參數 和 函數返回值;
結構體聲明 : struct 結構標記 {結構成員} 普通變量;
-- 結構體示例 :
struct student { char *name; int age; };
-- 結構成員 : 在 {} 中定義的變量就是結構成員;
-- 普通變量 : 在聲明結構體的時候後面可以加上若干普通變量, 相當於定義結構體變量;
結構體變量聲明 : 可以在定義的時候聲明變量, 也可以在定義完結構體使用 結構標記 聲明變量;
-- 定義結構體時聲明變量 : 這種聲明變量的方式可以不用 結構標記, 變量名寫在 花括號 後面, 用頭號隔開;
struct student { char *name; int age; } s1, s2, s3;
struct student s4, s5, s6;
結構體內存分配 : 結構體內存是在聲明變量的時候分配的, 如果只聲明了結構體, 沒有聲明對應變量, 那麼不會分配內存;
結構體變量初始化 :
-- 聲明結構體的時候初始化 : struct student s1 = {Tom, 12} ; 注意 初值表中必須時結構體對應類型的常量表達式;
-- 聲明之後初始化 : 結構體變量名.成員名 可以訪問結構體中的成員變量, s1.name = Tom; s2.age = 12;
結構體嵌套 : 結構體中的成員變量可以是 結構體變量;
struct student { char *name; int age; } s1; struct class { struct student s1; struct student s2; } c1;
結構體代碼示例 :
/************************************************************************* > File Name: base_struct.c > Author: octopus > Mail: octopus_work.163.com > Created Time: 2014年03月24日 星期一 10時49分46秒 ************************************************************************/ #includeint main(int argc, char **argv) { /* * 聲明結構體 同時聲明變量s1 */ struct student { char *name; int age; } s1; /* * 結構體嵌套 */ struct class { struct student s1; struct student s2; } c1; struct student s2 = {Tom, 12};/*只有聲明的時候才能對結構體初始化才能使用花括號賦值*/ struct class c2 = {{Jack, 13}, {Pig, 15}}; s1.name = Hack; /*變量聲明後對結構體賦值只能一個一個賦值*/ s1.age = 14; //s1 = {fuck, 1}; /*只有在初始化的時候才能使用 花括號初始化結構體變量*/ c1.s1.name = CJ; c1.s1.age = 21; c1.s2.name = KW; c1.s2.age = 22; /*訪問結構體中的變量, 使用 . 進行訪問*/ printf(s1 : name = %s, age = %d , s1.name, s1.age); printf(s2 : name = %s, age = %d , s2.name, s2.age); printf(c1 : s1 : name = %s, age = %d ; s2 : name = %s, age = %d , c1.s1.name, c1.s1.age, c1.s1.name, c1.s2.age); printf(c2 : s1 : name = %s, age = %d ; s2 : name = %s, age = %d , c2.s1.name, c2.s1.age, c2.s1.name, c2.s2.age); return 0; }
octopus@octopus-Vostro-270s:~/code/c/struct$ gcc base_struct.c octopus@octopus-Vostro-270s:~/code/c/struct$ ./a.out s1 : name = Hack, age = 14 s2 : name = Tom, age = 12 c1 : s1 : name = CJ, age = 21 ; s2 : name = CJ, age = 22 c2 : s1 : name = Jack, age = 13 ; s2 : name = Jack, age = 15
結構體的合法操作 :
-- 整體復制 : 結構體可以復制;
-- 整體賦值 : 聲明結構體的時候可以整體賦值, 在其它情況下不可以;
-- & 取地址 : 使用 & 運算符獲取 結構體地址;
-- 訪問成員 : 使用 結構體變量名.成員變量名 可以訪問成員變量;
函數傳遞結構體方法 :
-- 傳遞結構體成員 : 將結構體拆開, 將其中的成員變量分別傳入;
struct class create_class(struct student s2, struct student s3) { struct class c1 = {s2, s3}; return c1; }
-- 傳遞結構體 : 將整個結構體當做參數傳入, 這種情況和傳遞其它類型參數一樣, 都是通過值傳遞的;
struct class create_class(struct student s2, struct student s3) { struct class c1 = {s2, s3}; return c1; }
-- 傳遞結構體指針 : 傳遞結構體的指針, 訪問形式如下;
/* * 傳入一個結構體指針 * 通過指針訪問結構體的方法 : * (*結構體指針變量).成員變量 訪問; * 結構體指針變量 -> 成員變量 訪問; */ void printf_struct_pointer(struct student *s) { printf(student : (*s).name = %s, (*s).age = %d , (*s).name, (*s).age); printf(student : s->name = %s, s->age = %d , s->name, s->age); }
結構體指針訪問 :
-- 示例 : 定義一個結構體指針;
struct student { char *name; int age; }*p;
-- . 和 ->優先級 : 這兩個運算符都是從左到右運算, 都是右結合; . 和 -> 優先級比 * , ++ 優先級要高; 這兩個運算符 與 () [] 是四個優先級最高的運算符;
-- ++p->age 分析 : 是對結構體中的 age 進行自增操作;
-- *p->name 分析 : 獲取 結構體中的 name 字符串的值(注意不是指針|地址);
-- *p++->name 分析 : 先獲取 name 字符串的值, 再將p自增;
結構體函數示例 :
/************************************************************************* > File Name: method_struct.c > Author: octopus > Mail: octopus_work.163.com > Created Time: 2014年03月24日 星期一 14時46分16秒 ************************************************************************/ #include/*聲明一個結構體類型, 其成員變量是普通變量*/ struct student { char *name; int age; }; /*聲明一個結構體類型, 其成員變量是結構體*/ struct class { struct student s1; struct student s2; }; /* * 傳遞 2 個結構體的成員變量 * 在函數中創建結構體並返回 */ struct student create_student(char *name, int age) { struct student s1 = {name, age}; return s1; } /* * 傳遞 2 個結構體變量 */ struct class create_class(struct student s2, struct student s3) { struct class c1 = {s2, s3}; return c1; } /* * 傳入一個結構體指針 * 通過指針訪問結構體的方法 : * (*結構體指針變量).成員變量 訪問; * 結構體指針變量 -> 成員變量 訪問; */ void printf_struct_pointer(struct student *s) { printf(student : (*s).name = %s, (*s).age = %d , (*s).name, (*s).age); printf(student : s->name = %s, s->age = %d , s->name, s->age); } int main(int argc, char **argv) { /*使用函數獲取一個結構體, 傳入結構體的值*/ struct student s1 = create_student(Tom, 11); printf(student : name = %s, age = %d , s1.name, s1.age); /*創建一個成員變量是結構體的結構體, 並打印結構體數據*/ struct class c1 = create_class(create_student(Jack, 12), create_student(CJ, 13)); printf(c1 : s1 : name = %s, age = %d ; s2 : name = %s, age = %d , c1.s1.name, c1.s1.age, c1.s2.name, c1.s2.age); /*聲明結構體指針*/ struct student *p = &s1; printf_struct_pointer(p); return 0; }
octopus@octopus-Vostro-270s:~/code/c/struct$ gcc method_struct.c octopus@octopus-Vostro-270s:~/code/c/struct$ ./a.out student : name = Tom, age = 11 c1 : s1 : name = Jack, age = 12 ; s2 : name = CJ, age = 13 student : (*s).name = Tom, (*s).age = 11 student : s->name = Tom, s->age = 11
聲明結構體數組 :
-- 聲明結構體的時候聲明結構體數組 : 格式為 : struct 結構標記 {} 數組名[];
-- 使用結構標記聲明結構體數組 : 格式為 : struct 結構標記 數組名[];
結構體數組聲明初始化 :
-- 逐個元素初始化 : 數組名[] = {{結構體1}, {結構體2}};
-- 總體初始化 : 數組名[] = {常量1, 常量2 ...};
結構體初始化 :
/************************************************************************* > File Name: array_struct.c > Author: octopus > Mail: octopus_work.163.com > Created Time: 2014年03月24日 星期一 16時40分15秒 ************************************************************************/ #include/* * 聲明結構體 * 同時也聲明結構體類型數組 * 為數組初始化 * 直接將每個結構體成員的值依次列出即可 */ struct student { char *name; int age; } team1[] = { Tom, 12, Jack, 13 }; int main(int argc, char **argv) { int i; /*將每個結構體初始化, 賦值, 每個結構體初始化內容使用 花括號括起來*/ struct student team2[] = {{CJ, 34}, {KI, 32}}; for(i = 0; i < 2; i++) { printf(team1 : team1[i].name = %s, team1[i].age = %d , team1[i].name, team1[i].age); } for(i = 0; i < 2; i++) { printf(team2 : team2[i].name = %s, team2[i].age = %d , team2[i].name, team2[i].age); } return 0; }
octopus@octopus-Vostro-270s:~/code/c/struct$ gcc array_struct.c octopus@octopus-Vostro-270s:~/code/c/struct$ ./a.out team1 : team1[i].name = Tom, team1[i].age = 12 team1 : team1[i].name = Jack, team1[i].age = 13 team2 : team2[i].name = CJ, team2[i].age = 34 team2 : team2[i].name = KI, team2[i].age = 32
需求 : 實現一個統計 C 語言關鍵字出現次數;
代碼 :
/************************************************************************* > File Name: word_count.c > Author: octopus > Mail: octopus_work.163.com > Created Time: 2014年03月24日 星期一 17時12分32秒 ************************************************************************/ #include#include #include #define MAXWORD 10 /* * 定義結構體類型 key * 該類型結構體中存儲一個 字符串(關鍵字) 和 int 數據(關鍵字出現次數) * 同時聲明一個結構體數組 * 對結構體數組進行初始化 * * */ struct key { char *word; int count; }key_count[] = { auto, 0, break, 0, case, 0, char, 0, const, 0, continue, 0, default, 0, void, 0, volatitle, 0, while, 0 }; int main(int argc, char **argv) { int n; char word[MAXWORD]; /*循環接收字符串, 如果字符串與結構體數組中匹配, 結構體的count ++*/ while(getword(word, MAXWORD) != EOF) if(isalpha(word[0])) if((n = binsearch(word, key_count, 10)) >= 0) key_count[n].count++; /*打印大於0的關鍵字 及 個數*/ for (n = 0; n < 10; n ++) if(key_count[n].count > 0) printf(%2d %s , key_count[n].count, key_count[n].word); return 0; } /* * 重要api解析 : * int getc(FILE *stream) 從標准輸入流中讀取字符 * int ungetc(int c, FILE *stream) 將字符退回到標准輸入流中 * int isspace(int c) 判斷字符是否是 空格 '' ' ' ' ' ' ' '' * int isalpha(int c) 判斷是否是字母 */ int getword(char *word, int lim) { int c, getc(FILE*), ungetc(int, FILE*); char *wp = word; /*過濾空格, 如果輸入的不是 空, 就繼續向下執行*/ while(isspace(c = getc(stdin))); /*如果輸入的不是結束符, 那麼 wp指針, 先取值, 之後地址自增*/ if(c != EOF) *wp++ = c; /*如果輸入的不是字母, 直接返回, 關鍵字裡面沒有數字開頭的*/ if(!isalpha(c)) { *wp = ''; return c; } /* * 循環條件 a. 接收的最大字符個數 lim, 每讀取一個字符, 該變量自減 * 當該變量自減為0時停止循環接收字符串 * 循環條件 b. 當讀取到的字符 不是 字母 或者數字的時候, 停止循環 */ for(; --lim > 0; wp++) { if(!isalnum(*wp = getc(stdin))) { ungetc(*wp, stdin); break; } } *wp = ''; return word[0]; } /* * 參數解析 : word 是要查找的字符串 tab 字符串數組 n 字符串大小 */ int binsearch(char *word, struct key tab[], int n) { /* * cond 判斷 查找的字符串 是在mid 坐標之前 (<0) 之後(>0) 或者就是mid坐標 * * 如果查找的不是正好中間的變量符合, 就返回 -1 失敗 */ int cond, low, high, mid; low = 0; high = n - 1; /* * 要找的值的下標在low 和 high之間 * mid 是 low 和 high 的中值 * 如果 word 小於中值下標 將比較范圍 縮小 */ while(low <= high) { mid = (low + high) / 2; if((cond = strcmp(word, tab[mid].word)) < 0) high = mid - 1; else if(cond > 0) low = mid + 1; else return mid; } return -1; }
執行結果 :
[root@ip28 struct]# gcc word_count.c [root@ip28 struct]# ./a.out auto break break char 1 auto 2 break 1 char
宏定義方法 : 獲取結構體數組大小;
-- sizeof 方法 : sizeof (對象) | sizeof (類型名稱) 可以獲取對象 或者 類型占用的存儲空間, 其返回值是 size_t 類型的, 定義在stddef.h 頭文件中;
-- 使用類型測量 :
#define KEYS (sizeof(key_count) / sizeof(struct key))-- 使用對象測量 :
#define KEYS (sizeof(key_count) / sizeof(struct key_count[0])
使用指針進行二分查找 :
-- 使用下標找中值 : 在之前找中值時通過 mid = (low + high)方法, 這樣做可行是因為 low 從0開始的;
-- 如果是指針情況 : mid low high 都是指針, 那麼就不能使用上面的那種方法了, 使用 mid = low + (high - low) / 2;.
-- 指針操作情況的 high 和 low : 其中 low 是首元素的 首地址, high 是 尾元素的尾地址, 只有這樣 它們的差的 0.5 倍才是准確的中值的首地址;
指針指向數組注意點 : 不要生成非法的指針, 指針不能指向數組之外的元素;
-- &key_count[-1] : 這個指針時非法的;
-- &key_count[n] : 對數組的最後一個元素後面的第一個元素進行 & 運算時合法的, 其它操作都是非法的;
示例程序 :
/************************************************************************* > File Name: pointer_struct.c > Author: octopus > Mail: octopus_work.163.com > Created Time: Tue 25 Mar 2014 12:31:08 AM CST ************************************************************************/ #include#include #include #define MAXWORD 20 /*計算結構體數組的大小*/ #define KEYS (int)(sizeof(key_count) / sizeof(struct key)) struct key { char *word; int count; } key_count[] = { auto, 0, break, 0, case, 0, char, 0, const, 0 }; int getword(char *, int); struct key *binsearch(char*, struct key*, int); int main(int argc, char **argv) { char word[MAXWORD]; struct key *p; /*存放查找方法返回的結構體指針, 該指針指向數組中查找到元素的下標*/ while(getword(word, MAXWORD) != EOF) if(isalpha(word[0])) if((p = binsearch(word, key_count, KEYS)) != NULL) p->count++; for(p = key_count; p < key_count + KEYS; p++) if(p->count > 0) printf(%2d %s , p->count, p->word); return 0; } /* * 沒有循環控制變量的 for 循環, 在內部通過條件 break */ int getword(char *word, int max) { int c, getc(FILE*), ungetc(int, FILE*); char *wp = word; /*處理第一個字符, 第一個字符不是 空 不是 EOF 再進行下面的操作*/ while(isspace(c = getc(stdin))); if(c != EOF) *wp++ = c; if(!isalpha(c)) { *wp = ''; return c; } /*循環接收字符串, 字符串接收到非標識符 或者 到達個數上限停止循環*/ for(; --max > 0; wp++) if(!isalnum(*wp = getc(stdin))) { ungetc(*wp, stdin); break; } *wp = ''; return word[0]; } /* * 注意點 : * 取兩個地址的中值 : 一個數組n個元素, 其中值計算 是由 首元素的首地址 和 尾元素的尾地址計算的 * 二分查找 : * 如果要把區間前移, 那麼就需要將尾地址設置為 中間元素前一個元素的尾地址, 即中間元素的首地址 * 如果要把區間後移, 那麼就需要將首地址設置為 中間元素後一個元素的首地址, 即中間元素 + 1 的地址 * * 指向結構體數組的指針 : * struct key tab * 是指向結構體數組指針, 該指針可以操作結構體數組 */ struct key *binsearch(char *word, struct key *tab, int n) { int cond; struct key *low = &tab[0]; /*0元素的首地址*/ struct key *high = &tab[n]; /*尾元素的尾地址*/ struct key *mid; while(low < high) { /*計算中間值的地址*/ mid = low + (high - low) / 2; if((cond = strcmp(word, mid->word)) < 0) high = mid; /*mid 是 中間元素前一個元素的尾地址*/ else if(cond > 0) low = mid + 1; /*這裡low要成為mid後一個元素的首地址*/ else return mid; } return NULL; }
octopus@octopus-Vostro-270s:~/code/c/struct$ gcc pointer_struct.c octopus@octopus-Vostro-270s:~/code/c/struct$ ./a.out auto case auto break 2 auto 1 break 1 case
結構體數組指針算術運算 : struct key *p = word_count; 指針 p 指向 結構體數組, 如果 p + 1 , 結果是 p 地址 加上 結構體所占內存大小;
結構體大小 : 結構體的大小不是完全等於各個成員的長度之和, 對象之間有對齊要求;
-- 空穴 : 對象間對齊, 會產生空穴, 占有空間, 但是不存儲數據;
示例 : 結構體中由一個 char 和 int , 占用的空間卻是 8個字節, 它們的和是 5個字節;
/************************************************************************* > File Name: memory_struct.c > Author: octopus > Mail: octopus_work.163.com > Created Time: 2014年03月25日 星期二 12時55分45秒 ************************************************************************/ #includestruct word { char c; int i; }; int main(int argc, char **argv) { printf(sizeof(struct word) = %d , sizeof(struct word)); return 0; }
octopus@octopus-Vostro-270s:~/code/c/struct$ gcc memory_struct.c octopus@octopus-Vostro-270s:~/code/c/struct$ ./a.out sizeof(word) = 8
.
作者 : 萬境絕塵
轉載請注明出處 : http://www.hanshuliang.com/?post=30
.