當前位置:首頁 » 編程軟體 » 利用編譯器原理獲取結構體成員

利用編譯器原理獲取結構體成員

發布時間: 2022-12-30 10:41:01

1. 用c語言編寫學生基本信息(最好用結構體)拜託啦,急求!

在C語言中,可以使用結構體(Struct)來存放一組不同類型的數據。結構體的定義形式為:
struct 結構體名{
結構體所包含的變數或數組
};
結構體是一種集合,它裡麵包含了多個變數或數組,它們的類型可以相同,也可以不同,每個這樣的變數或數組都稱為結構體的成員(Member)。請看下面的一個例子:
struct stu{
char *name; //姓名
int num; //學號
int age; //年齡
char group; //所在學習小組
float score; //成績
};
stu 為結構體名,它包含了 5 個成員,分別是 name、num、age、group、score。結構體成員的定義方式與變數和數組的定義方式相同,只是不能初始化。
注意大括弧後面的分號;不能少,這是一條完整的語句。
結構體也是一種數據類型,它由程序員自己定義,可以包含多個其他類型的數據。

像 int、float、char 等是由C語言本身提供的數據類型,不能再進行分拆,我們稱之為基本數據類型;而結構體可以包含多個基本類型的數據,也可以包含其他的結構體,我們將它稱為復雜數據類型或構造數據類型。
結構體變數

既然結構體是一種數據類型,那麼就可以用它來定義變數。例如:
struct stu stu1, stu2;
定義了兩個變數 stu1 和 stu2,它們都是 stu 類型,都由 5 個成員組成。注意關鍵字struct不能少。

stu 就像一個「模板」,定義出來的變數都具有相同的性質。也可以將結構體比作「圖紙」,將結構體變數比作「零件」,根據同一張圖紙生產出來的零件的特性都是一樣的。

你也可以在定義結構體的同時定義結構體變數:
struct stu{
char *name; //姓名
int num; //學號
int age; //年齡
char group; //所在學習小組
float score; //成績
} stu1, stu2;
將變數放在結構體定義的最後即可。

如果只需要 stu1、stu2 兩個變數,後面不需要再使用結構體名定義其他變數,那麼在定義時也可以不給出結構體名,如下所示:
struct{ //沒有寫 stu
char *name; //姓名
int num; //學號
int age; //年齡
char group; //所在學習小組
float score; //成績
} stu1, stu2;
這樣做書寫簡單,但是因為沒有結構體名,後面就沒法用該結構體定義新的變數。

理論上講結構體的各個成員在內存中是連續存儲的,和數組非常類似,例如上面的結構體變數 stu1、stu2 的內存分布如下圖所示,共佔用 4+4+4+1+4 = 17 個位元組。

但是在編譯器的具體實現中,各個成員之間可能會存在縫隙,對於 stu1、stu2,成員變數 group 和 score 之間就存在 3 個位元組的空白填充(見下圖)。這樣算來,stu1、stu2 其實佔用了 17 + 3 = 20 個位元組。

關於成員變數之間存在「裂縫」的原因,我們將在《C語言和內存》專題中的《C語言內存對齊,提高定址效率》一節中詳細講解。
成員的獲取和賦值

結構體和數組類似,也是一組數據的集合,整體使用沒有太大的意義。數組使用下標[ ]獲取單個元素,結構體使用點號.獲取單個成員。獲取結構體成員的一般格式為:
結構體變數名.成員名;
通過這種方式可以獲取成員的值,也可以給成員賦值:
#include <stdio.h>
int main(){
struct{
char *name; //姓名
int num; //學號
int age; //年齡
char group; //所在小組
float score; //成績
} stu1;
//給結構體成員賦值
stu1.name = "Tom";
stu1.num = 12;
stu1.age = 18;
stu1.group = 'A';
stu1.score = 136.5;
//讀取結構體成員的值
printf("%s的學號是%d,年齡是%d,在%c組,今年的成績是%.1f!\n", stu1.name, stu1.num, stu1.age, stu1.group, stu1.score);
return 0;
}
運行結果:
Tom的學號是12,年齡是18,在A組,今年的成績是136.5!

除了可以對成員進行逐一賦值,也可以在定義時整體賦值,例如:
struct{
char *name; //姓名
int num; //學號
int age; //年齡
char group; //所在小組
float score; //成績
} stu1, stu2 = { "Tom", 12, 18, 'A', 136.5 };
不過整體賦值僅限於定義結構體變數的時候,在使用過程中只能對成員逐一賦值,這和數組的賦值非常類似。

2. 編譯器的工作原理

編譯 是從源代碼(通常為高級語言)到能直接被計算機或虛擬機執行的目標代碼(通常為低級語言或機器語言)的翻譯過程。然而,也存在從低級語言到高級語言的編譯器,這類編譯器中用來從由高級語言生成的低級語言代碼重新生成高級語言代碼的又被叫做反編譯器。也有從一種高級語言生成另一種高級語言的編譯器,或者生成一種需要進一步處理的的中間代碼的編譯器(又叫級聯)。
典型的編譯器輸出是由包含入口點的名字和地址, 以及外部調用(到不在這個目標文件中的函數調用)的機器代碼所組成的目標文件。一組目標文件,不必是同一編譯器產生,但使用的編譯器必需採用同樣的輸出格式,可以鏈接在一起並生成可以由用戶直接執行的EXE,
所以我們電腦上的文件都是經過編譯後的文件。

3. 結構體變數調用成員是是如何定址的

呵呵,還在研究那。
我把VC6.0編譯出的機器碼給你看看:

22: Student stu;
004011BD lea ecx,[ebp-20h] //將stu的地址(ebp-20h)放入ecx寄存器
004011C0 call @ILT+55(Student::Student) (0040103c) //調用Student 的構造函數對ecx寄存器所保存地址上的對象進行構造
004011C5 mov dword ptr [ebp-4],0

23: stu.num = 01;
004011CC mov dword ptr [ebp-20h],1 //將1賦值給stu.num

ebp為棧基址指針,也就是函數棧幀底部的內存地址,很明顯stu是放在棧幀底部20個位元組的內存空間上的,num放在其中最上面的4個位元組,也就是stu的首部,而name放在下面的16個位元組中。
如果對程序的內部工作機制很感興趣,就要看調試器列出的匯編代碼,什麼都能弄懂。

4. C語言結構體成員的引用問題

scanf的用法,格式後的參數都必須是地址,name是數組名,本身就是數據,age和sex都是整型數據,必須使用取地址符&。

p是struct student*類型,stu是stu[3]這個數組的第一個元素的地址,p指向第一個元素,因此ABC都是對的,D錯在把(int*)變數值賦值給(struct student*)變數,有個強制轉換則不會錯,因為age是結構體第一個變數,其地址就是結構體變數的地址。

是指針間的賦值,必須保證左右操作數的指針類型是一致的。由於n是結構體sk的第一個變數,因此其地址與結構體變數地址相同,可以強制轉換得到正確的地址。

c是一個二維數組,p是一個指向數組的指針,因此p的*運算都是得到一個數組,**運算才能取到數組中的值。只有D是**運算。

(4)利用編譯器原理獲取結構體成員擴展閱讀:

C語言初學者比較喜歡的Turbo C2.0提供了400多個運行時函數,每個函數都完成特定的功能,用戶可隨意調用。這些函數總體分成輸入輸出函數、數學函數、字元串和內存函數、與BIOS和DOS有關的函數、 字元屏幕和圖形功能函數、過程式控制制函數、目錄函數等。

Windows系統所提供的Windows SDK中包含了數千個跟Windows應用程序開發相關的函數。其他操作系統,如linux,也同樣提供了大量的函數讓應用程序開發人員調用。

參考資料來源:網路-C語言

5. 編譯原理詞法語法語義錯誤題!!!!!求大神啊!!!!

錯誤如下

  1. 在main函數中,調用了max函數取三個值中的最大值,而在max函數的聲明和定義中都只有兩個參數,屬於語義錯誤,應該把參數中加一個「int z」(在max函數的定義中出現了名為c的變數,不能重名,故用z)

  2. 在max函數中,第二行的「c==a>b?a:b」從上下文來看沒有任何意義,應該把條件表達式改為賦值表達式(「==」改為「=」),這個部分在語法分析中不會出問題,故屬於語義錯誤(編譯器不會報錯)

  3. 在max函數的第三行中,額。。我也不知道這個想表達什麼,而且點號一般用於獲取結構體中的成員,屬於後綴表達式,其yacc語法為

    postfix_expression '.' IDENTIFIER
    而在語法和詞法分析中都不會出錯,屬於語義錯誤,應改為「c=c > z ? c : z;」


這樣改動後,max中的局部變數d就沒有了什麼作用,建議刪去

6. 如何用C語言讀取txt文件中的數據到結構體數組中

1、在vscode裡面添加了Python文件和用於讀取的文本文件。

7. 結構體c語言是什麼

在C語言中,結構體(struct)指的是一種數據結構,是C語言中復合數據類型(aggregate data type)的一類。

結構體可以被聲明為變數、指針或數組等,用以實現較復雜的數據結構。結構體同時也是一些元素的集合,這些元素稱為結構體的成員(member),且這些成員可以為不同的類型,成員一般用名字訪問

一、成員訪問

結構體成員依據結構體變數類型的不同,一般有2種訪問方式,一種為直接訪問,一種為間接訪問。直接訪問應用於普通的結構體變數,間接訪問應用於指向結構體變數的指針。

直接訪問使用結構體變數名.成員名,間接訪問使用(*結構體指針名).成員名或者使用結構體指針名->成員名。相同的成員名稱依靠不同的變數前綴區分。

二、變數存儲

在內存中,編譯器按照成員列表順序分別為每個結構體變數成員分配內存,當存儲過程中需要滿足邊界對齊的要求時,編譯器會在成員之間留下額外的內存空間。

如果想確認結構體佔多少存儲空間,則使用關鍵字sizeof,如果想得知結構體的某個特定成員在結構體的位置,則使用offsetof宏(定義於stddef.h)。

匿名struct

匿名struct、匿名union以及C++的匿名class,是指既沒有類型名,也沒有直接用這種類型定義了對象;如果緊隨類型定義之後,又定義了該類型的對象,就不算是匿名類型,與普通情形的使用是一樣的。

匿名類型作為嵌套定義,即在一個外部類(這里的類是指struct、union、class)的內部定義,則編譯器就在匿名類型定義之後定義一個無名變數,並把該匿名類型的數據成員的名字提升到匿名類的外部類的作用域內。

如果匿名類型是連續嵌套,則最內部的匿名類型的成員名字被提升到最外部的可用變數名字訪問的類的作用域內。

8. 編譯原理的數據結構

編譯原理一直是計算機學習的必修課.
當然,由編譯器的階段使用的演算法與支持這些階段的數據結構之間的交互是非常強大的。編譯器的編寫者盡可能有效實施這些方法且不引起復雜性。理想的情況是:與程序大小成線性比例的時間內編譯器,換言之就是,在0 ( n )時間內,n是程序大小的度量(通常是字元數)。本節將講述一些主要的數據結構,它們是其操作部分階段所需要的,並用來在階段中交流信息。 臨時文件(temporary file):計算機過去一直未能在編譯器時將整個程序保留在存儲器中。這一問題已經通過使用臨時文件來保存翻譯時中間步驟的結果或通過「匆忙地」編譯(也就是只保留源程序早期部分的足夠信息用以處理翻譯)解決了。存儲器的限制現在也只是一個小問題了,現在可以將整個編譯單元放在存儲器之中,特別是在可以分別編譯的語言中時。但是偶爾還是會發現需要在某些運行步驟中生成中間文件。其中典型的是代碼生成時需要反填(backpatch)地址。例如,當翻譯如下的條件語句時 if x = 0 then ... else ... 在知道else部分代碼的位置之前必須由文本跳到else部分:
CMP X,0 JNE NEXT ;;
location of NEXT not yet known < code for then-part > NEXT : < code for else-part >
通常,必須為NEXT的值留出一個空格,一旦知道該值後就會將該空格填上,利用臨時文件可以很容易地做到這一點。
如果想利用上面的編譯原理開發一套屬於自己的編程語言,或者想在一個產品中嵌入編程語言,可以參考zengl開源網開發的zengl編程語言,該編程語言為國人使用C語言開發,裡麵包含兩個部分,一個是編譯器,一個是解釋執行中間代碼的虛擬機。編譯器包含了詞法掃描,語法分析,中間代碼輸出等,虛擬機則類似JAVA一樣解釋執行中間代碼。作者將所有的版本都公布出來,好讓讀者可以由淺入深的做研究,並且為了證明該編程語言的實用性,還結合SDL游戲開發庫開發了一款圖形界面和命令行界面的21點撲克小游戲 。
zengl編程語言目前適用平台為windows和linux (最開始在Linux下使用gcc開發,後來移植到windows平台)

9. C++ 結構體 成員函數

C++允許在結構中定義函數,該函數稱為成員函數。其描述形式如下:
struct 結構名{
數據成員
成員函數
}
下面是一個使用成員函數的例子
#include <iostream>

/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;

struct Point
{
double m,n;
void Setab(double a,double b)
{
m=a;
n=b;
}

void display()
{
cout<<"display()------>>" <<m<<"\t"<<n<<endl;
}
};

int main(int argc, char** argv)
{
Point p;
p.Setab(2,3);
p.display();
cout <<"數據成員"<<p.m<<"\t" <<p.n <<endl;
return 0;
}

總結:
1、用到了結構塊 結構塊中的函數調用的方法,寫完上面程序發現C++的結構塊像Java中靜態方法,直接用結構名調用數據成員和成員函數;還有一個就是「\t」 是製表符意思是橫向跳到下一個製表符的位置,相當於鍵盤上的Tab鍵,「\n」換行相當於endl;
2、在結構中成員默認都是public如不不希望對象直接訪問數據成員,可以將數據成員使用private來修飾,即私有的數據成員,必須通過共有的成員函數才能使用,成為數據的封裝性,下個小例子看看數據的封裝性。

10. C語言中結構體位元組的計算方式

  在說計算方式之前先講講幾個概念一個是 偏移量 還有一個是 內存對齊 。先說偏移量,網路對於它的定義是這樣:把存儲單元的實際地址與其所在段的段地址之間的距離稱為段內偏移,也稱為「有效地址或偏移量」。在結構體裡面大概是指結構體變數中成員的地址和結構體變數地址的差。然後再說一下內存對齊這個概念:內存中存放基本類型數據時,計算機的系統會對其位置有限制,系統會要求這些數據的首地址的值是某個數的倍數,而這個數被稱為該數據類型的對齊模數。雖然ANSI C標准中沒有強制規定相鄰聲明的變數內存中要相鄰,但是編譯器會自動幫你處理這個問題,也就是相鄰變數之間可能會填充一些位元組。因此在這個問題上又有了編譯器的區別。

  那我們先來講講結構體變數在微軟的編譯器的對齊吧

      1.結構成員的首地址要是其最寬的基本類型成員的整數倍。編譯器在給結構體分配內存的時候先找到最寬的基本成員,然後再在內存中尋找地址,並將這個最寬的基本數據類型的大小作為對齊模數

       2.結構體每一個成員相對於首地址的偏移量是成員大小的整數倍,如果沒有達到這個要求,編譯器會自動填加位元組。編譯器在為結構體成員開辟內存的時候會先檢查開辟內存的首地址與結構體變數的首地址之間的偏移量,如果是成員體的整數倍那麼就存放這個變數,不然的話就在這個成員和上一個成員之間填充位元組,以達到整數倍的目的

       3.結構體所佔的總內存大小要是最大成員體大小的整數倍,如果不是,那麼編譯器會在末尾補充位元組。結構體的最後一個成員,不僅要滿足前兩條原則,最後一條准則也要滿足。

      接下來來看看幾個例子。

這個結構體在VS 2017下的sizeof的運算結果是12。那麼根據上面的對其規則我們來對其進行計算。

首先是char a。char大小是1,相對於首地址的偏移量是0,然後是int i。int i的大小是4,相對於首地址的偏移量是1,但是1不是4的整數倍,所以編譯器會自動在char a和int i之間填充位元組位元組。所以in i的偏移量是4。而之後的float b大小4偏移量就是int i的偏移量加上int i的大小故float b的偏移量大小就是8,而8正好是4的倍數那麼就不會有位元組填充。而結構體的大小也就自然是最後8+4=12了

從這里也可以看出結構體大小等於最後一個成員體的大小加上它的偏移量。

那麼我們再來看一個例子

那麼我們再利用之前的演算法來對其進行運算,int i的大小是4偏移量是0,int c 的大小是4偏移量是4,double b的大小是8,偏移量是8。char a的大小是1 ,偏移量是16。那麼這個結構體的變數的大小就是16+1=17嗎?答案肯定不是這樣的。在VS的sizeof的運算下這個的結果是24,為什是24呢,那麼這之前說的最後一條原則就要用上了。結構體的大小確實是等於最後一個成員的偏移量加上最後一個成員的大小,但是如果這個結構不滿足是結構體中最大成員大小的整數倍這個條件那麼,編譯器會自動在最後填充位元組使其滿足,也就是說,雖然我們計算出的結果是17但是17並不是8的倍數,所以編譯器自動在最後填充位元組使其成為8的倍數,即自動擴充成24.、

那我們再來說一下GCC編譯器下的模式。GCC編譯器在Windows環境下用的會比較少,主要在Linux平台下使用GCC編譯器就不遵守微軟的編譯器下的一些准則了,比如之前 說過的對齊模數。微軟的編譯器下的對齊模數是結構體成員中最大的大小而在GCC編譯器下對齊模數最大隻能是4。這就意味著對齊模數只能是1,2,4中的一個。因此之前講過的在微軟編譯器下的的一些原則會有些不同。之前講過的成員的首地址的偏移量要是成員大小的整數倍在這里就有點區別了。在GCC中如果成員大小小於等數4那麼繼續按照之前的標准就好了,但如果大於4,則結構體每個成員相對於結構體首地址的偏移量只能按照是4的整數倍來進行判斷是否添加填充。來看一個簡單的例子。

在這個例子中,如果是按照微軟的編譯器的話計算的結構應該是16,但是在GCC編譯器下是12。道理就是之前講的。

  雖然理論上如此但是我自己在試的時候發現上面的運算結果是16,沒錯是16而不是12!為什麼呢?難道是這個理論錯了么,當然不是。我們可以嘗試在GCC下計算一下sizeof(int *)(是int *而不是int)。你會發現結果是8(如果是sizeof(int)那麼結果結果就是4),輸出結果是8那就解決了我們的疑惑。 64位系統的對其長度默認是8而32位的才是4 。這就合理解釋了為什麼計算結果是16而不是12。

對齊模數的選擇只能是基於基本數據類型,所以對於結構體嵌套結構體就不能這么,至於其的計算方式之後再補充

熱點內容
數據結構編譯器哪個好 發布:2025-09-18 04:33:52 瀏覽:435
ad轉換c語言 發布:2025-09-18 04:21:21 瀏覽:752
sqlserver2008設置外鍵 發布:2025-09-18 04:21:12 瀏覽:114
伺服器電腦電源管理 發布:2025-09-18 03:52:33 瀏覽:324
叉叉助手刪除腳本 發布:2025-09-18 03:21:24 瀏覽:851
深圳ug五軸編程培訓 發布:2025-09-18 03:13:35 瀏覽:197
安卓軟體殘留怎麼清理 發布:2025-09-18 03:02:02 瀏覽:345
centos7apachephp7 發布:2025-09-18 03:01:47 瀏覽:657
安卓如何實現點擊彈出列表 發布:2025-09-18 02:47:25 瀏覽:58
python文件函數 發布:2025-09-18 02:47:23 瀏覽:574