国产毛片a精品毛-国产毛片黄片-国产毛片久久国产-国产毛片久久精品-青娱乐极品在线-青娱乐精品

C/C++ 嵌入式筆試題

發(fā)布時間:2009-4-7 14:53    發(fā)布者:虞美人
關鍵詞: 筆試 , 嵌入式
預處理器(Preprocessor)

1. 用預處理指令#define 聲明一個常數(shù),用以表明1年中有多少秒(忽略閏年問題)

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
我在這想看到幾件事情:
1). #define 語法的基本知識(例如:不能以分號結束,括號的使用,等等)
2). 懂得預處理器將為你計算常數(shù)表達式的值,因此,直接寫出你是如何計算一年中有多少秒而不是計算出實際的值,是更清晰而沒有代價的。
3). 意識到這個表達式將使一個16位機的整型數(shù)溢出-因此要用到長整型符號L,告訴編譯器這個常數(shù)是的長整型數(shù)。
4). 如果你在你的表達式中用到UL(表示無符號長整型),那么你有了一個好的起點。記住,第一印象很重要。


2. 寫一個“標準”宏MIN,這個宏輸入兩個參數(shù)并返回較小的一個。


#define MIN(A,B) ((A) <= (B) (A) : (B))
這個測試是為下面的目的而設的:
1). 標識#define在宏中應用的基本知識。這是很重要的,因為直到嵌入(inline)操作符變?yōu)闃藴蔆的一部分,宏是方便產(chǎn)生嵌入代碼的唯一方法,對于嵌入式系統(tǒng)來說,為了能達到要求的性能,嵌入代碼經(jīng)常是必須的方法。
2). 三重條件操作符的知識。這個操作符存在C語言中的原因是它使得編譯器能產(chǎn)生比if-then-else更優(yōu)化的代碼,了解這個用法是很重要的。
3). 懂得在宏中小心地把參數(shù)用括號括起來
4). 我也用這個問題開始討論宏的副作用,例如:當你寫下面的代碼時會發(fā)生什么事?
least = MIN(*p++, b);
其實宏在編程中的副作用主要表現(xiàn)在編譯器的處理計算上,如果考慮不周全很容易出現(xiàn)重復計算的問題。所以寫程序要用宏的簡潔,又要注意其中的陷阱,以防出現(xiàn)莫名其妙的錯誤

3. 預處理器標識#error的目的是什么?

如果你不知道答案,請看參考文獻1。這問題對區(qū)分一個正常的伙計和一個書呆子是很有用的。只有書呆子才會讀C語言課本的附錄去找出象這種
問題的答案。當然如果你不是在找一個書呆子,那么應試者最好希望自己不要知道答案。


死循環(huán)(Infinite loops)


4. 嵌入式系統(tǒng)中經(jīng)常要用到無限循環(huán),你怎么樣用C編寫死循環(huán)呢?

這個問題用幾個解決方案。我首選的方案是:
while(1) { }
一些程序員更喜歡如下方案:
for(;;) { }
這個實現(xiàn)方式讓我為難,因為這個語法沒有確切表達到底怎么回事。如果一個應試者給出這個作為方案,我將用這個作為一個機會去探究他們這樣做的
基本原理。如果他們的基本答案是:“我被教著這樣做,但從沒有想到過為什么。”這會給我留下一個壞印象。
第三個方案是用 goto
Loop:
...
goto Loop;
應試者如給出上面的方案,這說明或者他是一個匯編語言程序員(這也許是好事)或者他是一個想進入新領域的BASIC/FORTRAN程序員。

數(shù)據(jù)聲明(Data declarations)

5. 用變量a給出下面的定義

a) 一個整型數(shù)(An integer)
b) 一個指向整型數(shù)的指針(A pointer to an integer)
c) 一個指向指針的的指針,它指向的指針是指向一個整型數(shù)(A pointer to a pointer to an integer)
d) 一個有10個整型數(shù)的數(shù)組(An array of 10 integers)
e) 一個有10個指針的數(shù)組,該指針是指向一個整型數(shù)的(An array of 10 pointers to integers)
f) 一個指向有10個整型數(shù)數(shù)組的指針(A pointer to an array of 10 integers)
g) 一個指向函數(shù)的指針,該函數(shù)有一個整型參數(shù)并返回一個整型數(shù)(A pointer to a function that takes an integer as an argument and returns an integer)
h) 一個有10個指針的數(shù)組,該指針指向一個函數(shù),該函數(shù)有一個整型參數(shù)并返回一個整型數(shù)( An array of ten pointers to functions that take an integer argument and return an integer )

答案是:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer


人們經(jīng)常聲稱這里有幾個問題是那種要翻一下書才能回答的問題,我同意這種說法。當我寫這篇文章時,為了確定語法的正確性,我的確查了一下書。
但是當我被面試的時候,我期望被問到這個問題(或者相近的問題)。因為在被面試的這段時間里,我確定我知道這個問題的答案。應試者如果不知道
所有的答案(或至少大部分答案),那么也就沒有為這次面試做準備,如果該面試者沒有為這次面試做準備,那么他又能為什么出準備呢?


Static

6. 關鍵字static的作用是什么?

這個簡單的問題很少有人能回答完全。在C語言中,關鍵字static有三個明顯的作用:
1). 在函數(shù)體,一個被聲明為靜態(tài)的變量在這一函數(shù)被調用過程中維持其值不變。
2). 在模塊內(nèi)(但在函數(shù)體外),一個被聲明為靜態(tài)的變量可以被模塊內(nèi)所用函數(shù)訪問,但不能被模塊外其它函數(shù)訪問。它是一個本地的全局變量。
3). 在模塊內(nèi),一個被聲明為靜態(tài)的函數(shù)只可被這一模塊內(nèi)的其它函數(shù)調用。那就是,這個函數(shù)被限制在聲明它的模塊的本地范圍內(nèi)使用。
大多數(shù)應試者能正確回答第一部分,一部分能正確回答第二部分,同是很少的人能懂得第三部分。這是一個應試者的嚴重的缺點,因為他顯然不懂得本地化數(shù)據(jù)和代碼范圍的好處和重要性。


Const

7.關鍵字const是什么含意?
我只要一聽到被面試者說:“const意味著常數(shù)”,我就知道我正在和一個業(yè)余者打交道。去年Dan Saks已經(jīng)在他的文章里完全概括了const的所有用法,因此ESP(譯者:Embedded Systems Programming)的每一位讀者應該非常熟悉const能做什么和不能做什么.如果你從沒有讀到那篇文章,只要能說出const意味著“只讀”就可以了。盡管這個答案不是完全的答案,但我接受它作為一個正確的答案。(如果你想知道更詳細的答案,仔細讀一下Saks的文章吧。)如果應試者能正確回答這個問題,我將問他一個附加的問題:下面的聲明都是什么意思?

const int a;
int const a;
const int *a;
int * const a;
int const * a const;

前兩個的作用是一樣,a是一個常整型數(shù)。第三個意味著a是一個指向常整型數(shù)的指針(也就是,整型數(shù)是不可修改的,但指針可以)。第四個意思a是一個指向整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是可以修改的,但指針是不可修改的)。最后一個意味著a是一個指向常整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是不可修改的,同時指針也是不可修改的)。如果應試者能正確回答這些問題,那么他就給我留下了一個好印象。順帶提一句,也許你可能會問,即使不用關鍵字const,也還是能很容易寫出功能正確的程序,那么我為什么還要如此看重關鍵字const呢?我也如下的幾下理由:
1). 關鍵字const的作用是為給讀你代碼的人傳達非常有用的信息,實際上,聲明一個參數(shù)為常量是為了告訴了用戶這個參數(shù)的應用目的。如果你曾花很多時間清理其它人留下的垃圾,你就會很快學會感謝這點多余的信息。(當然,懂得用const的程序員很少會留下的垃圾讓別人來清理的。)
2). 通過給優(yōu)化器一些附加的信息,使用關鍵字const也許能產(chǎn)生更緊湊的代碼。
3). 合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的參數(shù),防止其被無意的代碼修改。簡而言之,這樣可以減少bug的出現(xiàn)。

Volatile

8. 關鍵字volatile有什么含意 并給出三個不同的例子。

一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優(yōu)化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個例子:
1). 并行設備的硬件寄存器(如:狀態(tài)寄存器)
2). 一個中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)
3). 多線程應用中被幾個任務共享的變量
回答不出這個問題的人是不會被雇傭的。我認為這是區(qū)分C程序員和嵌入式系統(tǒng)程序員的最基本的問題。嵌入式系統(tǒng)程序員經(jīng)常同硬件、中斷、RTOS等等打交道,所用這些都要求volatile變量。不懂得volatile內(nèi)容將會帶來災難。
假設被面試者正確地回答了這是問題(嗯,懷疑這否會是這樣),我將稍微深究一下,看一下這家伙是不是直正懂得volatile完全的重要性。
1). 一個參數(shù)既可以是const還可以是volatile嗎?解釋為什么。
2). 一個指針可以是volatile 嗎?解釋為什么。
3). 下面的函數(shù)有什么錯誤:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案:
1). 是的。一個例子是只讀的狀態(tài)寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。
2). 是的。盡管這并不很常見。一個例子是當一個中服務子程序修該一個指向一個buffer的指針時。
3). 這段代碼的有個惡作劇。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由于*ptr指向一個volatile型參數(shù),編譯器將產(chǎn)生類似下面的代碼:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}

位操作(Bit manipulation)

9. 嵌入式系統(tǒng)總是要用戶對變量或寄存器進行位操作。給定一個整型變量a,寫兩段代碼,第一個設置a的bit 3,第二個清除a 的bit 3。在以上兩個操作中,要保持其它位不變。

對這個問題有三種基本的反應
1). 不知道如何下手。該被面者從沒做過任何嵌入式系統(tǒng)的工作。
2). 用bit fields。Bit fields是被扔到C語言死角的東西,它保證你的代碼在不同編譯器之間是不可移植的,同時也保證了的你的代碼是不可重用的。我最近不幸看到 Infineon為其較復雜的通信芯片寫的驅動程序,它用到了bit fields因此完全對我無用,因為我的編譯器用其它的方式來實現(xiàn)bit fields的。從道德講:永遠不要讓一個非嵌入式的家伙粘實際硬件的邊。
3). 用 #defines 和 bit masks 操作。這是一個有極高可移植性的方法,是應該被用到的方法。最佳的解決方案如下:
#define BIT3 (0x1<<3)
static int a;
void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}
一些人喜歡為設置和清除值而定義一個掩碼同時定義一些說明常數(shù),這也是可以接受的。我希望看到幾個要點:說明常數(shù)、|=和&=~操作。

訪問固定的內(nèi)存位置(Accessing fixed memory locations)

10. 嵌入式系統(tǒng)經(jīng)常具有要求程序員去訪問某特定的內(nèi)存位置的特點。在某工程中,要求設置一絕對地址為0x67a9的整型變量的值為0xaa66。編譯器是一個純粹的ANSI編譯器。寫代碼去完成這一任務。

這一問題測試你是否知道為了訪問一絕對地址把一個整型數(shù)強制轉換(typecast)為一指針是合法的。這一問題的實現(xiàn)方式隨著個人風格不同而不同。典型的類似代碼如下:
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;

一個較晦澀的方法是:
*(int * const)(0x67a9) = 0xaa55;

即使你的品味更接近第二種方案,但我建議你在面試時使用第一種方案。

中斷(Interrupts)

11. 中斷是嵌入式系統(tǒng)中重要的組成部分,這導致了很多編譯開發(fā)商提供一種擴展—讓標準C支持中斷。具代表事實是,產(chǎn)生了一個新的關鍵字 __interrupt。下面的代碼就使用了__interrupt關鍵字去定義了一個中斷服務子程序(ISR),請評論一下這段代碼的。

__interrupt double compute_area (double radius)
{
     double area = PI * radius * radius;
     printf(" Area = %f", area);
     return area;
}

這個函數(shù)有太多的錯誤了,以至讓人不知從何說起了:
1). ISR 不能返回一個值。如果你不懂這個,那么你不會被雇用的。
2). ISR 不能傳遞參數(shù)。如果你沒有看到這一點,你被雇用的機會等同第一項。
3). 在許多的處理器/編譯器中,浮點一般都是不可重入的。有些處理器/編譯器需要讓額處的寄存器入棧,有些處理器/編譯器就是不允許在ISR中做浮點運算。此外,ISR應該是短而有效率的,在ISR中做浮點運算是不明智的。
4). 與第三點一脈相承,printf()經(jīng)常有重入和性能上的問題。如果你丟掉了第三和第四點,我不會太為難你的。不用說,如果你能得到后兩點,那么你的被雇用前景越來越光明了。

代碼例子(Code examples)

12 . 下面的代碼輸出是什么,為什么?

void foo(void)
{
     unsigned int a = 6;
     int b = -20;
     (a+b > 6) puts("> 6") : puts("<= 6");
}

這個問題測試你是否懂得C語言中的整數(shù)自動轉換原則,我發(fā)現(xiàn)有些開發(fā)者懂得極少這些東西。不管如何,這無符號整型問題的答案是輸出是 “>6”。原因是當表達式中存在有符號類型和無符號類型時所有的操作數(shù)都自動轉換為無符號類型。因此-20變成了一個非常大的正整數(shù),所以該表達式計算出的結果大于6。這一點對于應當頻繁用到無符號數(shù)據(jù)類型的嵌入式系統(tǒng)來說是豐常重要的。如果你答錯了這個問題,你也就到了得不到這份工作的邊緣。

13. 評價下面的代碼片斷:

unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1's complement of zero */

對于一個int型不是16位的處理器為說,上面的代碼是不正確的。應編寫如下:

unsigned int compzero = ~0;

這一問題真正能揭露出應試者是否懂得處理器字長的重要性。在我的經(jīng)驗里,好的嵌入式程序員非常準確地明白硬件的細節(jié)和它的局限,然而PC機程序往往把硬件作為一個無法避免的煩惱。
到了這個階段,應試者或者完全垂頭喪氣了或者信心滿滿志在必得。如果顯然應試者不是很好,那么這個測試就在這里結束了。但如果顯然應試者做得不錯,那么我就扔出下面的追加問題,這些問題是比較難的,我想僅僅非常優(yōu)秀的應試者能做得不錯。提出這些問題,我希望更多看到應試者應付問題的方法,而不是答案。不管如何,你就當是這個娛樂吧…

動態(tài)內(nèi)存分配(Dynamic memory allocation)

14. 盡管不像非嵌入式計算機那么常見,嵌入式系統(tǒng)還是有從堆(heap)中動態(tài)分配內(nèi)存的過程的。那么嵌入式系統(tǒng)中,動態(tài)分配內(nèi)存可能發(fā)生的問題是什么?

這里,我期望應試者能提到內(nèi)存碎片,碎片收集的問題,變量的持行時間等等。這個主題已經(jīng)在ESP雜志中被廣泛地討論過了(主要是 P.J. Plauger, 他的解釋遠遠超過我這里能提到的任何解釋),所有回過頭看一下這些雜志吧!讓應試者進入一種虛假的安全感覺后,我拿出這么一個小節(jié)目:下面的代碼片段的輸出是什么,為什么?

char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Got a null pointer");
else
puts("Got a valid pointer");

這是一個有趣的問題。最近在我的一個同事不經(jīng)意把0值傳給了函數(shù)malloc,得到了一個合法的指針之后,我才想到這個問題。這就是上面的代碼,該代碼的輸出是“Got a valid pointer”。我用這個來開始討論這樣的一問題,看看被面試者是否想到庫例程這樣做是正確。得到正確的答案固然重要,但解決問題的方法和你做決定的基本原理更重要些。

Typedef

15. Typedef 在C語言中頻繁用以聲明一個已經(jīng)存在的數(shù)據(jù)類型的同義字。也可以用預處理器做類似的事。例如,思考一下下面的例子:
#define dPS struct s *
typedef struct s * tPS;

以上兩種情況的意圖都是要定義dPS 和 tPS 作為一個指向結構s指針。哪種方法更好呢?(如果有的話)為什么?
這是一個非常微妙的問題,任何人答對這個問題(正當?shù)脑颍┦菓敱还驳摹4鸢甘牵簍ypedef更好。思考下面的例子:
dPS p1,p2;
tPS p3,p4;

第一個擴展為
struct s * p1, p2;

上面的代碼定義p1為一個指向結構的指,p2為一個實際的結構,這也許不是你想要的。第二個例子正確地定義了p3 和p4 兩個指針。


16. C語言同意一些令人震驚的結構,下面的結構是合法的嗎,如果是它做些什么?
int a = 5, b = 7, c;
c = a+++b;

這個問題將做為這個測驗的一個愉快的結尾。不管你相不相信,上面的例子是完全合乎語法的。問題是編譯器如何處理它?水平不高的編譯作者實際上會爭論這個問題,根據(jù)最處理原則,編譯器應當能處理盡可能所有合法的用法。因此,上面的代碼被處理成:
c = a++ + b;
因此, 這段代碼持行后a = 6, b = 7, c = 12。


1、請定義一個宏,比較兩個數(shù)a、b的大小,不能使用大于、小于、if語句

#define Max(a,b) ( a/b)?a:b

2、如何輸出源文件的標題和目前執(zhí)行行的行數(shù)

int line = __LINE__;

char *file = __FILE__;

cout<<"file name is "<<(file)<<",line is "<

3、兩個數(shù)相乘,小數(shù)點后位數(shù)沒有限制,請寫一個高精度算法

4、寫一個病毒

while (1)

{

int *p = new int[10000000];

}

5、不使用額外空間,將 A,B兩鏈表的元素交*歸并

6、將樹序列化 轉存在數(shù)組或 鏈表中

struct st{

int i;

short s;

char c;

};

sizeof(struct st);

7、

char * p1;

void * p2;

int p3;

char p4[10];

sizeof(p1...p4) =?

8、

4,4,4,10

二分查找

快速排序

雙向鏈表的刪除結點
 
1。主鍵用于唯一標識表中的行數(shù)據(jù),一個主鍵值對應一行數(shù)據(jù)。另外,會自動在主鍵上創(chuàng)建索引,用于加快查詢。
2。外鍵用于兩個表的聯(lián)系。兩個表必須具有相同類型的屬性,在該屬性上有相同的值。該屬性應為其中一個表的主鍵,在另外一個表設置為外鍵。

主鍵:唯一標識,不能為空,加快查詢速度,自動創(chuàng)建索引,
外鍵:約束內(nèi)表的數(shù)據(jù)的更新,從定義外鍵時可以發(fā)現(xiàn) 外鍵是和主鍵表聯(lián)系,數(shù)據(jù)類型要統(tǒng)一,長度(存儲大小)要統(tǒng)一。這樣在更新數(shù)據(jù)的時候會保持一致性。
索引能將數(shù)據(jù)按一定的規(guī)則進行排列這樣進行查詢時能很快定位數(shù)據(jù),從而加快查詢的速度,
但不合適的索引將導致INSERT或UPDATE很慢


#include<stdio.h>
#include<stdlib.h>

int   main(void)
{
       union A{
         char a;
         char y:3;
         char z:3;
         char x:2;
       }a;

       a.a = 0x67;
       printf("a.a =%0x a.x=%0x \t a.y=%0x\t a.z=%0x\n",a.a, a.x, a.y, a.z );
       return 0;
}
結果
a.a =67 a.x=ffffffff     a.y=ffffffff a.z=ffffffff
a.a =64 a.x=0 a.y=fffffffc a.z=fffffffc
a.a =65 a.x=1 a.y=fffffffd a.z=fffffffd

很久沒動c語言了,很多基礎性的東西都沒有深入學習。今天看到關于聯(lián)合體位域面試題,想了半天才知道程序答案的來由~~汗顏~~
如果單從位域來理解這個還是簡單,問題的關鍵是理解其在計算機內(nèi)的存取規(guī)則。
對a.a=64, 單從取位(二進制)上可知a.x=00, a.y=101, a.z=101.目前通用計算機x86大都是32位機,我的機器也是32位,在存取上默認是存取32位。對每個數(shù)而言第一位是符號位,補碼存儲。那么可以理解a.x的補碼就是00000000, a.y的補碼就是11111100, a.z的補碼就是11111100.這樣看比較自然,但如果輸出結果是10進制,就會覺得難以理解。當然關鍵還是對數(shù)據(jù)的存取規(guī)則和編碼的熟悉。
a.a=0x64的10進制結果是a.x=0, a.y=-4 ,a.z=-4
補充一點,union內(nèi)的變量順序對結果不影響(每次只可能有一種解釋是合理的,這個跟struct顯然不同)

關于位域在結構體的應用主要要注意內(nèi)存對齊規(guī)則的理解和空域的理解
http://blog.csdn.net/jiyucn/archive/2006/07/01/862085.aspx
使用位域的主要目的是壓縮存儲,其大致規(guī)則為:
1)   如果相鄰位域字段的類型相同,且其位寬之和小于類型的sizeof大小,則后面的字段將緊鄰前一個字段存儲,直到不能容納為止;
2)   如果相鄰位域字段的類型相同,但其位寬之和大于類型的sizeof大小,則后面的字段將從新的存儲單元開始,其偏移量為其類型大小的整數(shù)倍;
3)   如果相鄰的位域字段的類型不同,則各編譯器的具體實現(xiàn)有差異,VC6采取不壓縮方式,Dev-C++采取壓縮方式;
4)   如果位域字段之間穿插著非位域字段,則不進行壓縮;
5)   整個結構體的總大小為最寬基本類型成員大小的整數(shù)倍。

#include <stdio.h>
int main()
{
union
{
            struct
            {
                     unsigned short s1:3;
                     unsigned short s2:3;
                     unsigned short s3:3;
            }x;
            char c;
}v;
v.c=100;

printf("%d\n",sizeof(v));
printf("s1=%d\n",v.x.s1);
printf("s2=%d\n",v.x.s2);
printf("s3=%d\n",v.x.s3);
return 0;
}
fc6--linux下gcc-4.1.1
2
s1=4
s2=4
s3=5
windows xp2下vc6.0
2
s1=4
s2=4
s3=1

可見s3的結果并不一樣vc6.0的結果如果只是按位取,就很好理解,這樣跟之前的union的存取規(guī)則又不一樣了~~而對于gcc-4.1.1,s3=5還沒想出該結果的原因。同時考慮
struct
            {
                     unsigned short s1:3;
                     unsigned short s2:3;
                     unsigned short s3:3;
    unsigned short s4:7;
            }x;
   最后s4的結果更加撲朔迷離~~請大家多指點~·
#include<stdio.h>
#include<stdlib.h>

int   main(void)
{
       union A{
         char a;
         char y:3;
         char z:3;
         char x:2;
       }a;

       a.a = 0x67;
       printf("a.a =%0x a.x=%0x \t a.y=%0x\t a.z=%0x\n",a.a, a.x, a.y, a.z );
       return 0;
}
結果
a.a =67 a.x=ffffffff     a.y=ffffffff a.z=ffffffff
a.a =64 a.x=0 a.y=fffffffc a.z=fffffffc
a.a =65 a.x=1 a.y=fffffffd a.z=fffffffd

很久沒動c語言了,很多基礎性的東西都沒有深入學習。今天看到關于聯(lián)合體位域面試題,想了半天才知道程序答案的來由~~汗顏~~
如果單從位域來理解這個還是簡單,問題的關鍵是理解其在計算機內(nèi)的存取規(guī)則。
對a.a=64, 單從取位(二進制)上可知a.x=00, a.y=101, a.z=101.目前通用計算機x86大都是32位機,我的機器也是32位,在存取上默認是存取32位。對每個數(shù)而言第一位是符號位,補碼存儲。那么可以理解a.x的補碼就是00000000, a.y的補碼就是11111100, a.z的補碼就是11111100.這樣看比較自然,但如果輸出結果是10進制,就會覺得難以理解。當然關鍵還是對數(shù)據(jù)的存取規(guī)則和編碼的熟悉。
a.a=0x64的10進制結果是a.x=0, a.y=-4 ,a.z=-4
補充一點,union內(nèi)的變量順序對結果不影響(每次只可能有一種解釋是合理的,這個跟struct顯然不同)

關于位域在結構體的應用主要要注意內(nèi)存對齊規(guī)則的理解和空域的理解
http://blog.csdn.net/jiyucn/archive/2006/07/01/862085.aspx
使用位域的主要目的是壓縮存儲,其大致規(guī)則為:
1)   如果相鄰位域字段的類型相同,且其位寬之和小于類型的sizeof大小,則后面的字段將緊鄰前一個字段存儲,直到不能容納為止;
2)   如果相鄰位域字段的類型相同,但其位寬之和大于類型的sizeof大小,則后面的字段將從新的存儲單元開始,其偏移量為其類型大小的整數(shù)倍;
3)   如果相鄰的位域字段的類型不同,則各編譯器的具體實現(xiàn)有差異,VC6采取不壓縮方式,Dev-C++采取壓縮方式;
4)   如果位域字段之間穿插著非位域字段,則不進行壓縮;
5)   整個結構體的總大小為最寬基本類型成員大小的整數(shù)倍。

#include <stdio.h>
int main()
{
union
{
            struct
            {
                     unsigned short s1:3;
                     unsigned short s2:3;
                     unsigned short s3:3;
            }x;
            char c;
}v;
v.c=100;

printf("%d\n",sizeof(v));
printf("s1=%d\n",v.x.s1);
printf("s2=%d\n",v.x.s2);
printf("s3=%d\n",v.x.s3);
return 0;
}
fc6--linux下gcc-4.1.1
2
s1=4
s2=4
s3=5
windows xp2下vc6.0
2
s1=4
s2=4
s3=1

可見s3的結果并不一樣vc6.0的結果如果只是按位取,就很好理解,這樣跟之前的union的存取規(guī)則又不一樣了~~而對于gcc-4.1.1,s3=5還沒想出該結果的原因。同時考慮
struct
            {
                     unsigned short s1:3;
                     unsigned short s2:3;
                     unsigned short s3:3;
    unsigned short s4:7;
            }x;
   最后s4的結果更加撲朔迷離~~請大家多指點~·


C++的static有兩種用法:面向過程程序設計中的static和面向對象程序設計中的static。前者應用于普通變量和函數(shù),不涉及類;后者主要說明static在類中的作用。

一、面向過程設計中的static

1、靜態(tài)全局變量

在全局變量前,加上關鍵字static,該變量就被定義成為一個靜態(tài)全局變量。靜態(tài)全局變量有以下特點:
該變量在全局數(shù)據(jù)區(qū)分配內(nèi)存;
未經(jīng)初始化的靜態(tài)全局變量會被程序自動初始化為0(自動變量的值是隨機的,除非它被顯式初始化);
靜態(tài)全局變量在聲明它的整個文件都是可見的,而在文件之外是不可見的;
靜態(tài)變量都在全局數(shù)據(jù)區(qū)分配內(nèi)存,包括后面將要提到的靜態(tài)局部變量。對于一個完整的程序,在內(nèi)存中的分布情況如下圖:
代碼區(qū)
全局數(shù)據(jù)區(qū)
堆區(qū)
棧區(qū)

一般程序的由new產(chǎn)生的動態(tài)數(shù)據(jù)存放在堆區(qū),函數(shù)內(nèi)部的自動變量存放在棧區(qū)。自動變量一般會隨著函數(shù)的退出而釋放空間,靜態(tài)數(shù)據(jù)(即使是函數(shù)內(nèi)部的靜 態(tài)局部變量)也存放在全局數(shù)據(jù)區(qū)。全局數(shù)據(jù)區(qū)的數(shù)據(jù)并不會因為函數(shù)的退出而釋放空間。

的確,定義全局變量就可以實現(xiàn)變量在文件中的共享,但定義靜態(tài)全局變量還有以下好處:
靜態(tài)全局變量不能被其它文件所用;
其它文件中可以定義相同名字的變量,不會發(fā)生沖突;

2、靜態(tài)局部變量

在局部變量前,加上關鍵字static,該變量就被定義成為一個靜態(tài)局部變量。通常,在函數(shù)體內(nèi)定義了一個變量,每當程序運行到該語句時都會給該局部變量分配棧內(nèi)存。但隨著程序退出函數(shù)體,系統(tǒng)就會收回棧內(nèi)存,局部變量也相應失效。
但有時候我們需要在兩次調用之間對變量的值進行保存。通常的想法是定義一個全局變量來實現(xiàn)。但這樣一來,變量已經(jīng)不再屬于函數(shù)本身了,不再僅受函數(shù)的控制,給程序的維護帶來不便。
靜態(tài)局部變量正好可以解決這個問題。靜態(tài)局部變量保存在全局數(shù)據(jù)區(qū),而不是保存在棧中,每次的值保持到下一次調用,直到下次賦新值。

example:

void foo()
{
static int a;
a++;
cout<<a<<endl;
}
int main()
{
foo();
foo();
foo();
return 0;
}

結果是 1 2 3 每次foo()退出后,并未銷毀變量a,因為它是存放在全局數(shù)據(jù)區(qū)的,不是棧空間。
靜態(tài)局部變量有以下特點:

該變量在全局數(shù)據(jù)區(qū)分配內(nèi)存;
靜態(tài)局部變量在程序執(zhí)行到該對象的聲明處時被首次初始化,即以后的函數(shù)調用不再進行初始化;
靜態(tài)局部變量一般在聲明處初始化,如果沒有顯式初始化,會被程序自動初始化為0;
它始終駐留在全局數(shù)據(jù)區(qū),直到程序運行結束。但其作用域為局部作用域,當定義它的函數(shù)或語句塊結束時,其作用域隨之結束;

3、靜態(tài)函數(shù)

在函數(shù)的返回類型前加上static關鍵字,函數(shù)即被定義為靜態(tài)函數(shù)。靜態(tài)函數(shù)與普通函數(shù)不同,它只能在聲明它的文件當中可見,不能被其它文件使用。

靜態(tài)函數(shù)的例子:

//Example 4

#include <iostream.h>

static void fn();//聲明靜態(tài)函數(shù)

void main()

{

fn();

}

void fn()//定義靜態(tài)函數(shù)

{

int n=10; cout<<n<<endl;

}
定義靜態(tài)函數(shù)的好處:
靜態(tài)函數(shù)不能被其它文件所用;
其它文件中可以定義相同名字的函數(shù),不會發(fā)生沖突;


二、面向對象的static關鍵字(類中的static關鍵字)

1、靜態(tài)數(shù)據(jù)成員

在類內(nèi)數(shù)據(jù)成員的聲明前加上關鍵字static,該數(shù)據(jù)成員就是類內(nèi)的靜態(tài)數(shù)據(jù)成員。先舉一個靜態(tài)數(shù)據(jù)成員的例子。

可以看出,靜態(tài)數(shù)據(jù)成員有以下特點:
對于非靜態(tài)數(shù)據(jù)成員,每個類對象都有自己的拷貝。而靜態(tài)數(shù)據(jù)成員被當作是類的成員。無論這個類的對象被定義了多少個,靜態(tài)數(shù)據(jù)成員在程序中也只有一份拷貝,由該類型的所有對象共享訪問。也就是說,靜態(tài)數(shù)據(jù)成員是該類的所有對象所共有的。對該類的多個對象來說,靜態(tài)數(shù)據(jù)成員只分配一次內(nèi)存,供所有對象共用。所以,靜態(tài)數(shù)據(jù)成員的值對每個對象都是一樣的,它的值可以更新;
靜態(tài)數(shù)據(jù)成員存儲在全局數(shù)據(jù)區(qū)。靜態(tài)數(shù)據(jù)成員定義時要分配空間,所以不能在類聲明中定義。在Example 5中,語句int Myclass::Sum=0;是定義靜態(tài)數(shù)據(jù)成員;
靜態(tài)數(shù)據(jù)成員和普通數(shù)據(jù)成員一樣遵從public,protected,private訪問規(guī)則;
因為靜態(tài)數(shù)據(jù)成員在全局數(shù)據(jù)區(qū)分配內(nèi)存,屬于本類的所有對象共享,所以,它不屬于特定的類對象,在沒有產(chǎn)生類對象時其作用域就可見,即在沒有產(chǎn)生類的實例時,我們就可以操作它;
靜態(tài)數(shù)據(jù)成員初始化與一般數(shù)據(jù)成員初始化不同。靜態(tài)數(shù)據(jù)成員初始化的格式為:
<數(shù)據(jù)類型><類名>::<靜態(tài)數(shù)據(jù)成員名>=<值>
類的靜態(tài)數(shù)據(jù)成員有兩種訪問形式:
<類對象名>.<靜態(tài)數(shù)據(jù)成員名> 或 <類類型名>::<靜態(tài)數(shù)據(jù)成員名>
如果靜態(tài)數(shù)據(jù)成員的訪問權限允許的話(即public的成員),可在程序中,按上述格式來引用靜態(tài)數(shù)據(jù)成員 ;
靜態(tài)數(shù)據(jù)成員主要用在各個對象都有相同的某項屬性的時候。比如對于一個存款類,每個實例的利息都是相同的。所以,應該把利息設為存款類的靜態(tài)數(shù)據(jù)成員。這有兩個好處,第一,不管定義多少個存款類對象,利息數(shù)據(jù)成員都共享分配在全局數(shù)據(jù)區(qū)的內(nèi)存,所以節(jié)省存儲空間。第二,一旦利息需要改變時,只要改變一次,則所有存款類對象的利息全改變過來了;
同全局變量相比,使用靜態(tài)數(shù)據(jù)成員有兩個優(yōu)勢:
靜態(tài)數(shù)據(jù)成員沒有進入程序的全局名字空間,因此不存在與程序中其它全局名字沖突的可能性;
可以實現(xiàn)信息隱藏。靜態(tài)數(shù)據(jù)成員可以是private成員,而全局變量不能;

2、靜態(tài)成員函數(shù)
與靜態(tài)數(shù)據(jù)成員一樣,我們也可以創(chuàng)建一個靜態(tài)成員函數(shù),它為類的全部服務而不是為某一個類的具體對象服務。靜態(tài)成員函數(shù)與靜態(tài)數(shù)據(jù)成員一樣,都是類的內(nèi)部 實現(xiàn),屬于類定義的一部分。普通的成員函數(shù)一般都隱含了一個this指針,this指針指向類的對象本身,因為普通成員函數(shù)總是具體的屬于某個類的具體對象的。通常情況下,this 是缺省的。如函數(shù)fn()實際上是this->fn()。但是與普通函數(shù)相比,靜態(tài)成員函數(shù)由于不是與任何的對象相聯(lián)系,因此它不具有this指針。從這個意義上講,它無法訪問屬于類對象的非靜態(tài)數(shù)據(jù)成員,也無法訪問非靜態(tài)成員函數(shù),它只能調用其余的靜態(tài)成員函數(shù)。

關于靜態(tài)成員函數(shù),可以總結為以下幾點:
出現(xiàn)在類體外的函數(shù)定義不能指定關鍵字static;
靜態(tài)成員之間可以相互訪問,包括靜態(tài)成員函數(shù)訪問靜態(tài)數(shù)據(jù)成員和訪問靜態(tài)成員函數(shù);
非靜態(tài)成員函數(shù)可以任意地訪問靜態(tài)成員函數(shù)和靜態(tài)數(shù)據(jù)成員;
靜態(tài)成員函數(shù)不能訪問非靜態(tài)成員函數(shù)和非靜態(tài)數(shù)據(jù)成員;
由于沒有this指針的額外開銷,因此靜態(tài)成員函數(shù)與類的全局函數(shù)相比速度上會有少許的增長;
調用靜態(tài)成員函數(shù),可以用成員訪問操作符(.)和(->)為一個類的對象或指向類對象的指針調用靜態(tài)成員函數(shù),也可以直接使用如下格式:
<類名>::<靜態(tài)成員函數(shù)名>(<參數(shù)表>)
調用類的靜態(tài)成員函數(shù)。

 

堆和棧的區(qū)別


一般認為在c中分為這幾個存儲區(qū)
1棧 - 有編譯器自動分配釋放
2堆 - 一般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收
3全局區(qū)(靜態(tài)區(qū)),全局變量和靜態(tài)變量的存儲是放在一塊的,初始化的全局變量和靜
態(tài)變量在一塊區(qū)域,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。
- 程序結束釋放
4另外還有一個專門放常量的地方。 - 程序結束釋放
在函數(shù)體中定義的變量通常是在棧上,用malloc, calloc, realloc等分配內(nèi)存的函數(shù)分
配得到的就是在堆上。在所有函數(shù)體外定義的是全局量,加了static修飾符后不管在哪
里都存放在全局區(qū)(靜態(tài)區(qū)),在所有函數(shù)體外定義的static變量表示在該文件中有效,
不能extern到別的文件用,在函數(shù)體內(nèi)定義的static表示只在該函數(shù)體內(nèi)有效。另外,
函數(shù)中的"adgfdf"這樣的字符串存放在常量區(qū)。
比如:
int a = 0; 全局初始化區(qū)
char *p1; 全局未初始化區(qū)
main()
{
int b; 棧
char s[] = "abc";棧
char *p2; 棧
char *p3 = "123456"; 123456\0在常量區(qū),p3在棧上。
static int c =0; 全局(靜態(tài))初始化區(qū)
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得來得10和20字節(jié)的區(qū)域就在堆區(qū)。
strcpy(p1, "123456"); 123456\0放在常量區(qū),編譯器可能會將它與p3所指向的"12345
6"優(yōu)化成一塊。
}
還有就是函數(shù)調用時會在棧上有一系列的保留現(xiàn)場及傳遞參數(shù)的操作。
棧的空間大小有限定,vc的缺省是2M。棧不夠用的情況一般是程序中分配了大量數(shù)組和
遞歸函數(shù)層次太深。有一點必須知道,當一個函數(shù)調用完返回后它會釋放該函數(shù)中所有
的棧空間。棧是由編譯器自動管理的,不用你操心。
堆是動態(tài)分配內(nèi)存的,并且你可以分配使用很大的內(nèi)存。但是用不好會產(chǎn)生內(nèi)存泄漏。
并且頻繁地malloc和free會產(chǎn)生內(nèi)存碎片(有點類似磁盤碎片),因為c分配動態(tài)內(nèi)存時
是尋找匹配的內(nèi)存的。而用棧則不會產(chǎn)生碎片。
在棧上存取數(shù)據(jù)比通過指針在堆上存取數(shù)據(jù)快些。
一般大家說的堆棧和棧是一樣的,就是棧(stack),而說堆時才是堆heap.
棧是先入后出的,一般是由高地址向低地址生長。


堆(heap)和棧(stack)是C/C++編程不可避免會碰到的兩個基本概念。首先,這兩個概念

都可以在講數(shù)據(jù)結構的書中找到,他們都是基本的數(shù)據(jù)結構,雖然棧更為簡單一些。

在具體的C/C++編程框架中,這兩個概念并不是并行的。對底層機器代碼的研究可以揭示

,棧是機器系統(tǒng)提供的數(shù)據(jù)結構,而堆則是C/C++函數(shù)庫提供的。

具體地說,現(xiàn)代計算機(串行執(zhí)行機制),都直接在代碼底層支持棧的數(shù)據(jù)結構。這體現(xiàn)

在,有專門的寄存器指向棧所在的地址,有專門的機器指令完成數(shù)據(jù)入棧出棧的操作。

這種機制的特點是效率高,支持的數(shù)據(jù)有限,一般是整數(shù),指針,浮點數(shù)等系統(tǒng)直接支

持的數(shù)據(jù)類型,并不直接支持其他的數(shù)據(jù)結構。因為棧的這種特點,對棧的使用在程序

中是非常頻繁的。對子程序的調用就是直接利用棧完成的。機器的call指令里隱含了把

返回地址推入棧,然后跳轉至子程序地址的操作,而子程序中的ret指令則隱含從堆棧中

彈出返回地址并跳轉之的操作。C/C++中的自動變量是直接利用棧的例子,這也就是為什

么當函數(shù)返回時,該函數(shù)的自動變量自動失效的原因(因為 顏換指戳說饔們暗 狀態(tài))。

 

和棧不同,堆的數(shù)據(jù)結構并不是由系統(tǒng)(無論是機器系統(tǒng)還是操作系統(tǒng))支持的,而是由

函數(shù)庫提供的。基本的malloc/realloc/free函數(shù)維護了一套內(nèi)部的堆數(shù)據(jù)結構。當程序

使用這些函數(shù)去獲得新的內(nèi)存空間時,這套函數(shù)首先試圖從內(nèi)部堆中尋找可用的內(nèi)存空

間,如果沒有可以使用的內(nèi)存空間,則試圖利用系統(tǒng)調用來動態(tài)增加程序數(shù)據(jù)段的內(nèi)存

大小,新分配得到的空間首先被組織進內(nèi)部堆中去,然后再以適當?shù)男问椒祷亟o調用者

。當程序釋放分配的內(nèi)存空間時,這片內(nèi)存空間被返回內(nèi)部堆結構中,可能會被適當?shù)?br>
處理(比如和其他空閑空間合并成更大的空閑空間),以更適合下一次內(nèi)存分配申請。這

套復雜的分配機制實際上相當于一個內(nèi)存分配的緩沖池(Cache),使用這套機制有如下若

干原因:

1. 系統(tǒng)調用可能不支持任意大小的內(nèi)存分配。有些系統(tǒng)的系統(tǒng)調用只支持固定大小及其

倍數(shù)的內(nèi)存請求(按頁分配);這樣的話對于大量的小內(nèi)存分類來說會造成浪費。

2. 系統(tǒng)調用申請內(nèi)存可能是代價昂貴的。系統(tǒng)調用可能涉及用戶態(tài)和核心態(tài)的轉換。

3. 沒有管理的內(nèi)存分配在大量復雜內(nèi)存的分配釋放操作下很容易造成內(nèi)存碎片。

堆和棧的對比

從以上知識可知,棧是系統(tǒng)提供的功能,特點是快速高效,缺點是有限制,數(shù)據(jù)不靈活

;而棧是函數(shù)庫提供的功能,特點是靈活方便,數(shù)據(jù)適應面廣泛,但是效率有一定降低

。棧是系統(tǒng)數(shù)據(jù)結構,對于進程/線程是唯一的;堆是函數(shù)庫內(nèi)部數(shù)據(jù)結構,不一定唯一

。不同堆分配的內(nèi)存無法互相操作。棧空間分靜態(tài)分配和動態(tài)分配兩種。靜態(tài)分配是編

譯器完成的,比如自動變量(auto)的分配。動態(tài)分配由alloca函數(shù)完成。棧的動態(tài)分配

無需釋放(是自動的),也就沒有釋放函數(shù)。為可移植的程序起見,棧的動態(tài)分配操作是

不被鼓勵的!堆空間的分配總是動態(tài)的,雖然程序結束時所有的數(shù)據(jù)空間都會被釋放回

系統(tǒng),但是精確的申請內(nèi)存/釋放內(nèi)存匹配是良好程序的基本要素。


可以放一塊思考
堆和棧的生長方向恰好相反,
|--------------| 低地址
| 堆 |
|--------------|
| | |
| I |
| |
| ^ |
| 棧 | 高地址
-----------------
所以計算機中的堆和棧經(jīng)常時放一塊講的


nod 一般不是必要就不要動態(tài)創(chuàng)建,最討厭把new出來的東西當局部變量用,用萬了馬上
delete 的做法.

理由
1.棧分配比堆快,只需要一條指令就呢給配所有的局部變量
2.棧不會出現(xiàn)內(nèi)存碎片
3。棧對象好管理

當然,某些情況下也要那么寫,比如
1.對象很大
2.對象需要在某個特定的時刻構造或析夠
3.類只允許對象動態(tài)創(chuàng)建,比如VCL的大多數(shù)類

當然,必須用堆對象時也不能躲避


 對于類的申明(還沒有定義)來說,可以有限的方式使用它。如我們可以聲明指向該類類型的指針或引用。允許指針和引用是因為它們都有固定的大小,而與它們指向的對象的大小無關。只有到完全定義了該類才能對這些指針和引用解引用。
       只有對類定義了,才能聲明該類類型對象。在程序中還沒有看到類定義之前,數(shù)據(jù)成員只能是該類類型的指針或引用。 

       當一個類的類頭被看到時,它就被視為已經(jīng)聲明了,所以一個類可以有指向自身類型的指針或引用作為數(shù)據(jù)成員。只有一個類的類體已經(jīng)完整時,它才被視為已經(jīng)被定義。

       所以可以有如下形式:

    class LinkScreen{

          Screen window;

          LinkScreen *next;

          LinkScreen *prev;

    }
本文地址:http://m.qingdxww.cn/thread-3175-1-1.html     【打印本頁】

本站部分文章為轉載或網(wǎng)友發(fā)布,目的在于傳遞和分享信息,并不代表本網(wǎng)贊同其觀點和對其真實性負責;文章版權歸原作者及原出處所有,如涉及作品內(nèi)容、版權和其它問題,我們將根據(jù)著作權人的要求,第一時間更正或刪除。
您需要登錄后才可以發(fā)表評論 登錄 | 立即注冊

廠商推薦

  • Microchip視頻專區(qū)
  • 為何選擇集成電平轉換?
  • 想要避免發(fā)生災難,就用MPLAB® SiC電源仿真器!
  • 無線充電基礎知識及應用培訓教程3
  • 5分鐘詳解定時器/計數(shù)器E和波形擴展!
  • 貿(mào)澤電子(Mouser)專區(qū)

相關視頻

關于我們  -  服務條款  -  使用指南  -  站點地圖  -  友情鏈接  -  聯(lián)系我們
電子工程網(wǎng) © 版權所有   京ICP備16069177號 | 京公網(wǎng)安備11010502021702
快速回復 返回頂部 返回列表
主站蜘蛛池模板: 性88分钟在线播放 | 拍真实国产伦偷精品 | 西瓜影院在线观看理论片 | 一级片免费在线观看 | 麻豆国产一区二区在线观看 | 视频在线精品 | 中文在线最新版天堂8 | 久久国产精品自线拍免费 | 免费人成网站免费看视频 | 国内精品视频在线播放 | 亚洲情人网 | 亚洲欧洲精品视频在线观看 | 亚洲最大福利网 | 一级毛片特黄久久免费看 | 四虎国产欧美成人影院 | 青青草97| 国产精品13页 | 大陆一级毛片国语对白 | 欧美激情二区三区 | 久久综合九色综合精品 | 欧美日韩免费播放一区二区 | 四虎永久在线日韩精品观看 | 日韩高清在线日韩大片观看网址 | 欧美视频一区 | 91免费精品国偷自产在线在线 | 一级毛片在播放免费 | 亚洲日韩中文字幕 | 91碰视频| 青青草香蕉 | 中文字幕大看蕉永久网下载 | 免费老色鬼永久视频网站 | 久久国产热视频 | 欧美中文一区 | 男女视频在线观看网站 | 国产午夜精品一区二区 | 欧美日韩亚洲一区二区精品 | 青青五月| 国产黄色在线免费观看 | 九九热视频免费在线观看 | 4hu四虎永久免在线视看 | 国产精品日韩欧美一区二区 |