博客 / 詳情

返回

結構體筆記(結構體嵌套、自引用,結構體指針)

結構體筆記(結構體嵌套、自引用,結構體指針)

結構體(struct)

1、基本概念

  結構體-----將不同類型的數據成員組織到統一的名字之下,適用於對關係緊密,邏輯相關、具有相同或不同類型的數據進行處理

2、結構體定義格式

定義結構

  為了定義結構,您必須使用 struct 語句。struct 語句定義了一個包含多個成員的新的數據類型,struct 語句的格式如下:

struct 標籤名

{

類型 變量名;

類型 變量名;

······

} 結構變量 (結構體名字);

struct tag
{

member_list  
member_list  
member_list

​ ...

​ } variable_list ;

  tag 是結構體標籤。

  member-list 是標準的變量定義,比如 int i; 或者 float f,或者其他有效的變量定義。

  variable-list 結構變量,定義在結構的末尾,最後一個分號之前,您可以指定一個或多個結構變量。下面是聲明student結構的方式:

struct student
{
    char  name[50]; //名字
    int   id;      //學號
} student1;

  (聲明結構體類型僅僅是聲明瞭一個類型,系統並不為之分配內存,就如同系統不會為類型 int 分配內存一樣。只有當使用這個類型定義了變量時,系統才會為變量分配內存。所以在聲明結構體類型的時候,不可以對裏面的變量進行初始化。)

  定義了一個tag為student的結構體和一個結構變量student1,如果省略變量名(student1),就變成了對結構的聲明,上述結構體聲明也可分開寫

struct student
{
    char  name[50]; //名字
    int   id;      //學號
} ;
struct student student1;

  與上面效果相同,可理解為struct student類似於int,而我們用的是student1類似於變量,如果省略結構名,則稱之為無名結構,這種情況常常出現在函數內部,或者説你只需要student1這一個變量,

  後面不需要再使用結構體名定義其他變量,那麼在定義時也可以不給出結構體名

struct 
{
    char  name[50]; //名字
    int   id;      //學號
} student1;

  (在聲明結構體時常常與typedef函數配合使用)

  2.

3、結構體變量的初始化

  和其它類型變量一樣,對結構體變量可以在定義時指定初始值。

  例:

struct student
{
    char  name[50];
    int   id;
} charon = {"charon", 666};
int main()
{
    printf ( "name : %s\nid: %d\n", charon.name, charon.id );

  輸出:

4、結構體成員的訪問

  訪問結構體變量的成員必須使用成員選擇運算符(也稱圓點運算符),格式為:結構體變量名.成員名

  若使用指針對結構體成員進行訪問,格式為:指針->成員名 等價於 (*指針).成員名 ​

  相同點:兩個操作符都是二元操作符,且其有操作符是結構體成員的名稱。

  不同點:“ . ”操作符左邊的操作數是一個“結構體”的表達式,而“ -> ”操作符左邊的操作數是一個指向結構體的指針。

  例:

typedef struct
{
    int num;
    float score;
    char name[10];
} STUDENT;
STUDENT temp;
STUDENT * p = &temp;

  在這裏temp.score代表的是結構體temp裏的成員score。

  p->score代表指向temp結構體成員score的指針。

  為了使用方便和直觀,C語言允許把(*temp).score用p->score來替換。

  也就是p->score等價與(*temp).score。

  所以在結構體中“ . ”和“ -> ”的用法相似,但是並不等價。

5、typedef函數

  為一種數據類型定義一個新名字。這裏的數據類型包括內部數據類型(int,char等)和自定義的數據類型(struct等),

  (注意與#define的區別,typedef 是用來定義一種類型的新別名的,它不同於宏#define,宏是簡單的字符串替換)

  例:

  為int定義了一個新的名字INTEGER,也就是説INTEGER與int是同義詞,也可以為結構體定義一個別名

typedef struct student STUDENT;

  或者

typedef struct student
{
  int num;
} STUDENT;

  上述兩條語句是等價的,二者都是為struct student結構體類型定義了一個新的名字STUDENT,即STUDENT與struct student是同義詞,所以下列兩條語句等價

STUDENT stu1,stu2;
struct student stu1, stu2;

6、結構體嵌套

  就是在一個結構體內包含了另一個結構體作為其成員

相互引用

使用typedef
typedef struct date
{
    int year;
    int month;
    int day;
} DATE;
typedef struct student
{
    long studentID;
    char studentName[10];
    char studentSex;
    DATE birthday;
    int score[4];
} STUDENT;
STUDENT charon;

  上面代碼中,定義了結構體變量birthday和charon,並給struct date和struct student分別取別名為DATE和STUDENT,

  當出現結構體嵌套時,必須以級聯方式訪問結構體成員,即通過成員選擇運算符逐級找到最底層的成員時再引用

charon.birthday.day = 10;
printf ( "%d", charon.birthday.day );
不使用typedef

  上面是使用了typedef的結構體嵌套,也可以不使用,代碼如下

struct STUDENT
{
    long studentID;
    char studentName[10];
    char studentSex;
    int score[4];
    struct DATE
    {
        int year;
        int month;
        int day;
    } birthday;
} student;

  定義了結構體變量student和birthday,引用成員的的方法和上面類似

student.birthday.day = 100;
printf("%d", student.birthday.day);

  C語言允許對具有相同結構體類型的變量進行整體賦值,注意:對字符數組型結構體成員進行賦值時一定要使用strcpy()

strcpy(stu1.studentName, “張三”);

  而不能寫成

stu2.studentName = stu1.studentName

  因為結構體成員studentName是一個字符型數組,studentName是該數組的名字,代表字符型數組的首地址,是一個常量,不能作為賦值表達式的左值。

自引用(self reference)(重點)

  使用typedef 時

  錯誤的方式:

typedef struct tag{ 
  int value; 
  charon *link; /* 雖然也使用指針,但這裏的問題是:charon尚未被定義 */ 
} charon; 

  這裏的目的是使用typedef為結構體創建一個別名charon。但是這裏是錯誤的,因為類型名的作用域是從語句的結尾開始,而在結構體內部是不能使用的,因為還沒定義。
正確的方式:有三種,差別不大,使用哪種都可以。

  當你使用typedef去代替整個結構體並且以原結構體名代替的時候,如果此時你內部還自嵌套了,那麼內部的成員變量之前一定要加上struct。

/* 方法一 */ 
typedef struct tag_1{ 
  int value; 
  struct tag_1 *link;  
} charon; 
 
 
/* 方法二 */ 
struct tag_2;  //結構體的不完全聲明
typedef struct tag_2 charon; 
struct tag_2{ 
  int value; 
  charon *link;   
}; 
 
 
/* 方法三 */ 
struct tag_3{ 
  int value; 
  struct tag *link;  
}; 
typedef struct tag_3 charon; 

  不使用typedef時

  錯誤的方式:

struct tag_1
{
    struct tag_1 A;  /* 結構體 */
    int value;
};

   這種聲明是錯誤的,因為這種聲明實際上是一個無限循環,成員b是一個結構體,b的內部還會有成員是結構體,依次下去,無線循環。在分配內存的時候,由於無限嵌套,也無法確定這個結構體的長度,所以這種方式是非法的。

  正確的方式: (使用指針):

struct tag_1{ 
  struct tag_1 *A; /* 指針 */ 
  int value; 
}; 

  由於指針的長度是確定的(在64位機器上指針長度為8),所以編譯器能夠確定該結構體的長度。

  可以用sizeof查看:

​如何理解結構中包含一個指向結構本身的指針?

  至於指向自己的問題,結構體只是個數據類型,不是一個變量。

  只有在聲明一個結構體變量的時候才會在內存中分配一段空間,指針中存放的正是變量在內存中的地址。所以不是指針指向了結構體自己,而是指針指向了結構體變量。

  C語言中的類型有完整和不完整之分,只有完整類型的變量才能夠使用。完整代表已確定了類型的大小,底層只需要大小屬性,而不需要類型屬性。

  指針類型都是完整的,因為指針類型的大小都是確定的。struct tag * 是指針類型,而不是結構類型。結構的成員類型都是完整的,那麼結構類型就是完整的。

  程序員可以使用強制類型轉換來將一段內存轉換為需要的數據類型,例如下面有一個數組a,現在將其強制轉換為一個結構體類型stu:

#include <stdio.h>
typedef struct student
{
    int   id;
    char  name[50];
} stu;

int a[10]={1,2,3,4,5};

int main()
{
    stu *student1;
    student1 =(student*)a;
    printf("student1->name=%d\n",student1->name);
    printf("student1->id=%d\n",student1->id);
    return 0;
}

  

  可以看到a[10]被強制轉換為student結構體類型,當然不使用強制類型轉換也是可以的,只是編譯器會報警報。

  “一個結構體裏包含一個指針,這個指針指向的數據類型是這個結構體的數據類型”。

  例子:

typedef struct Node
{
    int data;
    Node* next;
} node;
int main(){
    node node1;
    node node2={666};
    node node3;
    node1.next = &node2;
    node2.next = &node3;
    node3.next = NULL;
    cout<<&node2<<endl;
    cout<<node1.next<<endl;
    cout<<node2.data<<endl;
    cout<<node1.next->data<<endl;
    return 0;
}

  

  ​ima![ge](![assets/image-20221125140606-4tpsamp.png]

參考鏈接

結構體(結構體嵌套、結構體指針、結構體參數傳遞) - 藍海人 - 博客園 (cnblogs.com)

如何理解結構中包含一個指向結構本身的指針? - 知乎 (zhihu.com)

詳解C語言中結構體的自引用和相互引用_C 語言_腳本之家 (jb51.net)

C 結構體 | 菜鳥教程 (runoob.com)

​  <img alt="知識共享許可協議" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" />
本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。

  <img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" />
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

  使用者可以對本創作進行轉載、節選、混編、二次創作,但不得運用於商業目的,且使用時須進行署名,採用本創作的內容必須同樣採用本協議進行授權。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.