1. 關鍵字volatile有什麼含意?並給出三個不同的例子。
一個定義為volatile的變數是說這變數可能會被意想不到地改變,這樣,編譯器就不會去假設這個變數的值了。精確地說就是,最佳化器在用到這個變數時必須每次都小心地重新讀取這個變數的值,而不是使用儲存在寄存器裡的備份。下面是volatile變數的幾個例子:
(1)平行裝置的硬體寄存器(如:狀態寄存器)
(2)一個中斷服務子程式中會訪問到的非自動變數(Non-automatic variables)
(3) 多線程應用中被幾個任務共用的變數
回答不出這個問題的人是不會被僱傭的。我認為這是區分C程式員和嵌入式系統程式員的最基本的問題。搞嵌入式的傢伙們經常同硬體、中斷、RTOS等等打交道,所有這些都要求用到volatile變數。不懂得volatile的內容將會帶來災難。
- 一個參數既可以是const還可以是volatile嗎?解釋為什麼。
答:是的。一個例子是唯讀狀態寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程式不應該試圖去修改它。
- 一個指標可以是volatile 嗎?解釋為什麼。
答:是的。儘管這並不很常見。一個例子是當一個中斷服務子程式修該一個指向一個buffer的指標時。
- 下面的函數有什麼錯誤:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
答:這段代碼有點變態。這段代碼的目的是用來返指標*ptr指向值的平方,但是,由於*ptr指向一個volatile型參數,編譯器將產生類似下面的代碼:
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;
}
2. 嵌入式系統經常具有要求程式員去訪問某特定的記憶體位置的特點。在某工程中,要求設定一絕對位址為0x67a9的整型變數的值為0xaa66。編譯器是一個純粹的ANSI編譯器。寫代碼去完成這一任務。
這一問題測試你是否知道為了訪問一絕對位址把一個整型數強制轉換(typecast)為一指標是合法的。這一問題的實現方式隨著個人風格不同而不同。典型的類似代碼如下:
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;
A more obscure approach is:
一個較晦澀的方法是:
*(int * const)(0x67a9) = 0xaa55;
3. 運算式中如果同時存在有符號數和無符號數的話,有符號數會被提升為無符號數
擷取數組長度
template <typename T, size_t N>
size_t arrarysize(T (&array)[N]) { return N; }
4. 數組指標與多維陣列的理解
| |
一般形式 |
typedef定義 |
指標數組 |
int *a[10]; |
typedef int *t;
t a[10]; |
數組指標 |
int (*a)[10]; |
typedef int t[10];
t *a; |
例如:
(1)int a[10];
int (*pa)[10]=&a;
a是一個數組,在&a這個運算式中,數組名做左值,取整個數組的首地址賦給指標pa。注意,&a[0]表示數組a的首元素的首地址,而&a表示數組a的首地址,顯然這兩個地址的數值相同,但這兩個運算式的類型是兩種不同的指標類型,前者的類型是int *,而後者的類型是int (*)[10]。*pa就表示pa所指向的數組a,所以取數組的a[0]元素可以用運算式(*pa)[0]。注意到*pa可以寫成pa[0],所以(*pa)[0]這個運算式也可以改寫成pa[0][0],pa就像一個二維數組的名字,它表示什麼含義呢?下面把pa和二維數組放在一起做個分析。
(2)int a[5][10];
int (*pa)[10] = &a[0];
則pa[0]和a[0]取的是同一個元素,唯一比原來複雜的地方在於這個元素是由10個int組成的數組,而不是基本類型。這樣,我們可以把pa當成二維數組名來使用,pa[1][2]和a[1][2]取的也是同一個元素,而且pa比a用起來更靈活,數組名不支援賦值、自增等運算,而指標可以支援,pa++使pa跳過二維數組的一行(40個位元組),指向a[1]的首地址。
(3) int a[2][3][5];
int (*pa)[5]=a[0];
int *ppa=a[0][0];
a是表示a+0,是指向第一個二維數組的指標,
a[0]表示a[0]+0,是指向第一個一維數組的指標
a[0][0]表示a[0][0]+0,是指向一維數組的第一個元素的指標
a[0][0][0]表示int,是一維數組的第一個元素
5. C++中的多態的理解
C++中多態的實現要求基類的相應的函數要定義為virtual。
可以這樣理解:我們有這樣的需求,就是要用子類的方法去覆蓋基類的某些方法,所以,為了實現這個目標,第一步就是子類與基類之間的函數的原型是一樣的。第二步就是將基類中的該方法聲明為virtual。這樣做其實是為了通知編譯器,說這個函數我要實行多態,你在產生子類的虛函數表(v-table)的時候注意,要用子類的函數的地址替代(覆蓋)基類的函數的地址。
7. C語言注意事項
(1)scanf和printf的傳回值
scanf()函數的傳回值為按照要求格式讀取量的個數。當讀取錯誤是(即都沒讀取成功),則返回0;當遇到錯誤時則返回EOF;
printf()函數的傳回值為輸出量的個數。傳回值都為一個整型數
(2)sizeof的容易被忽略的用法
sizeof可以返回字串常量的長度
如:sizeof("AB")=3
char s[]="abc";sizeof(s)=4
下面是網上找到的:
對於char str[] = "abcdef";就有sizeof(str) == 7,因為str的類型是char[7],
也有sizeof("abcdef") == 7,因為"abcdef"的類型是const char[7]。
對於char *ptr = "abcdef";就有sizeof(ptr) == 4,因為ptr的類型是char*。
對於char str2[10] = "abcdef";就有sizeof(str2) == 10,因為str2的類型是char[10]。
對於void func(char sa[100],int ia[20],char *p);
就有sizeof(sa) == sizeof(ia) == sizeof(p) == 4,因為sa的類型是char*,ia的類型是int*,p的類型是char*。
(3) C語言中如何判斷檔案是否存在
用函數access,標頭檔是io.h,原型:
int access(const char *filename, int amode);
amode參數為0時表示檢查檔案的存在性,如果檔案存在,返回0,不存在,返回-1。
這個函數還可以檢查其它檔案屬性:
06 檢查讀寫權限
04 檢查讀許可權
02 檢查寫入權限
01 檢查執行許可權
00 檢查檔案的存在性
在UNIX和VC下實驗成功。
好處是 :fopen(..,"r")不好,當無讀許可權時一不行了。而這個就算這個檔案沒有讀許可權,也可以判斷這個檔案存在於否:存在返回0,不存在返回-1
#include <stdio.h>
int main()
{
printf ("%d",access("111",0));
}
C語言中建立一個目錄system("mkdir D:\\abc\def");
8. C++語言中extern C的作用
因為C語言和 C++ 語言的編譯規則不一樣,所以要告訴系統哪些函數是用 C 方式編譯,哪些函數需要用 C++ 方式編譯。 如果你不加 extern "C" ,在編譯時間,系統會提示找不到此函數。
extern "C"表示編譯產生的內部符號名使用C約定
例如: int Fun(int i,int j)
C:_Fun
C++:_Fun_int_int
具體產生什麼可能與編譯器有關 。由於C++支援重載,而重載是在編譯期確定的,所以C++必須在內部符號名上區分各重載函數,所以就將參數類型加在函數名後
9.C++中為什麼用模板類。
答:(1)可用來建立動態增長和減小的資料結構
(2)它是類型無關的,因此具有很高的可複用性。
(3)它在編譯時間而不是運行時檢查資料類型,保證了型別安全
(4)它是平台無關的,可移植性
(5)可用於基礎資料型別 (Elementary Data Type)
10. C++中近堆與遠堆問題
應該是near 和 far指標的問題。
產生原因是這樣的:如果動態建立的資料量比較大,用一個資料區段(一般是64K,此時段指標不變,位移量指標在16bit內變化)放不下的時候,需要重新開闢一個資料區段供存放更多的資料,此時稱原來的堆部分為近堆,改變段地址後的新的資料區段所在的堆部分稱為遠堆
PC機得儲存空間地址是由段地址和位移地址組合而成,每一段不能超過64k位元組地址,因而統一個段內的地址存取,用偏 移地址就可以實現,因段地址寄存器所存的段地址是不變。當用指標時,只16未就夠了,這一類就是near指標。當要在另一個段內取資料,就要跨越段,既要 指明存取段的段地址和位移地址,這時段地址寄存器所存段地址要改變,在使用指標指向另一個段內地址時,就要用32位表示,就是far指標了。
由於dos是16位的作業系統,故程式碼受到segment的限制,near指標最多隻能指向64k以內的代碼或資料。而far則可以跨段定址,如果我 沒記錯的話好像還有huge類型的遠指標。但是由於watcom,djgpp等dos下的編譯器,以及所有windows下的編譯器都是32位的顧沒有 64k的限制,指標也就不分遠近了