C語言中結構體在函式中的應用

2021-06-19 15:07:37 字數 3955 閱讀 4829

(2012-05-09 16:39:47) 前

天在編寫一段**時突然對結構體在函式中的用法有些模糊了,經過複習,基本弄清了這些知識,特總結如下:

一、結構體與函式引數

結構体作函式引數可分為傳值與傳指標。

1.傳值時結構體引數會被拷貝乙份,在函式體內修改結構體引數成員的值實際上是修改呼叫引數的乙個臨時拷貝的成員的值,這不會影響到呼叫引數。在這種情況下,由於涉及到結構體引數的拷貝,程式空間及時間效率都會受到影響,所以這種方法基本不用。

例如:typedef struct tagstudentstudent;

void fun(student stu)

2.傳指標時直接將結構體的首位址傳遞給函式體,在函式體中通過指標引用結構體成員,可以對結構體引數成員的值造成實際影響。這種用法效率高,經常採用。

例如:typedef struct tagstudentstudent;

void fun(student* pstu)

二、結構體與函式返回值

對於某些版本的c語言編譯器,返回值僅能為基本資料型別如int、char以及指標,因此結構體作為一種組合資料型別,不能以值的方式返回,而在有些版本的c編譯器中又可以直接返回結構體變數,在c++中也是可以直接返回結構體變數的。

直接返回結構體變數示例如下;

typedef struct tagstudentstudent;

student fun();

int _tmain(int argc, _tchar* argv)

student fun()

以指標方式返回結構體示例如下:

typedef struct tagstudentstudent;

student* fun()

關於結構體,看核心又遇到了,關於賦值中存在·的奇怪用法,在網上沒有找到答案,卻把以前一直弄的比較模糊的對齊問題給翻出來了。如下為**內容:

有人給對齊原則做過總結,具體在**看到現在已記不起來,這裡引用一下前人的經驗(在沒有#pragma pack巨集的情況下):

原則1、資料成員對齊規則:結構(struct或聯合union)的資料成員,第乙個資料成員放在offset為0的地方,以後每個資料成員儲存的起始位置要從該成員大小的整數倍開始(比如int在32位機為4位元組,則要從4的整數倍位址開始儲存)。

原則2、結構體作為成員:如果乙個結構裡有某些結構體成員,則結構體成員要從其內部最大元素大小的整數倍位址開始儲存。(struct a裡存有struct b,b裡有char,int,double等元素,那b應該從8的整數倍開始儲存。)

原則3、收尾工作:結構體的總大小,也就是sizeof的結果,必須是其內部最大成員的整數倍,不足的要補齊。

這三個原則具體怎樣理解呢?我們看下面幾個例子,通過例項來加深理解。

例1:structa;

structb;

sizeof(a) = 6; 這個很好理解,三個short都為2。

sizeof(b) = 8; 這個比是不是比預想的大2個位元組?long為4,short為2,整個為8,因為原則3。

例2:struct a;

struct b;

sizeof(a) = 8; int為4,char為1,short為2,這裡用到了原則1和原則3。

sizeof(b) = 12; 是否超出預想範圍?char為1,int為4,short為2,怎麼會是12?還是原則1和原則3。

深究一下,為什麼是這樣,我們可以看看記憶體裡的布局情況。

a  b 

c a的記憶體布局:1111, 

1*, 

11 b 

a  c

b的記憶體布局:1***, 

1111, 

11**

其中星號*表示填充的位元組。a中,b後面為何要補充乙個位元組?因為c為short,其起始位置要為2的倍數,就是原則1。c的後面沒有補充,因為b和c正好占用4個位元組,整個a占用空間為4的倍數,也就是最大成員int型別的倍數,所以不用補充。

b中,b是char為1,b後面補充了3個位元組,因為a是int為4,根據原則1,起始位置要為4的倍數,所以b後面要補充3個位元組。c後面補充兩個位元組,根據原則3,整個b占用空間要為4的倍數,c後面不補充,整個b的空間為10,不符,所以要補充2個位元組。

再看乙個結構中含有結構成員的例子:

例3:struct a;

struct b;

sizeof(a) = 24; 這個比較好理解,int為4,double為8,float為4,總長為8的倍數,補齊,所以整個a為24。

sizeof(b) = 48; 看看b的記憶體布局。

e  f 

g  h 

i  b的記憶體布局:11* *, 

1111, 

11111111, 11 * * * * * *, 

1111* * * *, 11111111, 1111 * * * *

i其實就是a的記憶體布局。i的起始位置要為24的倍數,所以h後面要補齊。把b的記憶體布局弄清楚,有關結構體的對齊方式基本就算掌握了。

以上講的都是沒有#pragma pack巨集的情況,如果有#pragma pack巨集,對齊方式按照巨集的定義來。比如上面的結構體前加#pragma pack(1),記憶體的布局就會完全改變。sizeof(a) = 16; sizeof(b) = 32;

有了#pragma pack(1),記憶體不會再遵循原則1和原則3了,按1位元組對齊。沒錯,這不是理想中的沒有記憶體對齊的世界嗎。

a  b 

c a的記憶體布局:1111, 

11111111, 

1111

e  f 

g  h 

i  b的記憶體布局:11, 

1111, 

11111111, 11 , 

1111, 11111111, 1111

那#pragma pack(2)的結果又是多少呢?#pragma pack(4)呢?留給大家自己思考吧,相信沒有問題。

還有一種常見的情況,結構體中含位域字段。位域成員不能單獨被取sizeof值。c99規定int、unsigned int和bool可以作為位域型別,但編譯器幾乎都對此作了擴充套件,允許其它型別型別的存在。

使用位域的主要目的是壓縮儲存,其大致規則為: 

1) 如果相鄰位域字段的型別相同,且其位寬之和小於型別的sizeof大小,則後面的字段將緊鄰前乙個字段儲存,直到不能容納為止; 

2) 如果相鄰位域字段的型別相同,但其位寬之和大於型別的sizeof大小,則後面的字段將從新的儲存單元開始,其偏移量為其型別大小的整數倍; 

3) 如果相鄰的位域字段的型別不同,則各編譯器的具體實現有差異,vc6採取不壓縮方式,dev-c++採取壓縮方式; 

4) 如果位域字段之間穿插著非位域字段,則不進行壓縮; 

5) 整個結構體的總大小為最寬基本型別成員大小的整數倍。

還是讓我們來看看例子。

例4:struct a;

a  b 

c a的記憶體布局:111, 

1111 *, 

11111 * * *

位域型別為char,第1個位元組僅能容納下f1和f2,所以f2被壓縮到第1個位元組中,而f3只能從下乙個位元組開始。因此sizeof(a)的結果為2。

例5:struct b;

由於相鄰位域型別不同,在vc6中其sizeof為6,在dev-c++中為2。

例6:struct c;

非位域字段穿插在其中,不會產生壓縮,在vc6和dev-c++中得到的大小均為3。

考慮乙個問題,為什麼要設計記憶體對齊的處理方式呢?如果體系結構是不對齊的,成員將會乙個挨乙個儲存,顯然對齊更浪費了空間。那麼為什麼要使用對齊呢?體系結構的對齊和不對齊,是在時間和空間上的乙個權衡。對齊節省了時間。假設乙個體繫結構的字長為w,那麼它同時就假設了在這種體系結構上對寬度為w的資料的處理最頻繁也是最重要的。它的設計也是從優先提高對w位資料操作的效率來考慮的。有興趣的可以google一下,人家就可以跟你解釋的,一大堆的道理。

最後順便提一點,在設計結構體的時候,一般會尊照乙個習慣,就是把占用空間小的型別排在前面,占用空間大的型別排在後面,這樣可以相對節約一些對齊空間

C語言中結構體

struct oursvoid main struct ours o2 01 結構體整體直接賦值的時候,即使字串也可以直接賦值 o1.str o2.str 錯誤,字串不能直接賦值 字串拷貝的方式 sprintf o1.str,02.str strcpy o1.str,o2.str 3.1 第一種情況...

中結構體與C語言中結構體的區別

在c語言中,可以定義結構體型別,將多個相關的變數包裝成為乙個整體使用。在結構體中的變數,可以是相同 部分相同,或完全不同的資料型別。在c語言中,結構體不能包含函式。在物件導向的程式設計中,物件具有狀態 屬性 和行為,狀態儲存在成員變數中,行為通過成員方法 函式 來實現。c語言中的結構體只能描述乙個物...

C語言中的結構體

在 c語言中,結構體 struct 指的是一種資料結構,是c語言中聚合資料型別 aggregate data type 的一類。結構體可以被宣告為 變數 指標或 陣列等,用以實現較複雜的 資料結構。結構體同時也是一些元素的集合,這些元素稱為結構體的成員 member 且這些成員可以為不同的型別,成員...