1. 程式人生 > >結構體的相關知識

結構體的相關知識

主題1:結構體出現的緣由

對於一個學生來說,有姓名、學號、班級、年齡、性別、手機號等資訊。那麼,如何來儲存這些資訊呢?
要儲存一個人的姓名,我們知道可以定義一個字元陣列,把該姓名存入字元陣列,然後就可以對該姓名進行查詢、修改等操作。
如果要儲存咱們班49個同學的姓名,我們知道可以定義一個二維字元陣列或者一個字元指標陣列來解決該問題。
那麼,如果要儲存一個人的年齡,我們知道可以定義一個整型變數,把該年齡存入該整型變數,然後就可以對該年齡進行你所需要的一切操作了。
如果要儲存咱們班49個同學的年齡,我們知道可以定義一個二維整型陣列來解決該問題。
。。。。。。。。。。。。。
如此這般,每個同學的姓名、學號、班級、年齡、性別、手機號等資訊零散地存在於多個地方。既然這些資訊歸屬於一個同學,它們就是一個整體,能不能把它們放在一起儲存呢?進而執行操作的時候也方便哦。答案是肯定的,結構體型別就可以來解決該問題。

主題2:使用者如何自定義一個結構體型別?

那麼,什麼是結構體?我的理解是結構體是人們自創的一種資料型別,根據業務處理的需要而自定義的一種資料型別,通常也稱之為構造資料型別。切記,它也是一種資料型別。下來,我們來揭開它的面紗吧。

struct Student
{
    char name[12];
    char XH[12];
    int age;
};

這就是一個結構體的定義,定義了一個結構體型別,struct是結構體型別的關鍵字,Student是結構體型別的名稱(該名稱為任意合法的識別符號,但不能與關鍵字同名),{}裡面的內容稱為結構體的成員,結構體的定義以;結束。綜上所述,我們定義了一個Student的結構體型別。

主題3:如何使用結構體型別?

眾所周知,有了int型別,我們就可以定義int型別的變數、int型別的陣列、int型別的指標變數等等。同理,有了結構體型別,我們當然可以定義結構體型別的變數、結構體型別的陣列、結構體型別的指標變數等等。

struct Student{
    char name[12];
    char XH[12];
    int age;
};

struct Student a,b[49],*t;  該語句定義了一個struct Student 型別的變數a,struct Student型別的陣列b,struct Student型別指標變數t。

那麼,定義完之後,如何對它們進行操作呢?

3.1結構體型別的變數

先來看一下如何對結構體型別的變數進行輸入輸出操作吧。

#include<stdio.h>
struct Student{
    char name[12];
    char XH[12];
    int age;
};
int main()
{
    struct Student a;
    gets(a.name);
    gets(a.XH);
    scanf("%d",&a.age);
    printf("output a\n");
    puts(a.name);
    puts(a.XH);
    printf("%d\n",a.age);
 return 0;
}

上述程式對struct Student型別的變數a進行了輸入輸出操作,需要告訴編譯器給a的哪個成員進行輸入輸出操作,因而需要a.成員名的方式來對各個成員進行引用。
那麼,如何對結構體型別的陣列進行操作呢?

3.2結構體型別的陣列

一提到陣列,就離不開迴圈(除表示字串的字元陣列外,它有專屬的字串處理函式,得天獨厚)。同理,對結構體型別的陣列進行操作,也需要用到迴圈結構。

#include<stdio.h>
struct Student{
    char name[12];
    char XH[12];
    int age;
};
int main()
{
    struct Student b[3];
    int i;
    for(i=0;i<3;i++)
    {
        gets(b[i].name);
        gets(b[i].XH);
        scanf("%d",&b[i].age);
    }
    printf("output array b:\n");
    for(i=0;i<3;i++)
    {
        puts(b[i].name);
        puts(b[i].XH);
        printf("%d\n",b[i].age);
    }
    return 0;
}

密切注意:在輸入的時候,因為涉及到多種型別的成員,尤其是字串,往往很容易出現錯誤,當i=0時,我們輸完第1個同學的年齡之後,如果敲了回車,這個回車符會作為下一個同學的姓名,因為回車符也是字元或字串。所以在輸入年齡之後,不用回車直接輸入下一個同學的姓名即可。具體如下圖所示。
在這裡插入圖片描述

但是,不讓敲回車,大多數同學會很不習慣,因而可以在年齡輸入語句的後面加上ch=getchar();或者getchar();用getchar來吃掉這個容易引起誤解的回車符吧。

for(i=0;i<3;i++)
    {
        gets(b[i].name);
        gets(b[i].XH);
        scanf("%d",&b[i].age);
        ch=getchar(); //也可以只有getchar();
}

其實,還有另一種處理方法也較常見,就是把int型別的變數也當作字串,存放在char陣列中。具體如下:

#include<stdio.h>
#include<string.h>
struct Student{
    char name[12];
    char XH[12];
    char age[4];
};
int main()
{
    struct Student a,b[49];
    int i;
    for(i=0;i<3;i++)
    {
        gets(b[i].name);
        gets(b[i].XH);
        gets(b[i].age);
    }
    printf("output array b:\n");
    for(i=0;i<3;i++)
    {
        puts(b[i].name);
        puts(b[i].XH);
        printf("%d\n",atoi(b[i].age));
    }
    return 0;
}

把age也作為一個字串來處理,因而用gets來輸入,就可以自然地敲回車了,然後在對年齡進行操作時,採用函式atoi將該字串轉換為int即可。atoi是在標頭檔案string.h中定義,因而需要引入該標頭檔案

3.3 結構體型別的指標變數

我們不要把結構體型別相像的很神祕,它和其它型別一樣,只是一種資料型別而已,只不過我們在操作時需要指明它的成員。
如同int* p; p指向的空間肯定儲存的是一個int型的資料,因而結構體型別的指標變數指向的空間肯定是一個相同結構體型別的資料。如果把一個結構體型別的變數地址賦值給相同結構體型別的指標變數,則該指標變數指向該結構體變數的空間,繼而可以通過該指標變數對其空間中的各個成員進行操作。

#include<stdio.h>
#include<string.h>
struct Student{
    char name[12];
    char XH[12];
    char age[4];
};
int main()
{
    struct Student a,*p;
    int i;
    p=&a;
    gets((*p).name);
    gets(p->XH);
    gets(p->age);
    printf("output a:\n");
    puts(p->name);
    puts(p->XH);
    printf("%d\n",atoi(p->age));
    return 0;
}

語句:p=&a;也就意味著p指向了a的空間,然後可以通過p對a中的成員進行操作,指標對成員的引用有兩個形式:(*p).age或者p->age,個人比較喜歡後者,因為後者更有指標的感覺!嘻嘻。
那麼,結構體型別的指標如何來操作結構體型別的陣列呢?顯然,如果是一維陣列的話,把陣列名賦值給它就行了。然後採用指標操作一維陣列的方法就可以了(共三種方法,你還記得嗎?指標變數法,首地址+偏移量,下標法)。這裡我們只講指標變數法(不斷改變指標的指向)。

#include<stdio.h>
#include<string.h>
struct Student{
    char name[12];
    char XH[12];
    char age[4];
};
int main()
{
    struct Student b[3],*p;
    int i;
    for(p=b;p<b+3;p++)
    {
        gets(p->name);
        gets(p->XH);
        gets(p->age);
    }
    printf("output array b:\n");
    for(p=b;p<b+3;p++)
    {
        puts(p->name);
        puts(p->XH);
        printf("%d\n",atoi(p->age));
    }
    return 0;
}

主題4:結構體定義的進一步探討

如果一個公司的人事部們需要我們做一個系統,所處理的資訊包括;員工編號、員工姓名、所屬部門、出生年月日。
我們可以定義結構體型別如下:

struct PeopleInfo{
    char number[12];
    char name[12];
    char dept[12];
    int year;
    int month;
    int day;
};

其實,還有另一種思路:

struct bir
{
    int year;
    int month;
    int day;
};
struct PeopleInfo{
    char number[12];
    char name[12];
    char dept[12];
    struct bir t;
};

在這種思路中,結構體型別struct PeopleInfo中的一個數據成員t,其型別為struct bir。那麼,如何對t中的資料進行引用呢?

#include<string.h>
struct bir
{
    int year;
    int month;
    int day;
};
struct PeopleInfo{
    char number[12];
    char name[12];
    char dept[12];
    struct bir t;
};
int main()
{
    struct PeopleInfo s;
    gets(s.number);
    gets(s.name);
    gets(s.dept);
    scanf("%d%d%d",&s.t.year,&s.t.month,&s.t.day);
    printf("info output:\n");
    puts(s.number);
    puts(s.name);
    puts(s.dept);
    printf("%d-%d-%d",s.t.year,s.t.month,s.t.day);
    return 0;
}

根據前面的經驗我們知道,當一個結構體型別中有其它型別和字元型別時,輸入時常常會因為回車符而出現狀況,只有一個結構體變數時一切正常,當對陣列操作時,會出現錯誤情況,因而可以把其它型別定義為字元型。操作時再轉換為你想要的型別。

#include<stdio.h>
#include<string.h>
struct bir
{
    char year[5];
    char month[3];
    char day[3];
};
struct PeopleInfo{
    char number[12];
    char name[12];
    char dept[12];
    struct bir t;
};
int main()
{
    struct PeopleInfo s[2];
    int i;
    for(i=0;i<2;i++)
    {
        gets(s[i].number);
        gets(s[i].name);
        gets(s[i].dept);
        gets(s[i].t.year);
        gets(s[i].t.month);
        gets(s[i].t.day);
    }
    printf("info output:\n");
    for(i=0;i<2;i++)
    {
        puts(s[i].number);
        puts(s[i].name);
        puts(s[i].dept);
        printf("%d-%d-%d\n",atoi(s[i].t.year),atoi(s[i].t.month),atoi(s[i].t.day));
    }
    return 0;
}

其實,當用戶要求輸出形式為:2009-07-08,將它們均定義為char型是非常合適的,輸出時直接採用:

printf("%s-%s-%s\n",s[i].t.year,s[i].t.month,s[i].t.day);

有同學可能會問:採用atoi可以將字串轉換為整型,那如果資料成員為float或double呢?例如,如果我們要編寫一個學生成績管理系統呢,假設每個學生有三門課,該如何進行處理呢?
(1)採用getchar函式

#include<stdio.h>
#include<string.h>
struct StuScore{
    char xh[12];
    char name[12];
    float score[3];
};
int main()
{
    struct StuScore s[2];
    int i,j;
    for(i=0;i<2;i++)
    {
        gets(s[i].xh);
        gets(s[i].name);
        for(j=0;j<3;j++)
            scanf("%f",&s[i].score[j]);
        getchar();
    }
    printf("info output:\n");
    for(i=0;i<2;i++)
    {
        puts(s[i].xh);
        puts(s[i].name);
        for(j=0;j<3;j++)
            printf("%f\n",s[i].score[j]);
    }
    return 0;
}

採用getchar函式的目的,就是在輸入字串之前接收那個回車符,覺得也挺不錯的。但是,根據應用的不同,有時候會出現n個getchar,瞅著都眼暈。利用,使用者還需要輸入年齡資訊,為此,在結構體定義中需要引入該成員。

#include<stdio.h>
#include<string.h>
struct StuScore{
    char xh[12];
    int age;
    char name[12];
    float score[3];
};
int main()
{
    struct StuScore s[2];
    int i,j;
    for(i=0;i<2;i++)
    {
        gets(s[i].xh);
        scanf("%d",&s[i].age);
        getchar();
        gets(s[i].name);
        for(j=0;j<3;j++)
            scanf("%f",&s[i].score[j]);
        getchar();
    }
    printf("info output:\n");
    for(i=0;i<2;i++)
    {
        puts(s[i].xh);
        printf("%d \t",s[i].age);
        puts(s[i].name);
        for(j=0;j<3;j++)
            printf("%f\n",s[i].score[j]);
    }
    return 0;
}

在這個結構體型別的定義中,char之間有了int型別,那麼在輸入age時敲下的回車符也需要有個getchar來接收。
有的系統還要考慮實際使用者的輸入習慣,也即成員之間的順序,可能會產生其它型別位於char之間,程式設計師就需要在其它型別的輸入之後都添上getchar函式,才能保證資料的正確輸入。