1. 程式人生 > >c語言 常見知識點

c語言 常見知識點

1、寫一個“標準”巨集MIN ,這個巨集輸入兩個引數並返回較小的一個

#define MIN ((A) <= (B) ? (A) : (B))

解答:

實現輸入兩個引數並返回較小的一個,應使用三目表示式。使用必須的足夠多的圓括號來保證以正確的順序進行執行和結合。

使用#define需要注意下面幾點:1)巨集的名字中不能有空格,但是在替代字串中可以使用空格。ANSI C 允許在引數列表中使用空格。
(2)用圓括號括住每個引數,並括住巨集的整體定義。
(3)用大寫字母表示巨集函式名,便於與變數區分。
(4)有些編譯器限制巨集只能定義一行。即使你的編譯器沒有這個限制,也應遵守這個限制。
(5
)巨集的一個優點是它不檢查其中的變數型別,這是因為巨集處理字元型字串,而不是實際值。 (6)在巨集中不要使用增量或減量運算子。

2、前處理器標識#error的目的是什麼?

#error 字串 => 表示產生一個錯誤資訊

解答:

#error 字串 => 表示產生一個錯誤資訊

#warning 字串 => 表示產生一個警告資訊

3、嵌入式系統中經常要用到無限迴圈,你怎麼樣用C編寫死迴圈呢?

while (1) {...}
for (;;){...}
Loop; ... goto Loop
解答:
while(邏輯表示式)
{
反覆執行的語句
}
只要邏輯表示式結果為真就反覆不停執行大括號裡的語句,直到邏輯表示式結果為假迴圈結束,只要把邏輯表示式寫成1
則迴圈成為死迴圈。 while 很好理解,下面講講 for 迴圈。 例如:for(num=1; num <10; num++); 在關鍵字for之後的圓括號中包含了由兩個分號分開的三個表示式: 第一個表示式進行初始化,它在for迴圈開始的時候執行一次,可以使用逗號為多個變數進行初始化。 第二個表示式是判斷條件,在每次執行之前都要對它進行求值。當表示式為假時,迴圈結束。 for迴圈的靈活性 (1)可以讓一個或多個表示式為空(但是不要遺漏分號)。只須確保在迴圈中包含一些能是迴圈最終結束的語句。(2)順便說一句,中間的那個控制表示式為空會被認為是真,所以下面的迴圈會永遠執行: for (; ;) printf ("hello world\n"
); (3)第一個表示式不必初始化一個變數,它也可是某種型別的 printf() 語句,要記住第一個表示式只在執行迴圈的其他部分之前被求值或執行一次。 (4for迴圈可使用逗號運算子把兩個表示式連結為一個表示式,並保證最左邊的表示式最先計算。 例如: for (n = 2, m = 0; m < 1000; n *=2) m +=n; (5)在for迴圈中使用陣列,可使用#define來指定陣列大小 例如: #define SIZE 10 for (int n = 0; n < SIZE; n++) 第三個表示式進行改變或者稱為更新,它在每次迴圈結束時進行計算。 printf ("hello world\n");

4、關鍵字static的作用是什麼?

static 修飾全域性變數
static 修飾區域性變數
static 修飾函式
解答:
(1static 修飾的全域性變數也叫靜態全域性變數,該類具有靜態儲存時期、檔案作用域和內部連結,僅在編譯時初始化一次。如未明確初始化,它的位元組都被設定為0static全域性變數只初使化一次,是為了防止在其他檔案單元中被引用;利用這一特性可以在不同的檔案中定義同名函式和同名變數,而不必擔心命名衝突。
(2static 修飾的區域性變數也叫靜態區域性變數,該類具有靜態儲存時期、程式碼作用域和空連結,僅在編譯時初始化一次。如未明確初始化,它的位元組都被設定為0。函式呼叫結束後儲存區空間並不釋放,保留其當前值。
(3static 修飾的函式也叫靜態函式,只可以在定義它的檔案中使用。

5、關鍵字const有什麼含意?

const 修飾的資料型別是指常型別,常型別的變數或物件的值是不能被更新的。或者說const意味著 只讀。
解答:
(1)在定義該const 變數時,通常需要對它進行初始化,因為以後就沒有機會再去改變它了;
(2)對指標來說,可以指定指標本身為const,也可以指定指標所指的資料為 const,或二者同時指定為const;
(3)在一個函式宣告中,const可以修飾形參,表明它是一個輸入引數,在函式內部不能改變其值;
(4)對於類的成員函式,若指定其為const 型別,則表明其是一個常函式,不能修改類的成員變數;
(5)對於類的成員函式,有時候必須指定其返回值為const 型別,以使得其返回值不為“左值”。
作用的話,可以保護被修改的東西,防止意外的修改,增強程式的健壯性。

6、關鍵字volatile有什麼含意?並給出三個不同的例子。

volatile 關鍵字是一種型別修飾符。volatile 的作用是作為指令關鍵字,確保本條指令不會因編譯器的優化而省略,且要求每次直接讀值。直接讀值是指從記憶體重新裝載內容,而不是直接從暫存器拷貝內容。
volatile 使用:
(1)並行裝置的硬體暫存器(如:狀態暫存器)
這些暫存器裡面的值是隨時變化的。如果我們沒有將這個地址強制型別轉換成 volatile,那麼我們在使用GPC1CON 這個暫存器的時候, 會直接從 CPU 的暫存器中取值。因為之前GPC1CON 被訪問過,也就是之前就從記憶體中取出 GPC1CON 的值儲存到某個暫存器中。之所以直接從暫存器中取值,而不去記憶體中取值,是因為編譯器優化程式碼的結果(訪問 CPU暫存器比訪問 RAM 快的多)。用 volatile 關鍵字對 0xE0200080 進行強制轉換,使得每一次訪問 GPC1CON 時,執行部件都會從 0xE0200080 這個記憶體單元中取出值來賦值給 GPC1CON 。
(2)一箇中斷服務子程式中會訪問到的非自動變數
由於訪問暫存器的速度要快過RAM,所以編譯器一般都會作減少存取外部RAM的優化。
(3)多執行緒應用中被幾個任務共享的變數
當兩個執行緒都要用到某一個變數且該變數的值會被改變時,應該用 volatile 宣告,該關鍵字的作用是防止優化編譯器把變數從記憶體裝入CPU暫存器中。如果變數被裝入暫存器,那麼兩個執行緒有可能一個使用記憶體中的變數,一個使用暫存器中的變數,這會造成程式的錯誤執行。volatile的意思是讓編譯器每次操作該變數時一定要從記憶體中真正取出,而不是使用已經存在暫存器中的值。

7、下面的程式碼輸出是什麼,為什麼?

void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts("> 6") : puts("<= 6");
}
當表示式中存在有符號型別和無符號型別時所有的運算元都自動轉換為無符號型別。因此-20變成了一個非常大的正整數,所以該表示式計算出的結果大於6。
解答:
除了有符號型別和無符號型別混合使用時自動轉換為無符號型別,較小的型別和較大的型別混合使用會被轉換成較大的型別,防止資料丟失

8、動態分配記憶體可能發生的問題是什麼?

(1)越界,理論上可以申請4G,但超過程式記憶體的大小就會返回空指標,所以要檢查返回的指標是否為空。
(2)用完記得釋放,以免記憶體洩漏。 還有使用的過程中不要指標越界,這樣會導致致命錯誤。
(3)使用 free 或 delete 釋放了記憶體後,沒有將指標設定為 NULL。導致產生“野指標”。

9、下面函式使用注意問題:

type *p;
p = (type*)malloc(n * sizeof(type));
if(NULL == p)
/*請使用if來判斷,這是有必要的*/
{
perror("error...");
exit(1);
}
.../*其它程式碼*/
free(p);
p = NULL;/*請加上這句*/
1malloc 函式返回的是 void * 型別,必須通過 (type *) 來將強制型別轉換。
2malloc 函式的實參為 sizeof(type),用於指明一個整型資料需要的大小。
3、申請記憶體空間後,必須檢查是否分配成功
4、當不需要再使用申請的記憶體時,記得釋放,而且只能釋放一次。如果把指標作為引數呼叫free函式釋放,則函式結束後指標成為野指標(如果一個指標既沒有捆綁過也沒有記錄空地址則稱為野指標),所以釋放後應該把指向這塊記憶體的指標指向NULL,防止程式後面不小心使用了它。
5、要求mallocfree符合一夫一妻制,如果申請後不釋放就是記憶體洩漏,如果無故釋放那就是什麼也沒做。釋放只能一次,如果釋放兩次及兩次以上會出現錯誤(釋放空指標例外,釋放空指標其實也等於啥也沒做,所以釋放空指標釋放多少次都沒有問題)。

10、Typedef 在C語言中頻繁用以宣告一個已經存在的資料型別的同義字。也可以用前處理器做類似的事。例如,思考一下下面的例子

#define dPS struct s *
typedef struct s * tPS;
以上兩種情況的意圖都是要定義dPS 和 tPS 作為一個指向結構s指標。哪種方法更好呢?(如果有的話)為什麼?
typedef更好
解答:
首先你要了解 typedef 和 define 的區別,巨集定義只是簡單的字串代換,是在預處理完成的,而typedef是在編譯時處理的,它不是作簡單的代換,而是對型別說明符重新命名。被命名的識別符號具有型別定義說明的功能。上面兩種情況,從形式上看這兩者相似,但在實際使用中卻不相同。
dPS p1,p2;
在巨集代換後變成: struct s* p1, p2; 定義p1為一個指向結構的指標,p2為一個實際的結構。
tPS p3,p4;
而typedef代換後,正確地定義了p3 和p4 兩個指標。
總結,typedef#define的不同之處:
1、與#define不同,typedef 給出的符號名稱僅限於對型別,而不是對值。
2typedef 的解釋由編譯器,而不是是處理器執行。
3、雖然它的範圍有限,但在其受限範圍內,typedef#define 更靈活。

11、C語言同意一些令人震驚的結構,下面的結構是合法的嗎,如果是它做些什麼?

int a = 5, b = 7, c;
c = a+++b;
上面的例子是完全合乎語法的。
#include <stdio.h>
int main (void)
{
int a = 5,b = 7,c;
c = a+++b;
printf ("c = %d\n", c);
return 0;
}
輸出結果:
c = 12
c = a+++b;
可以看做:
c = a++ + b;
或者
c = a + ++b;
兩者結果是不同的。
++ 自增運算子 為單目運算子 結合方向 是從右到左;+ 加 雙目運算子 結合方向 從左到右;++ 優先順序 高於 +。
上面的程式碼被編譯器處理成:
c = a++ + b;