天天看點

解析C語言結構體,位段

解析C語言結構體,位段

結構體是什麼呢?結構體(struct)是由一系列具有相同類型或不同類型的資料構成的資料集合,也叫結構。結構體和其他類型基礎資料類型一樣,例如int類型,char類型隻不過結構體可以做成你想要的資料類型。以友善日後的使用。在實際項目中,結構體是大量存在的。研發人員常使用結構體來封裝一些屬性來組成新的類型。由于C語言無法操作資料庫,是以在項目中通過對結構體内部變量的操作将大量的資料存儲在記憶體中,以完成對資料的存儲和操作。

一、首先來看一下它的定義及初始化,舉個簡單的例子。

1.定義和聲明分開

#include<iostream>
#include<stdio.h>
struct student            
{
  int age;              //年齡
  float score;          //成績
  char sex;              //性别
};
int main()
{
  struct student a = { 20, 79, 'f' };  //定義
  printf("年齡:%d 分數:%.2f 性别:%c\n", a.age, a.score, a.sex);
  system("pause");
  return 0;
}      
解析C語言結構體,位段

2.聲明時直接定義

#include<iostream>
#include<stdio.h>
struct student            
{
  int age;              //年齡
  float score;          //成績
  char sex;              //性别
}a = { 20, 79, 'f' };
int main()
{
  //struct student a = { 20, 79, 'f' };  //定義
  printf("年齡:%d 分數:%.2f 性别:%c\n", a.age, a.score, a.sex);
  system("pause");
  return 0;
}      

建議使用第一種方式。

二、結構體的typedef

typedef為​​C語言​​的關鍵字,作用是為一種資料類型定義一個新名字。這裡的資料類型包括内部資料類型(int,char等)和自定義的資料類型(struct等)。

下面來看一個例子:

typedef struct tagstudent            
{
  int age;              //年齡
  float score;          //成績
  char sex;              //性别
}student;      

實際上這些語句完成了兩個操作:定義了一個新的結構類型;typedef為這個新的結構起了一個名字,叫student,是以實際上這裡struct tagstudent相當于student,我們可以使用student來定義變量。

int main()
{
  student a = { 20, 79, 'f' };  //定義
  printf("年齡:%d 分數:%.2f 性别:%c\n", a.age, a.score, a.sex);
  system("pause");
  return 0;
}      

運作結果和第一問中是一樣的。

解析C語言結構體,位段

三、.結構體的記憶體存儲

我們必須要知道,一個結構體變量定義完之後,其在記憶體中的存儲并不等于其所包含元素的寬度之和,還是上面那個例子,我們一起分析。

#include<iostream>
#include<stdio.h>
struct student            
{
  int age;              //年齡
  float score;          //成績
  char sex;              //性别
}student1;
int main()
{
  printf("%d ", sizeof(student1));
  printf("%d ", sizeof(student1.age));
  printf("%d ", sizeof(student1.score));
  printf("%d ", sizeof(student1.sex));
  system("pause");
  return 0;
}      
解析C語言結構體,位段

可以發現并不是所有元素的和,這就存在記憶體對齊問題。首先讨論一下記憶體的對齊規則:

規則一:第一個成員在與結構變量偏移量為0的位址處。

規則二:其他成員變量要對齊到某個數字(對齊數)的整數倍的位址處。

              對齊數=編譯器預設的一個對齊數與該成員大小的較小值。

規則三:結構體總大小為最大對齊數(每個成員變量除了第一個成員都有一個對齊數)的整數倍。

規則四:如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍。

有人肯定想問,為什麼存在記憶體對齊呢?原因如下:

1、平台原因(移植原因):不是所有的硬體平台都能通路任意位址上的任意資料的;某些硬體平台隻能在某些位址處取某些特定類型的資料,否則抛出硬體異常。

2、性能原因:資料結構(尤其是棧)應該盡可能地在自然邊界上對齊,原因在于,為了通路未對齊的記憶體,處理器需要做兩次記憶體通路;而對齊的記憶體通路僅需要一次通路。

接下來讨論如何計算,以上為例:(注:offsetof函數為計算結構體内成員在結構體中的偏移)

#include<iostream>
#include<stdio.h>
struct student            
{
  int age;              //年齡
  float score;          //成績
  char sex;              //性别
}student1;
int main()
{
  printf("%d ", offsetof(struct student,age));
  printf("%d ", offsetof(struct student,score));
  printf("%d ", offsetof(struct student, sex));
  system("pause");
  return 0;
}      
解析C語言結構體,位段

我們一起來根據以上規則分析這樣偏移的原因,如圖所示,12便由此可得。

解析C語言結構體,位段

(預設對齊數 vs:8,​​Linux​​:4)

四、結構實作位段,位段大小的計算及位段的資料存儲

位段是在字段的聲明後面加一個冒号以及一個表示字段位長的整數來實作的。位段的聲明和結構類似,但位段的成員是一個或多個位的字段。位段成員必須聲明位int,unsigned int或signed int類型。這種用法又被就叫作“深入邏輯元件的程式設計”。

例子如下:

struct S
{
  char a : 2;
  char b : 4;
  char c : 6;
  char d : 7;
};
int main()
{
  printf("%d\n", sizeof(struct S));
  system("pause");
  return 0;
}      

這個簡單的結構占幾個位元組呢,運作結果如下:

解析C語言結構體,位段