C语言再学习 -- 结构和其他数据形式
一、結構體
結構體可以用來創建新的數據類型,這種數據類型可以把多個其他類型合并成一個整體,采用結構體聲明的變量叫做結構變量,結構體需要先聲明然后才能使用,聲明結構體需要使用struct關鍵字,結構體聲明語句不會分配內存,它可以寫在很多地方,使用結構體聲明變量的時候需要把struct關鍵字和結構體名稱合并起來作為數據類型使用,結構體變量才真正占有內容,才真正能記錄數字。
結構聲明:是描述結構如何組合的主要方法。聲明就像下面這樣
struct book {char title[MAXTITL];char author[MAXAUTL];float value; }; 首先使用關鍵字 struct,它表示接下來是一個結構。后面是一個可選的標記(book),它是用來引用該結構的快速標記。因此,以后我們就可以這樣聲明:
struct book library; 它把 library 聲明為一個使用book結構設計的結構變量。
在結構聲明中,接下來使用一對花括號括起來的結構成員列表。每個成員變量都用它自己的聲明來描述,用一個分號來結束描述。
//定義結構體 struct book {char title[MAXTITL];char author[MAXAUTL];float value; }library; //初始化結構體 struct book library = {"The Pirate and the Devious Damsel";"Renee Vivotte";1.95; };C語言中結構體內部不可以包含函數,初始化結構體變量的時候需要為每一個存儲區單獨提供初始化數據,這些數,應該寫在一對大括號里,如果提供了過多的數據多余的丟掉,數據不夠的話補 0,結構體通常不能作為整體使用,一次只能使用其中某一個存儲區,可以在結構體變量名稱后加 ?(.)?,然后再加某一部分名稱用來表示結構體變量中的某一存儲區。例如,library.value 就是指 library 的 value 部分。
結構數組:
聲明一個結構數組和聲明其他任何類型的數組一樣。
struct book library[MAXBKS]; library本身不是結構名,它是元素類型為 struct book 結構的數組名。
標識結構數組的成員:
library[0].value library[1].title 總結: library //book 結構數組 library[2] //數組元素,因此是一個 book 結構 library[2].title //char 數組 (library[2]的title 成員) library[2].title[4] //title 成員數組中的一個字符 //嵌套結構 #include <stdio.h> #define LEN 20 const char * msgs[5] = {" Thank you for the wonderful evening, ","You certainly prove that a ","is a special kind of guy, We must get togeter","over a delicious ","grilled salmon and have a few laughs" };struct names {char first[LEN];char last[LEN]; };struct guy {struct names handle;char favfood[LEN];char job[LEN];float income; };int main (void) {struct guy fellow = {{"Ewen", "Villard"},"grilled salmon","personlity coach",58112.00};printf ("Dear %s, \n\n", fellow.handle.first);printf ("%s%s. \n", msgs[0], fellow.handle.first);printf ("%s%s\n", msgs[1], fellow.job);printf ("%s\n", msgs[2]);printf ("%s%s%s", msgs[3], fellow.favfood, msgs[4]);if (fellow.income > 150000.0)puts ("!!");else if (fellow.income > 7500.0)puts ("!");else puts (".");printf ("\n%40s%s\n", " ", "See you soon");printf ("%40s%s\n", " ", "Shalala");return 0; } 輸出結果: Dear Ewen, Thank you for the wonderful evening, Ewen. You certainly prove that a personlity coach is a special kind of guy, We must get togeter over a delicious grilled salmongrilled salmon and have a few laughs!See you soonShalala 指向結構的指針指針就是用來表示變量地址,結構體指針可以用來記錄結構體變量的地址,當一個結構體指針和一個結構體變量捆綁后可以在這個指針后面加 (->),然后再加上某一部分的名稱來表示結構體變量內部的某個存儲區,同類型的結構體變量之間是可以直接賦值。
聲明結構指針:?
struct guy {char favfood[20];char job[20];float income; }; struct guy * him; //向函數傳遞結構信息 #include <stdio.h> #define FUNDLEN 50 struct funds {char bank[FUNDLEN];double bankfund;char save[FUNDLEN];double savefund; }; double sum (double, double ); //傳遞結構成員 double sum1 (const struct funds *); //使用結構地址 double sum2 (struct funds moolah); //把結構作為參數傳遞int main (void) {struct funds stan = {"Garlic-Melon Bank",3024.72,"Lucky`s Saving and Loan",9237.11};printf ("Stan has a total of $%.2f.\n", sum (stan.bankfund, stan.savefund));printf ("Stan has a total of $%.2f.\n", sum1 (&stan));printf ("Stan has a total of $%.2f.\n", sum2 (stan));return 0; }double sum (double x, double y) {return (x + y); }double sum1 (const struct funds * money) {return (money->bankfund + money->savefund); }double sum2 (struct funds moolah) {return (moolah.bankfund + moolah.savefund); } 輸出結果: Stan has a total of $12261.83. Stan has a total of $12261.83. Stan has a total of $12261.83.typedf關鍵字
typedef工具是一種高級數據特性,它使你能夠為某一類型創建你自己的名字。
參看:typedef和#define的不同之處
typedef unsigned char BYTE; typedef unsigned char byte; //小寫字母 使用BYTE 來定義變量BYTE x, y[10], *z; 該定義的作用域取決于 typedef 語句所在的位置。如果定義是在一個函數內部,它的作用域是局部的,限定在那個函數里。如果定義是在函數外部,它將具有全局作用域。
通常,這些定義使用大寫字母,以提醒用戶這個類型名稱實際上是一個符號縮寫。不過,您也可以使用下寫字母。
typedef關鍵字可以用來數據類型起別名,這些別名可以當作數據類型使用,可以把結構體聲明語句和起別名語句合并,這時候可以省略結構體名稱。 /*結構體演示*/ #include <stdio.h> typedef struct //結構體不分配內存 {int age;float height;char name[20]; }PE; //typedef struct person person; int main() {PE person2={23,1.73f,"abcdef"};PE person3={};PE *p_person=NULL;printf("年齡是%d\n",person2.age); //加. 表示結構體中的某一變量printf("身高是%g\n",person2.height); // 單精度浮點占位符%gprintf("姓名是%s\n",person2.name); //字符串占位符 %sp_person=&person2; //指針表示結構體變量地址printf("年齡是%d\n",p_person->age);printf("身高是%g\n",p_person->height);printf("姓名是%s\n",p_person->name);person3=person2;printf("年齡是%d\n",person3.age); //加. 表示結構體中的某一變量printf("身高是%g\n",person3.height); // 單精度浮點占位符%gprintf("姓名是%s\n",person3.name); //字符串占位符 %sreturn 0; } 輸出結果: 年齡是23 身高是1.73 姓名是abcdef 年齡是23 身高是1.73 姓名是abcdef 年齡是23 身高是1.73 姓名是abcdef #include <stdio.h> typedef struct {int row,col; }pt; pt *midpt(const pt *p_pt1,const pt *p_pt2,pt *p_mid) //用指針作形參 {p_mid->row=((p_pt1->row)+(p_pt2->row))/2;p_mid->col=((p_pt1->col)+(p_pt2->col))/2;return p_mid; //用指針作返回值 } int main() {pt pt1={},pt2={},mid={},*p_pt=NULL;printf("請輸入一個點的位置:\n");scanf("%d%d",&(pt1.row),&(pt1.col));printf("請再次輸入一個點的位置:\n");scanf("%d%d",&(pt2.row),&(pt2.col));p_pt=midpt(&pt1,&pt2,&mid); //指針printf("中間點的位置是(%d,%d)\n",p_pt->row,p_pt->col); printf("中間點的位置是(%d,%d)\n",mid.row,mid.col);return 0; } 輸出結果: 請輸入一個點的位置: 12 4 請再次輸入一個點的位置: 1 9 中間點的位置是(6,6) 中間點的位置是(6,6) #include <stdio.h> #include <string.h>typedef struct {int n;float m;char name[20]; }Ptr;int main (void) {Ptr p;//Ptr p = {11, 12.9, "hello"}; strcpy (p.name, "hello"); //注意字符串不能直接賦值p.n = 11;p.m = 12.9;printf ("n = %d, name = %s, m = %g\n", p.n, p.name, p.m);printf ("%d\n", sizeof (Ptr));return 0; } 輸出結果: n = 11, name = hello, m = 12.9 28
二、聯合(union)
聯合是一個在同一個存儲空間里存儲不同類型數據的數據類型。這些存儲區的地址都是一樣的,聯合里不同存儲區的內存是重疊的,修改了任何一個其他的會受影響,聯合的大小是其中最大部分存儲區的大小。
#include <stdio.h>typedef union {char ch;int num; }UN;int main (void) {printf ("sizeof (UN) is %d\n", sizeof (UN));return 0; } 輸出結果: sizeof (UN) is 4
參看:百度百科--union
1. 共用體聲明和共用體變量定義
共用體(參考“共用體”百科詞條)是一種特殊形式的變量,使用關鍵字union來定義
共用體(有些人也叫"聯合")聲明和共用體變量定義與結構體十分相似。其形式為:
union 共用體名{
數據類型 成員名;
數據類型 成員名;
...
} 變量名;
共用體表示幾個變量共用一個內存位置,在不同的時間保存不同的數據類型和不同長度的變量。在union中,所有的共用體成員共用一個空間,并且同一時間只能儲存其中一個成員變量的值。
下例表示聲明一個共用體foo:
union foo{/*“共用”類型“FOO”*/int i; /*“整數”類型“i”*/char c; /*“字符”類型“C”*/double k; /*“雙”精度類型“K”*/}; 再用已聲明的共用體可定義共用體變量。例如,用上面說明的共用體定義一個名為bar的共用體變量, 可寫成:
union foo bar;
在共用體變量bar中, 整型變量 i 和字符變量 c 共用同一內存位置。
當一個共用體被聲明時, 編譯程序自動地產生一個變量, 其長度為聯合中最大的變量長度的整數倍。以上例而言,最大長度是double數據類型,所以foo的內存空間就是double型的長度。
union foo/*“共用”類型“FOO”*/ {char s[10]; /*“字符”類型的數組“S”下面有“10”個元素*/int i; /*“整數”類型i*/ }; 在這個union中,foo的內存空間的長度為12,是int型的3倍,而并不是數組的長度10。若把int改為double,則foo的內存空間為16,是double型的兩倍。
2. 共用體和結構體的區別
1)共用體和結構體都是由多個不同的數據類型成員組成, 但在任何同一時刻, 共用體只存放了一個被選中的成員, 而結構體的所有成員都存在。
2.)對于共用體的不同成員賦值, 將會對其它成員重寫, 原來成員的值就不存在了, 而對于結構體的不同成員賦值是互不影響的。
三、枚舉(enum)
可以使用枚舉類型聲明代表整數常量的符號名稱。通過使用關鍵字 enum,可創建一個新“類型”并指定它可以具有的值(實際上,enum 常量是 int 類型的,因此在使用 int 類型的任何地方都可以使用它)。枚舉類型的目的是提高程序的可讀性。計算機用整數0代表枚舉類型中第一個名稱,后面的名稱一次遞增,可以在聲明枚舉類型的時候指定某個名字用某個整數表示(這個時候后面的名字也會隨之改變),使用宏可以實現和枚舉類似的效果 ?,不關心整數就用枚舉 ?關心就用宏。 #include <stdio.h> typedef enum {CHUN,XIA = 5,QIU,DONG }season; //類型名int main (void) {printf ("CHUN is %d\n", CHUN);printf ("QIU is %d\n", QIU);return 0; } 輸出結果: CHUN is 0 QIU is 6
擴展:
關于 typedef 使用可,參看:C語言再學習 -- 關鍵字struct(轉)
這里主要介紹下,枚舉與 #define宏的區別
1、#define 宏常量是在預編譯階段進行簡單替換。枚舉常量則是在編譯的時候確定其值。
2、一般在編譯器里,可以調試枚舉常量,但是不能調試宏常量。
3、枚舉可以一次定義大量相關的常量,而 #define 宏一次只能定義一次。
思考兩個問題:
1)枚舉能做到的事,#define 宏能不能做到?如果能,那為什還需要枚舉?
枚舉可以自增 1,這樣不用每一個值都定義,而宏必須每個值都定義。而且枚舉是一個集合,代表一類值,像上面例子的春夏秋冬四季,方便使用,而宏不能形成集合。
2)sizeof (sea) 的值是多少?為什么?
#include <stdio.h> typedef enum {CHUN,XIA = 5,QIU,DONG }Season; //類型名int main (void) {Season sea;printf ("sizeof (sea) = %d\n", sizeof (sea)); printf ("sizeof (Season) = %d\n", sizeof (Season)); return 0; } 輸出結果: sizeof (sea) = 4 sizeof (Season) = 4
因為枚舉類型聲明代表整數常量,整型是 4 個字節的。再有需要區分 Season 為枚舉類型,sea 為枚舉變量
四、結構體內存對齊與補齊
一個存儲區的地址一定是它自身大小的整數倍(雙精度浮點類型的地址只需要4的整數倍就行了),這個規則也叫數據對齊,結構體內部的每個存儲區通常也需要遵守這個規則。數據對齊可能造成結構體內部存儲區之間有浪費的字節。結構體的大小一定是內部最大基本類型存儲區大小的整數倍,這個規則叫數據補齊。
這里考慮一個問題,為什么會有內存對齊?
字,雙字,和四字在自然邊界上不需要在內存中對齊。(對字,雙字,和四字來說,自然邊界分別是偶數地址,可以被 4 整除的地址,和可以被 8 整除的地址。)無論如何,為了提高程序的性能,數據結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內存,處理器需要作兩次內存訪問;然而,對齊的內存訪問僅需要一次訪問。
一個字或雙字操作數跨越了 4 字節邊界,或者一個四字操作數跨越了 8 字節邊界,被認為是未對齊的,從而需要兩次總線周期來訪問內存。一個字起始地址是奇數但卻沒有跨越字邊界被認為是對齊的,能夠在一個總線周期中被訪問。某些操作雙四字的指令需要內存操作數在自然邊界上對齊。如果操作數沒有對齊,這些指令將會產生一個通用保護異常。雙四字的自然邊界是能夠被 16 整除的地址。其他的操作雙四字的指令允許未對齊的訪問(不會產生通用保護異常),然而,需要額外的內存總線周期來訪問內存中未對齊的數據。缺省情況下,編譯器默認將結構、棧中的成員數據進行內存對齊。編譯器將未對齊的成員向后移,將每一個都成員對齊到自然邊界上,從而也導致了整個結構的尺寸變大。盡管會犧牲一點空間(成員之間有部分內存空閑),但提高了性能。
再有、如何避免內存對齊的影響?
寫結構體的時候按照從小到大的順序寫,既達到提高性能的目的,又能節約一點空間
#include <stdio.h> typedef struct {char ch; //1個字節char ch1;int num; //4個字節 }stru1;int main (void) {printf ("sizeof (stru1) = %d\n", sizeof (stru1));return 0; } 輸出結果: sizeof (stru1) = 8
參看:C語言的字節對齊及#pragma pack的使用
#pragma pack (整數n),表示按照整數n倍進行補齊和對齊
使用偽指令#pragma pack (n),編譯器將按照n 個字節對齊;
使用偽指令#pragma pack (),取消自定義字節對齊方式。
注意:如果#pragma pack (n)中指定的n 大于結構體中最大成員的size,則其不起作用,結構體仍然按照size 最大的成員進行對界。
總結
以上是生活随笔為你收集整理的C语言再学习 -- 结构和其他数据形式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hadoop配置机架感知
- 下一篇: HDFS机架感知概念及配置实现