C語言結構體、枚舉以及位域的講解

謹記

什么是價值?或許沒有多少人能夠明白,其實價值并不是實際存在的,它應該是一種體現,比如為城市點綴最美好的一面而起早摸黑的打掃的城市清潔工的大媽大爺;為中國航天事業而幾個月沒回家奮斗在一線的工程師們;為城市交通方便,上班不遲到的公交車司機;為自己人生而奮斗終生的你等等。太多了,價值是無處不在的,不管你在身在何處,做著什么事,你都體現了你的一份價值,或許你的價值在別人心中是那么的偉大,又或者是那么的微小,請你一定要記住,你的一舉一動一言一行都決定了你的價值。

引用

前面我們學習了關于C語言很多的知識,希望大家都掌握了,對于C語言來說,大部分高級語法其實和C語言是想通的,所以,學好C語言將來你要轉其他語言是很輕松的。那么,今天我們將學習C語言的結構體、枚舉以及簡單的介紹一下位域,希望讀者認真的理解結構體和枚舉。

結構體

1、結構體的定義

在實際的處理對象中,有許多信息是由多個不同類型的數據組合在一起進行描述,而且這些不同類型的數據是互相聯系組成了一個有機的整體。此時,就要用到一種新的構造類型數據——結構體(structure),簡稱結構。
結構體的作用:為了處理復雜的數據結構(如動態數據結構等)提供了有效的手段,而且,它們為函數間傳遞不同類型的數據提供了方便。
結構體和數組一樣,也是一種構造型數據類型,是用戶自定義的新數據類型,在結構體中可以包含若干個不同數據類型和不同意義的數據項(當然也可以相同),從而使這些數據項組合起來反映某一個信息。
例如,可以定義一個學生student結構體,在這個結構體中包括學生學號、姓名、性
別、年齡、家庭住址、聯系電話。這樣就可以用一個結構體數據類型的變量來存放某個學生的所有相關信息。并且,用戶自定義的數據類型student也可以與int、double等基本數據類型一樣,用來作為定義其他變量的數據類型。

定義一個結構體類型的一般形式為
struct  結構體名
{
   數據類型   成員名1;
   數據類型   成員名2;
   :
   數據類型   成員名n;
};
在花括號中的內容也稱為“成員列表”或“域表”。
其中,每個成員名的命名規則與變量名相同;
數據類型可以是基本變量類型和數組類型,或者是一個結構體類型;
用分號“;”作為結束符。整個結構的定義也用分號作為結束符。
struct student{   
    long number;
    char name[20];
    char gender;        
    int age;            //  age是成員名
    float salary;
    char address[80];
};                      //注意分號不能省略
int  age = 10;      //age是變量名 

結構體類型中的成員名可以與程序中的變量名相同,二者并不代表同一對象,編譯程序可以自動對它們進行區分。
由于結構體的成員的數據類型可以是任何類型,可能是基本變量類型、數組類型、結構體類型、聯合體類型或枚舉類型等。

總結

結構體類型的特點:
a.結構體類型是用戶自行構造的;
b.它由若干不同的基本數據類型的數據構成;
c.它屬于C語言的一種數據類型,與整型、浮點型相當。因此,定義它時不分配空間,只有用它定義變量時才分配空間。

2、結構體變量的聲明、初始化及運用

2.1 結構體變量的聲明
在定義了結構體類型后,就可以聲明結構體類型的變量。有下面幾種形式:
① 先定義結構體類型,再定義變量名
定義結構體變量的一般形式如下:

struct 結構體名{
類型 成員名;
類型 成員名;
……
};
struct 結構體名 結構體變量名;

這里的結構體名是結構體的標識符,不是變量名。類型可以是基本的數據類型也可以是其他構造型數據類型。

struct Person
    {
        char array[20];
        char *name;
        int age;
        double height;
    };
    struct Person p;

注意

struct Person代表的是類型名,不能分開和省略寫,比如“struct P”;這是錯誤的語法。

② 在定義類型的同時,定義變量
這種形式的定義的一般形式為

struct 結構體名
    {
類型 成員名;
類型 成員名;
……
    }變量名;
struct Person
    {
        char array[20];
        char *name;
        int age;
        double height;
    }per;

③ 直接定義結構體變量
如果省略結構體名,則稱之為無名結構體,這種情況常常出現在函數內部。
這種形式的定義的一般形式為

struct 
    {
類型 成員名;
類型 成員名;
……
    }變量名;
struct
    {
        char array[20];
        char *name;
        int age;
        double height;
    }per;

結構體所占內存空間大小

一個結構體變量占用內存的實際大小,可以利用sizeof求出。它的運算表達式為
sizeof(運算量)
其中運算量可以是變量、數組或結構體變量,可以是數據類型的名稱。這樣,就可以求出給定的運算量占用內存空間的字節數。
sizeof(struct person);
sizeof(per);

2.2、結構體變量的使用
結構體變量是不同數據類型的若干數據的集合體。在程序中使用結構體變量時,一般情況下不能把它作為一個整體參加數據處理,而參加各種運算和操作的是結構體變量的各個成員項數據。

結構體變量的成員用以下一般形式表示:
    結構體變量名.成員名

在定義了結構體變量后,就可以用不同的賦值方法對結構體變量的每個成員賦值。

per.age = 27;
per.name = "helloworld";

除此之外,還可以引用結構體變量成員的地址以及成員中的元素。例如:引用結構體變量成員的首地址&per.name;引用結構體變量成員的第二個字符per.name[1];引用結構體變量的首地址&per。

溫馨提示
結構體變量在使用中應注意以下幾點。
① 不能將一個結構體類型變量作為一個整體加以引用,而只能對結構體類型變量中的各個成員分別引用。

printf("%s",per);//這種寫法是錯的
printf("%s\n",per.name);//這是可以的

舉一個完整的示例:

#include <stdio.h>
#include <string.h>
int main(int argc, const char * argv[]) {
    struct person {
        char name[20];
        int  age;
        double height;
    }per;
    memset(&per, 0, sizeof(per));
    printf("name:");
    scanf("%s",per.name);
        printf("age:");
    scanf("%d",&per.age);
    printf("height:");
    scanf("%lf",&per.height);
    printf("%s\n  %d\n %lf\n",per.name,per.age,per.height);
    return 0;
}
輸出結果:
name:helloworld
age:12
height:178
helloworld
  12
 178.000000
Program ended with exit code: 0

② 如果成員本身又屬一個結構體類型,則要用若干個成員運算符,一級一級地找到最低的一級成員。只能對最低級的成員進行賦值或存取以及運算。

#include <stdio.h>
#include <string.h>
#define N 64
struct employee{
    char name[N];
    struct {
        int year;
        int month;
        int day;
    }birthday;
    char gender;
    char address[N];
    float salary;
};
int main(int argc, const char * argv[]) {
    struct employee e1;
    memset(&e1, 0, sizeof(e1));
    printf("name:");
    scanf("%s", e1.name);
    printf("year:");
    scanf("%d", &e1.birthday.year);
    printf("month:");
    scanf("%d", &e1.birthday.month);
    printf("day:");
    scanf("%d", &e1.birthday.day);
    getchar();
    printf("gender:");
    scanf("%c", &e1.gender);
    printf("address:");
    scanf("%s", e1.address);
    printf("salary:");
    scanf("%f", &e1.salary);
    printf("\ninformation:%s %d-%d-%d %c %s %f\n", e1.name, e1.birthday.year,
            e1.birthday.month, e1.birthday.day, e1.gender, e1.address, e1.salary);
    return 0;
}
輸出結果:
name:zhangsan
year:1989
month:12
day:11
gender:m
address:Suzhou
salary:3200.5

information:zhangsan 1989-12-11 m Suzhou 3200.500000
Program ended with exit code: 0

在該程序中,結構體的成員birthday,是一個結構體類型的變量。對于這樣的變量,可以這樣訪問各成員:

e1.birthday.year
e1.birthday.month
e1.birthday.day

注意:不能用e1.birthday來訪問e1變量中的成員birthday,因為birthday本身是一個結構體變量。
③ 對成員變量可以像普通變量一樣進行各種運算(根據其類型決定可以進行的運算)。例如:

e2.age=e1.age;
sum=e1.age+e2.age;
e1.age++;

④ 在數組中,數組是不能彼此賦值的,而結構體類型變量可以相互賦值。在C程序中,同一結構體類型的結構體變量之間允許相互賦值,而不同結構體類型的結構體變量之間不允許相互賦值,即使兩者包含有同樣的成員。

2.3、結構體變量初始化
與其他類型變量一樣,也可以給結構體的每個成員賦初值,這稱為結構體的初始化。一種是在定義結構體變量時進行初始化,語法格式如下:

struct  結構體名 變量名={初始數據表};

另一種是在定義結構體類型時進行結構體變量的初始化。

struct  結構體名
{
類型 成員名;
類型 成員名;
……
}變量名={初始數據表}; 

這里就不在舉例了,后面的例子會用。

結構體數組

具有相同結構體類型的結構體變量也可以組成數組,稱它們為結構體數組。結構體數組的每一個數組元素都是結構體類型的數據,它們都分別包括各個成員(分量)項。
定義結構體數組的方法和定義結構體變量的方法相仿,只需說明其為數組即可。可以采用以下方法。
① 先定義結構體類型,再用它定義結構體數組,定義形式如下:

struct 結構體名 
{
類型 成員名;
類型 成員名;
……

};
struct 結構體名 數組名[元素個數];
#define N 64
struct employee{
    char name[N];
    int age;
    char gender;
    char address[N];
    float salary;
};
struct employee e[10];
這種聲明格式是最常見的。

② 定義結構體類型的同時,定義結構體數組,定義形式如下:

struct 結構體名 
{
類型 成員名;
類型 成員名;
……

}數組名[元素個數];
#define N 64
struct employee{
    char name[N];
    int age;
    char gender;
    char address[N];
    float salary;
} e[10];
這種情況,定義結構體數組,不用重復寫結構體類型,很簡潔。一般,當需要聲明一個全局的結構體數組時,使用這種方式。

③ 直接定義結構體數組,定義形式如下:

struct  
{
類型 成員名;
類型 成員名;
……

}數組名[元素個數];
#define N 64
struct{
    char name[N];
    int age;
    char gender;
    char address[N];
    float salary;
} e[10];

結構體數組初始化
結構體數組在定義的同時也可以進行初始化,并且與結構體變量的初始化規定相同。
結構體數組初始化的一般形式是:

   struct 結構體名  
    {
    類型 成員名;
    類型 成員名;
    ……
    };
    struct 結構體名 數組名[元素個數]={初始數據表};
    或者
    struct 結構體名 
    {
    類型 成員名;
    類型 成員名;
    ……
    }數組名[元素個數]={初始數據表};
    或者
    struct
    {
    類型 成員名;
    類型 成員名;
    ……
    }數組名[元素個數]={初始數據表};

由于結構體變量是由若干不同類型的數據組成,而結構體數組又是由若干結構體變量組成。所以要特別注意包圍在大括號中的初始數據的順序,以及它們與各個成員項間的對應關系。
結構體數組的使用
一個結構體數組的元素相當于一個結構體變量,因此前面介紹的有關結構體變量的規則也適應于結構體數組元素。
(1)引用某一元素中的成員。
(2)可以將一個結構體數組元素值賦給同一結構體類型的數組中的另一個元素,或賦給同一類型的變量。
(3)不能把結構體數組元素作為一個整體直接進行輸入輸出。

#include <stdio.h>
#include <string.h>
#define N 64
struct employee{
    char name[N];
    struct {
        int year;
        int month;
        int day;
    }birthday;
    char gender;
    char address[N];
    float salary;
}e1[2] = { {"zhangsan", {1980, 9, 4}, 'w', "Shanghai", 3400}, 
           {"lisi", {1991, 10, 24}, 'w', "Hebei", 3400}};
int main(int argc, const char * argv[]) {
    struct employee e2[2] = {{"wangwu", {1986, 2, 24}, 'w', "Hubei", 6400}, 
                             {"Lucy", {1986, 8, 14}, 'w', "Henan", 3421}};
    int i;
    for (i = 0; i < sizeof(e1)/sizeof(struct employee); i++)
        printf("information :%s %d-%d-%d %c %s %f\n", e1[i].name, 
                e1[i].birthday.year, e1[i].birthday.month, e1[i].birthday.day, 
                e1[i].gender, e1[i].address, e1[i].salary);
    printf("\n");
    for (i = 0; i < sizeof(e2)/sizeof(struct employee); i++)
        printf("information :%s %d-%d-%d %c %s %f\n", e2[i].name, 
                e2[i].birthday.year, e2[i].birthday.month, e2[i].birthday.day, 
                e2[i].gender, e2[i].address, e2[i].salary);
    return 0;
}
在該程序中,聲明了兩個結構體數組,一個全局數組,一個局部數組。在聲明的同時,寫初始化列表初始化。

結構體指針

可以設定一個指針變量用來指向一個結構體變量。此時該指針變量的值是結構體變量的起始地址,該指針稱為結構體指針。
結構體指針與前面介紹的各種指針變量在特性和方法上是相同的。與前述相同,在程序中結構體指針也是通過訪問目標運算“*”訪問它的對象。結構體指針在程序中的一般定義形式為

struct 結構體名  *結構指針名;

其中的結構體名必須是已經定義過的結構體類型。
例如,對于上一節中定義的結構體類型struct employee,可以說明使用這種結構體類型的結構指針如下:

struct employee *p;

其中p是指向struct employee結構體類型的指針。結構體指針的說明規定了它的數據特性,并為結構體指針本身分配了一定的內存空間。但是指針的內容尚未確定,即它指向隨機的對象,需要為指針變量賦初值。

#include <stdio.h>
#include <string.h>
#define N 64
struct employee{
    char name[N];
    int age;
    char gender;
    char address[N];
    float salary;
};
void input(struct employee *p){
    printf("name:");
    scanf("%s", p->name);
    printf("age:");
    scanf("%d", &p->age);
    getchar();
    printf("gender:");
    scanf("%c", &p->gender);
    printf("address:");
    scanf("%s", p->address);
    printf("salary:");
    scanf("%f", &p->salary);
}
void output(struct employee *p){
    printf("information:%s %d %c %s %f\n", p->name, p->age,
            p->gender, p->address, p->salary);
}
int main(int argc, const char * argv[]) {
    struct employee e1;
    memset(&e1, 0, sizeof(e1));
    input(&e1);
    output(&e1);
    return 0;
}
輸出結果:
name:zhao
age:21
gender:m
address:Shanghai
salary:3421.1
information:zhao 21 m Shanghai 3421.100098
Program ended with exit code: 0

枚舉

在C語言中還有一種構造類型,即枚舉。在實際問題中,有些變量只有幾種可能的取值。例如:一周有七天,程序中的錯誤只有那么幾種等。針對這樣特殊的變量,C語言中提供了“枚舉”類型,在枚舉的定義中,會將變量的值一一列出來。當然,枚舉類型的變量的值也就只限于列舉出來的值的范圍內。
枚舉類型的定義
枚舉類型的定義形式如下:
enum 枚舉名{ 枚舉成員列表 };
在枚舉成員列表中列出所有可能的取值,以分號結尾。注意和結構體、聯合體類似,“enum 枚舉名”是新定義的類型名。
舉例如下:

enum TimeofDay 
{ 
morning, 
afternoon, 
evening
};

該枚舉名為TimeofDay,共有3種可能的取值。在定義該枚舉類型的變量時,也只能取其中的一個值,進行賦值。
枚舉變量的聲明
其實枚舉變量的聲明和我們上面說的結構體變量的聲明是一樣的。
方式一:先定義類型,再聲明變量

enum TimeofDay 
{ 
morning, 
afternoon, 
evening 
};
enum TimeofDay a, b;

方式二:在定義類型的同時,聲明變量

enum TimeofDay 
{ 
morning, 
afternoon, 
evening 
} a, b;

方式三:直接定義無名枚舉變量

   enum 
    { 
    morning, 
    afternoon, 
    evening 
    } a, b;

枚舉變量的使用
通過一個示例來說吧

#include <stdio.h>
enum TimeofDay{
    morning = 2,
    afternoon,
    evening
}a;
int main(int argc, const char * argv[]) {
    enum TimeofDay b, c;
    a = morning;
    b = afternoon;
    c = evening;
    printf("a=%d b=%d c=%d\n", a, b, c);
    c = 10;
    printf("a=%d b=%d c=%d\n", a, b, c);
    return 0;
}
運行結果:
a=2 b=3 c=4
a=2 b=3 c=10
Program ended with exit code: 0

通過該程序,可以看出,可以把枚舉成員的值賦予枚舉變量,也可以把整數值直接賦予枚舉變量。實際上,枚舉變量,也是一種整型變量,因此,也可以使用switch-case結構,示例程序如下:

#include <stdio.h>
enum TimeofDay{
    morning,
    afternoon,
    evening
};
int main(int argc, const char * argv[]) {
    int i, j;
    enum TimeofDay a[10];
    j = morning;
    for (i = 0; i < 10; i++)
    {
        a[i] = j;
        j++;
        if (j > evening)
            j = morning;
    }
    for (i = 0; i < 10; i++)
    {
        switch (a[i])
        {
            case morning: printf("%d morning\n", a[i]); break;
            case afternoon: printf("%d afternoon\n", a[i]); break;
            case evening: printf("%d evening\n", a[i]); break;
            default:break;
        }
    }
    return 0;
}
輸出結果:
0 morning
1 afternoon
2 evening
0 morning
1 afternoon
2 evening
0 morning
1 afternoon
2 evening
0 morning
Program ended with exit code: 0

位域的介紹

在這里,對于位域,只是一個簡單的介紹,這個用的不是很多,如果真需要用,建議讀者去官網看教程。
位域的定義
所謂“位域”是把一個字節中的二進位劃分為幾個不同的區域,并說明每個區域的位數。每個域有一個域名,允許在程序中按域名進行操作。這樣就可以把幾個不同的對象用一個字節的二進制位域來表示。
位域的定義與結構體的定義相似,其一般形式如下:

struct 位域結構名
{   
  位域列表 
};

其中位域列表的形式為

類型說明符 位域名:位域長度

例如:

 struct data{
    unsigned int a:2;
    unsigned int b:3;
    unsigned int c:3;
};
其中a表示data的低兩位,表示data的3~5位,c表示data的6~8位。

關于位域的定義,有一些問題需要注意。
① 個位域必須存儲在同一個字節中,不能跨兩個字節。
② 位域的占用的位數,不能超過8個二進制位。
③ 允許位域無域名。
位域的使用
位域的使用和結構成員的使用相同,其一般形式為
位域變量名.位域名
示例代碼

#include <stdio.h>
struct data{
    unsigned int a: 2;
    unsigned int b: 4;
    unsigned int: 0;
    unsigned int c: 3;
}t;
int main(int argc, const char * argv[]) {
    struct data *p;
    t.a = 3;
    t.b = 5;
    t.c = 6;
    printf("t.a=%d t.b=%d t.c=%d\n",t.a, t.b, t.c);
    p = &t;
    p->a = 2;
    p->b &= 0;
    p->c |= 1;
    printf("t.a=%d t.b=%d t.c=%d\n",t.a, t.b, t.c);
    return 0;
}
輸出結果:
t.a=3 t.b=5 t.c=6
t.a=2 t.b=0 t.c=7
Program ended with exit code: 0
說明:由于位域中的各個域是以二進制位為單位,因此,大部分的位域程序,都有位運算

總結

本篇文章介紹了C語言的結構體、枚舉、位域,以及結構體和數組、指針的聯系,內容比較多,希望讀者認真的理解和體會,那么,在這里,C語言的基礎知識點我們都已經學的差不多了。希望能給讀者帶來幫助。

結尾

希望讀者真誠的對待每一件事情,每天都能學到新的知識點,要記住,認識短暫,開心也是過一天,不開心也是一天,無所事事也是一天,小小收獲也是一天,歡迎收藏和點贊、喜歡。最后送讀者一句話:你的路你自己選擇。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容