問答題
用預(yù)處理指令#define 聲明一個(gè)常數(shù),用以表明1年中有多少秒(忽略閏年問題)
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
注意事項(xiàng):
1). #define 語法的基本知識(shí)(例如:不能以分號(hào)結(jié)束,括號(hào)的使用,等等)
2). 懂得預(yù)處理器將為你計(jì)算常數(shù)表達(dá)式的值,因此,直接寫出你是如何計(jì)算一年中有多少秒而不是計(jì)算出實(shí)際的值,是更清晰而沒有代價(jià)的。
3). 意識(shí)到這個(gè)表達(dá)式將使一個(gè)16位機(jī)的整型數(shù)溢出-因此要用到長整型符號(hào)L,告訴編譯器這個(gè)常數(shù)是的長整型數(shù)。
4). 如果你在你的表達(dá)式中用到UL(表示無符號(hào)長整型),那么你有了一個(gè)好的起點(diǎn)。
寫一個(gè)“標(biāo)準(zhǔn)”宏MIN,這個(gè)宏輸入兩個(gè)參數(shù)并返回較小的一個(gè)
#define MIN(A, B) ((A)<=(B) ?(A) : (B))
考查的重點(diǎn):
1). 標(biāo)識(shí)#define在宏中應(yīng)用的基本知識(shí)。這是很重要的,因?yàn)橹钡角度?inline)操作符變?yōu)闃?biāo)準(zhǔn)C的一部分,宏是方便產(chǎn)生嵌入代碼的唯一方法,對于嵌入式系統(tǒng)來說,為了能達(dá)到要求的性能,嵌入代碼經(jīng)常是必須的方法。
2). 三重條件操作符的知識(shí)。這個(gè)操作符存在C語言中的原因是它使得編譯器能產(chǎn)生比if-then-else更優(yōu)化的代碼,了解這個(gè)用法是很重要的。
3). 懂得在宏中小心地把參數(shù)用括號(hào)括起來
預(yù)處理器標(biāo)識(shí)#error的目的是什么?
編譯程序時(shí),只要遇到 #error 就會(huì)跳出一個(gè)編譯錯(cuò)誤,其目的就是保證程序是按照你所設(shè)想的那樣進(jìn)行編譯的。
嵌入式系統(tǒng)中經(jīng)常要用到無限循環(huán),你怎么樣用C編寫死循環(huán)呢?
這個(gè)問題用幾個(gè)解決方案。首選的方案是:
while(1) { }
一些程序員更喜歡如下方案:
for(;;) { }
第三個(gè)方案是用 goto
Loop:
…
goto Loop;
用變量a給出下面的定義
a) 一個(gè)整型數(shù)(An integer)
b) 一個(gè)指向整型數(shù)的指針(A pointer to an integer)
c) 一個(gè)指向指針的的指針,它指向的指針是指向一個(gè)整型數(shù)(A pointer to a pointer to an integer)
d) 一個(gè)有10個(gè)整型數(shù)的數(shù)組(An array of 10 integers)
e) 一個(gè)有10個(gè)指針的數(shù)組,該指針是指向一個(gè)整型數(shù)的(An array of 10 pointers to integers)
f) 一個(gè)指向有10個(gè)整型數(shù)數(shù)組的指針(A pointer to an array of 10 integers)
g) 一個(gè)指向函數(shù)的指針,該函數(shù)有一個(gè)整型參數(shù)并返回一個(gè)整型數(shù)(A pointer to a function that takes an integer as an argument and returns an integer)
h) 一個(gè)有10個(gè)指針的數(shù)組,該指針指向一個(gè)函數(shù),該函數(shù)有一個(gè)整型參數(shù)并返回一個(gè)整型數(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
關(guān)鍵字static的作用是什么?
在C語言中,關(guān)鍵字static有三個(gè)明顯的作用:
1). 在函數(shù)體,一個(gè)被聲明為靜態(tài)的變量在這一函數(shù)被調(diào)用過程中維持其值不變。
2). 在模塊內(nèi)(但在函數(shù)體外),一個(gè)被聲明為靜態(tài)的變量可以被模塊內(nèi)所用函數(shù)訪問,但不能被模塊外其它函數(shù)訪問。它是一個(gè)本地的全局變量。
3). 在模塊內(nèi),一個(gè)被聲明為靜態(tài)的函數(shù)只可被這一模塊內(nèi)的其它函數(shù)調(diào)用。那就是,這個(gè)函數(shù)被限制在聲明它的模塊的本地范圍內(nèi)使用。
關(guān)鍵字const是什么含意?下面的聲明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
回答:
const是一個(gè)限定關(guān)鍵字,用const定義和聲明的變量為常量,必須在定義時(shí)初始化,在其生存期內(nèi)變量的值不能改變
const修飾基本數(shù)據(jù)類型,此時(shí)這些變量為常量,不能再修改或賦值
const修飾指針,const在*前說明是指向常量的指針,指向內(nèi)容不可變; const在*之后說明指針為常指針,指針值不可變,指向內(nèi)容可變;前后都有const說明指針為指向常量的常指針,指針值和指向內(nèi)容均不可變
前兩個(gè)的作用是一樣,a是一個(gè)常整型數(shù)。第三個(gè)意味著a是一個(gè)指向常整型數(shù)的指針(也就是,整型數(shù)是不可修改的,但指針可以)。第四個(gè)意思a是一個(gè)指向整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是可以修改的,但指針是不可修改的)。最后一個(gè)意味著a是一個(gè)指向常整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是不可修改的,同時(shí)指針也是不可修改的)。
關(guān)鍵字volatile有什么含意 并給出三個(gè)不同的例子。
一個(gè)定義為volatile的變量是說這變量可能會(huì)被意想不到地改變,這樣,編譯器就不會(huì)去假設(shè)這個(gè)變量的值了。精確地說就是,優(yōu)化器在用到這個(gè)變量時(shí)必須每次都小心地重新讀取這個(gè)變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個(gè)例子:
1). 并行設(shè)備的硬件寄存器(如:狀態(tài)寄存器)
2). 一個(gè)中斷服務(wù)子程序中會(huì)訪問到的非自動(dòng)變量(Non-automatic variables)
3). 多線程應(yīng)用中被幾個(gè)任務(wù)共享的變量
嵌入式系統(tǒng)總是要用戶對變量或寄存器進(jìn)行位操作。給定一個(gè)整型變量a,寫兩段代碼,第一個(gè)設(shè)置a的bit 3,第二個(gè)清除a 的bit 3。在以上兩個(gè)操作中,要保持其它位不變。
用 #defines 和 bit masks 操作。這是一個(gè)有極高可移植性的方法,是應(yīng)該被用到的方法。最佳的解決方案如下
define BIT3 (0x1<<3)
static int a;
void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}
嵌入式系統(tǒng)經(jīng)常具有要求程序員去訪問某特定的內(nèi)存位置的特點(diǎn)。在某工程中,要求設(shè)置一絕對地址為0x67a9的整型變量的值為0xaa66。編譯器是一個(gè)純粹的ANSI編譯器,寫代碼去完成這一任務(wù)。
為了訪問一絕對地址把一個(gè)整型數(shù)強(qiáng)制轉(zhuǎn)換(typecast)為一指針是合法的。典型的類似代碼如下:
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;
也可以寫為:
*(int * const)(0x67a9) = 0xaa55;
中斷是嵌入式系統(tǒng)中重要的組成部分,這導(dǎo)致了很多編譯開發(fā)商提供一種擴(kuò)展—讓標(biāo)準(zhǔn)C支持中斷。具代表事實(shí)是,產(chǎn)生了一個(gè)新的關(guān)鍵字__interrupt。下面的代碼就使用了__interrupt關(guān)鍵字去定義了一個(gè)中斷服務(wù)子程序(ISR),請?jiān)u論一下這段代碼的。
__interrupt double compute_area (double radius)
{
double area = PI * radius * radius;
printf(” Area = %f”, area);
return area;
}
這個(gè)函數(shù)有太多的錯(cuò)誤了:
1). ISR 不能返回一個(gè)值。
2). ISR 不能傳遞參數(shù)。
3). 在許多的處理器/編譯器中,浮點(diǎn)一般都是不可重入的。有些處理器/編譯器需要讓額處的寄存器入棧,有些處理器/編譯器就是不允許在ISR中做浮點(diǎn)運(yùn)算。此外,ISR應(yīng)該是短而有效率的,在ISR中做浮點(diǎn)運(yùn)算是不明智的。
4). 與第三點(diǎn)一脈相承,printf()經(jīng)常有重入和性能上的問題。
下面的代碼輸出是什么,為什么?
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6)? puts("> 6") : puts("<= 6");
}
問題的答案是輸出是“>6”。原因是當(dāng)表達(dá)式中存在有符號(hào)類型和無符號(hào)類型時(shí)所有的操作數(shù)都自動(dòng)轉(zhuǎn)換為無符號(hào)類型。因此-20變成了一個(gè)非常大的正整數(shù),所以該表達(dá)式計(jì)算出的結(jié)果大于6
評(píng)價(jià)下面的代碼片斷:
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1's complement of zero */
對于一個(gè)int型不是16位的處理器為說,上面的代碼是不正確的。應(yīng)編寫如下:
unsigned int compzero = ~0;
常見編程題---將字符串反轉(zhuǎn)
void reserverString(char str[]){
char tmp;
int i, j;
for(i = 0; j = strlen(str)-1; i<j){
tmp = str[i];
str[i] = str[j];
str[j] = tmp;
}
}
常見編程題---判斷大小端
int main(void)
int main(void)
{
int a = 0x1234;
char b = *((char*)&a);
if (b == 0x12)
printf("BIGEND\n");
if (b == 0x34)
printf("littleEND\n");
system("PAUSE");
return 0;
}
常見編程題 ---實(shí)現(xiàn)函數(shù)memcpy(), strcpy(), strcmp(), strcat()
memcpy()
void *memcpy(void *dest, const void *src, size_t n)
{
if (NULL == dest || NULL == src)
return NULL;
char* tmp = (char*)dest;
const char* s = (const char*)src;//這里注意下
while (n--)
{
*tmp++ = *s++;
}
return dest;
}
strcpy()
char * strcpy(char *dst,const char *src)
{
if((dst==NULL)||(src==NULL))
return NULL;
char *ret = dst;
while ((*dst++=*src++)!='\0');
return ret;
}
strcmp()
char* strcat(char *dest,const char *src)
{
for (; *dest == *src; dest++, src++)//注意for循環(huán),能循環(huán)下去的條件的兩者元素對應(yīng)相等,一旦不相等立即退出
if (*dest == '\0')
return 0;//表示相等
return *dest - *src;
}
strcat
char* strcat(char *dest,const char *src)
{
char* tmp = dest;
assert((NULL != dest) && (NULL != src));
while (*tmp)
{
tmp++;
}//循環(huán)結(jié)束時(shí),指針指向字符'\0',后面會(huì)覆蓋
while (*tmp++ = *src++)
{
}
return dest;
}
常見編程題---設(shè)計(jì)函數(shù) int atoi(char *s),void itoa(int n, char s[])
{
int i, n, sign;
for (i = 0; isspace(s[i]); i++); /*跳過空白符:空格時(shí)會(huì)一直循環(huán),直到遇到不是空格*/
sign = (s[i] == '-') ? -1 : 1;//遇到的如果不是-號(hào),比如是數(shù)字,則說明不是負(fù)數(shù)
if (s[i] == '+' || s[i] == '-') /* 跳過符號(hào) */
i++;
for (n = 0; isdigit(s[i]); i++)
n = 10 * n + (s[i] - '0');
return sign * n;
}//注意判斷空格、是否是數(shù)字的函數(shù),你懂寫嗎?
void itoa(int n, char s[])
void itoa(int n, char s[])
{
int i, sign;
if ((sign = n) < 0) //記錄符號(hào)
n = -n; //使n成為正數(shù)
i = 0;
do
{ //以反序生成數(shù)字 //假設(shè)n=987654
s[i++] = n % 10 + '0'; //n % 10 + '0'表示數(shù)字對應(yīng)的字符,比如第一次的數(shù)字是4,變成字符4,是4+'0'
} while ((n /= 10) > 0); //此時(shí)n變成了98765
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}