常見的C語言筆試題1

轉載於:http://blog.chinaunix.net/uid-20583479-id-1920068.html

1.1
   #ifdef NDEBUG
    #define TRACE(S) S
   #else
    #define TRACE(S) printf("%s;\n", #S); S
   #endif

問:以上TRACE()宏的作用是什麼?

答:這道題主要考察#的功能,S是一個表達式。TRACE()的作用就是在DEBUG狀態下,計算表達式S的值之前先打印S。 

1.2 #error的作用?

答:#error用於向編譯器報錯,並輸出它後面帶的錯誤信息。例如: 
#ifndef SOMETHING 
#error SOMETHING not defined! 
#endif 
如果在這段代碼之前未定義過SOMETHING,則在編譯時出錯,並給出"SOMETHING not defined!"的錯誤信息

1.3 定義一個宏,求出給定數組中的元素的個數
#define NELEMENTS(array) ??

答:#define NELEMENTS(array) (sizeof(array) / sizeof((array)[0])) 

1.4 定義一個宏,求出給定結構中給定成員的偏移量
#define OFFSET(structure, member) ??

答: #define OFFSET(structure, member) ((int) &(((structure *)0)->member)) 
【2 數據聲明和定義】

給定以下類型的變量a的定義式:

a) An integer
b) A pointer to an integer
c) A pointer to a pointer to an integer
d) An array of 10 integers
e) An array of 10 pointers to integers
f) A pointer to an array of 10 integers
g) A pointer to a <I>function</I> that takes an integer as an argument and returns an integer
h) An array of ten pointers to <I>function</I>s that take an integer argument and return an integer

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

【3 複雜類型(1)】

有如下表達式:

   char (*(*x())[])();

請用文字描述x是什麼。

答:這道題來自"The C Programming Language"中的一個例子。 
首先,確定標識符:x 
x是一個函數,沒有參數:x() 
返回值是一個指針:*x() 
這個指針指向一個數組:(*x())[] 
數組中的每個元素是指針:*(*x())[] 
指向一個不帶參數的函數:(*(*x())[])() 
函數的返回值是char:char (*(*x())[])() 
這裏,要知道*、()和[]的優先級。 


【4 複雜類型(2)】

jmp_buf的定義:

   typedef struct _jmp_buf
   {
    REG_SET  reg;
    int      extra[3];
   } jmp_buf[1];

setjmp函數的原型:

   extern int setjmp (jmp_buf __env);

問:調用setjmp時傳遞__env的內容,還是傳遞指針?

答:這個定義有點怪,它的意思是:jmp_buf這種類型是一個數組,只有一個元素,元素類型爲struct{...}。數組名作爲函數參數時,應該是傳遞地址/指針。 


【5 頭文件】

問:爲什麼標準頭文件都有類似以下的結構?

   #ifndef __INCvxWorksh
   #define __INCvxWorksh

   #ifdef __cplusplus
   extern "C" {
   #endif

   /*...*/

   #ifdef __cplusplus
   }
   #endif

   #endif /* __INCvxWorksh */

答:在編譯源文件時,C編譯器和C++編譯器都會對符號(函數或變量)名作某些修正,但兩者採用的修正方法不同,所以兩者生成的目標文件不能互相鏈接。在C++中使用extern "C"可以讓C++符號獲得C鏈接特性。由於C++編譯器會自動定義__cplusplus宏,所以在C語言頭文件中採用這種結構可以保證無論使用何種編譯器,生成的目標文件都具有C鏈接特性,能夠與標準C編譯器所生成的目標文件相鏈接。 

【6 static關鍵字】

請說出static關鍵字的3種用處:
(1)用於全局變量;
(2)用於局部變量;
(3)用於函數。

/* file.c */
static int a;
int b;

static int fn()
{
 static int x;
 int y;
}

答:(1)用於全局變量:外部靜態變量,只能在本源文件中被引用,不能被其它源文件所引用。 
(2)用於局部變量:局部靜態變量,在函數返回後存儲單元不釋放;下一次調用該函數時,該變量爲上次函數返回時的值。 
(3)用於函數:內部函數,只能被本源文件中的函數所調用,不能被其它源文件調用。 


【7 const關鍵字】

7.1 const關鍵字的意義是什麼?

答:const關鍵字在C語言中用於聲明"常變量",其值不可修改,但具有確定的數據類型。C編譯器總是爲其分配相應的存儲單元。 
在C++中,const關鍵字用於聲明常量,C++編譯器視具體情況決定是爲其分配存儲單元還是僅將其作爲編譯期間的常量。 


7.2 解釋以下的變量定義:

const int a1;
int const a2;
const int *a3;
int * const a4;
int const * const a5;

答:const int a1; a1是整型常量。 
int const a2; a2是整型常量。等同於const int a2; 
const int *a3; a3是指針(a3是可變的),指向一個整型常量。等同於int const *a3; 
int * const a4; a4是常量指針(a4不可變),指向一個整型變量。 
int const * const a5; a5是常量指針(a5不可變),指向一個整型常量。等同於const int * const a5; 

【8 volatile關鍵字】

8.1 volatile意義?例如
volatile int *p;

答:volatile關鍵字用於聲明內存映射的易失型變量,這類變量的值隨時可能由於某種編譯器所不知道的原因(例如,外部設備對其寫入)所改變,所以編譯器在進行代碼優化時不能對其做任何的假設和依賴。 

8.2 volatile能和const一起使用嗎?例如
volatile const int *p;

答:volatile可以和const一起使用,不過很少見。 
const關鍵字的意思是限制編程者自己不能修改變量的值;兩者並不矛盾。 
例如一個內存映射的、只讀的硬件寄存器,假設它的地址是p,則可以這樣聲明:volatile const UINT32 *p; 

【9 sizeof()】

有以下定義:

   char *pmsg = "A";
   char msg[] = "A";
   char ch    = 'A';

問:
sizeof(pmsg) = ?
sizeof(msg)  = ?
sizeof(“A”)  = ?
sizeof(ch)   = ?
sizeof(‘A’)  = ? (在C++中等於多少?)

void f(char param[100])
{
// sizeof(param) = ?
}

答:sizeof(pmsg) = 指針變量的長度 
sizeof(msg) = 2 (字符數組的長度) 
sizeof("A") = 2 (字符串的長度) 
sizeof(ch) = 1 (字符變量的長度) 
sizeof(‘A’) = 整型變量的長度 (在C語言中,字符常量的數據類型實際上是int;在C++中,它的數據類型是char,從而原式等於1) 
sizeof(param) = 指針變量的長度 (數組名作參數時,傳遞的是數組的起始地址) 

【10 字符串】

有以下代碼

   char *pmsg = "hello, world!";
   strcpy(pmsg, "hi, there.");

試評論該代碼。

答:這種寫法是和編譯器&操作系統相關的,所以不應當這樣寫。在WIN2K+VC環境下debug程序時會出現異常。 
不過這樣寫,編譯器不會報錯。按道理,"hello..."的類型是const char [N],它是不能賦值給char *的, 
因爲會丟失常量屬性。但在const關鍵字成爲C標準之前,大家都這樣寫程序,所以char *pmsg = "hello..." 
這種寫法被給予特別豁免,即使在C++中也是如此,在"The C++ Programming Language"的附錄裏對此有討論。 

"hello, world!"是字符串常量(string literal),它的類型是const char [N],N爲字符串的長度(包括結尾的0)。 
"The C Programming Language"指出,寫字符串常量的結果是未定義的(undefined)。所以在有些平臺(操作系統+編譯器) 
上程序不會出錯,而在其它平臺上程序出現異常。 

GNU手冊裏這樣說: 
Writing into string constants is a very bad idea; "constants" should be constant. 
不過在GNU中它提供另外的選擇:使用-fwritable-strings進行編譯就可以。 

那麼,爲什麼不允許修改字符串常量呢(它不也在內存中嗎)? 
這可能和另外一個特點有關,即重複字符串的合併。現在的編譯器應該會主動幫助我們合併程序中相同的字符串常量 
以節省內存。如果string literal可寫,就會出現問題。例如: 
void foo() 

printf("%s\n", "how are you?"); 

void bar() 

char *p = "how are you?"; 
strcpy(p, "WHO ARE YOU?"); 

調用foo()當然會打印"how are you"。但如果編譯器合併字符串,那麼先調用bar(),再調用foo(),foo()打印的就是 
"WHO ARE YOU?"。這當然不是我們想要的結果。 
另外一方面,這樣寫也有問題(確實有人這麼寫): 
if (func() == "something") 
... 
func()是: 
char *func() 

... 
return "something"; 

這就假設編譯器一定會幫我們合併字符串,然而那也不一定。 


【11 混合運算】

有以下代碼:

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

請問調用foo()的輸出?

答:輸出"> 6"。 
混合運算時的數據類型轉換次序:int --> unsigned --> long --> double。 
另外,char和short必定轉換爲int,float必定轉換爲double。

【12 內存訪問】

有以下代碼:

void fn()
{
  int a[100];
  int *p;

    p = (int *)((unsigned int)a + 1);
    printf(“p=0x%x\n”, *p);
}

試評論以上代碼。

答:p = (int *)((unsigned int)a + 1); 
代碼的意圖是想使p指向數組的第二個元素,但通常的寫法是:p=a+1。這裏存在這樣的問題:a是個常量地址, 
a+1指向下一個數組元素,而((unsigned int)a + 1)指向下一個內存地址。如果地址是字節計數的,則p指向的 
是數組第一個元素的第二個字節。還有一個效果就是:在RISC上該printf語句會出異常,因爲不允許非對齊訪問 
(mis-aligned access)。對齊訪問就是訪問2字節變量的地址要能被2整除,4字節變量的地址要能被4整除,etc。  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章