嵌入式入職的要求可以說挺高的,下面是我總結的嵌入式經典筆試題,希望可以幫助到大家。
工具/原料
網絡 WORD
方法/步驟
下面是個人認為嵌入式筆試中,答題捲上頻率比較高的知識點和案例,如果不對之處,希望大家多多指點批評
第一章C語言常見問題
1.1關作鍵字static的用
這個簡單的問題很少有人能回答完全。在C語言中,關鍵字static有三個明顯的作用:
1)在函數體內,一個被聲明為靜態的變量在這一函數被調用過程中維持其值不變(該變量存放在靜態變量區)。
2)在模塊內(但在函數體外),一個被聲明為靜態的變量可以被模塊內所用函數訪問,但不能被模塊外其它函數訪問。它是一個本地的全局變量。
3)在模塊內,一個被聲明為靜態的函數只可被這一模塊內的其它函數調用。那就是,這個函數被限制在聲明它的模塊的本地範圍內使用。
大多數應試者能正確回答第一部分,一部分能正確回答第二部分,但是很少的人能懂得第三部分。這是一個應試者的嚴重的缺點,
因為他顯然不懂得本地化數據和代碼範圍的好處和重要性。
考點:在嵌入式系統中,要時刻懂得移植的重要性,程序可能是很多程序員共同協作同時完成,在定義變量及函數的過程,可能會重名,這給系統的集成帶來麻煩,因此保證不衝突的辦法是顯示的表示此變量或者函數是本地的,static即可。
在Linux的模塊編程中,這一條很明顯,所有的函數和全局變量都要用static關鍵字聲明,將其作用域限制在本模塊內部,與其他模塊共享的函數或者變量要EXPORT到內核中。
1)設置變量的存儲域,函數體內static變量的作用範圍為該函數體,不同於auto變量,該變量的內存只被分配一次,因此其值在下次調用時仍維持上次的值;
(2)限制變量的作用域,在模塊內的static全局變量可以被模塊內所用函數訪問,但不能被模塊外其它函數訪問;
(3)限制函數的作用域,在模塊內的static函數只可被這一模塊內的其它函數調用,這個函數的使用範圍被限制在聲明它的模塊內;
(4)在類中的static成員變量意味著它為該類的所有實例所共享,也就是說當某個類的實例修改了該靜態成員變量,其修改值為該類的其它所有實例所見;
(5)在類中的static成員函數屬於整個類所擁有,這個函數不接收this指針,因而只能訪問類的static成員變量。
1.2程序運行時的內存分配
一個由C/C++編譯的程序佔用的內存分為以下幾個部分
1、棧區(stack)—由編譯器自動分配釋放,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧。
2、堆區(heap)—一般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收。注意它與數據結構中的堆是兩回事,分配方式類似於鏈表。
3、全局區(靜態區)(static)—全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。 - 程序結束後由系統釋放。
4、文字常量區—常量字符串就是放在這裡的。程序結束後由系統釋放
5、程序代碼區—存放函數體的二進制代碼。
二、例子程序
這是一個前輩寫的,非常詳細
//main.cpp
int a = 0;全局初始化區
char *p1;全局未初始化區
main()
{
int b;棧
char s[] ="abc";棧
char *p2;棧
char *p3 ="123456"; 123456\0在常量區,p3在棧上。
static int c =0;全局(靜態)初始化區
p1 = (char*)malloc(10);
p2 = (char*)malloc(20);分配得來得10和20字節的區域就在堆區。
strcpy(p1,"123456"); 123456\0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。
}
1.3堆和棧的區別
1)申請方式
stack:
由系統自動分配。例如,聲明在函數中一個局部變量int b;系統自動在棧中為b開闢空間
heap:
需要程序員自己申請,並指明大小,在c中malloc函數
如p1 = (char *)malloc(10);
在C++中用new運算符
如p2 = (char *)malloc(10);
但是注意p1、p2本身是在棧中的。
堆是向上增長而棧是向下增長
2)堆棧的進一步討論
在學習《深入理解計算機系統》中鏈接這一章中,數據講一個可執行文件包含多個段。在Linux系統中代碼段總是從0x08048000處開始,數據段在接下來的4KB對齊的地址處,運行時堆在接下來的讀寫段之後的第一個4KB對齊的地址處,
並通過調用malloc庫網上增長,開始於地址0x40000000處的段是為共享庫保留的,用戶棧總是從地址0xbfffffff處開始,並向下增長,從棧的上部開始於地址0xc0000000處的段是為操作系統駐留存儲器部分的代碼和數據保留的。如下圖:
下面通過代碼來測試:
#include
int a1=0;
staticintsa=0;
int a2=0;
int b;
int main()
{
int c1=1;
int c2;
int *d=(int *)malloc(sizeof(int)*10);
int *e=(int *)malloc(sizeof(int)*10);
staticint f=0;
if(d==NULL)
{
printf("malloc error");
return -1;
}
printf("a1:%p\n",&a1);
printf("sa:%p\n",&sa);
printf("a2:%p\n",&a2);
printf("b:%p\n",&b);
printf("c1:%p\n",&c1);
printf("c2:%p\n",&c2);
printf("d:%p\n",&d);
printf("*d:%p\n",d);
printf("e:%p\n",&e);
printf("*e:%p\n",e);
printf("f:%p\n",&f);
printf("%p\n",&"123");
free(d);
return 0;
}
分析如下:
a1,a2,sa,f是按序存放,且全局共有非靜態變量和靜態變量是分開存放的,緊接著存放的是全局非初始化變量b,由於是int型,故地址相差4.
c1,c2,d,e這幾個變量存儲在用戶棧空間。主要區分d、e和d、e申請內存之間的區別。
*d,*e代表申請的內存,地址是增大方向,即地址是向上增長的,為堆中申請的數據。
“123”為常量,地址0x80486da應該在只讀段的範圍內,屬於rodate數據。
故可知:C程序中,全局變量和靜態變量存儲在讀寫段,常量存儲在制度段,動態申請的內容是在堆上,局部變量在運行時棧中。
1.4關鍵字const的含義
只要能說出const意味著"只讀"就可以了。儘管這個答案不是完全的答案,但我接受它作為一個正確的答案。(如果你想知道更詳細的答案,仔細讀一下Saks的文章吧。)
如果應試者能正確回答這個問題,我將問他一個附加的問題:下面的聲明都是什麼意思?
Const只是一個修飾符,不管怎麼樣a仍然是一個int型的變量
constint a;
int const a;
constint *a;
int * const a;
int const * aconst;
本質:const在誰後面誰就不可修改,const在最前面則將其後移一位即可,二者等效
前兩個的作用是一樣,a是一個常整型數。第三個意味著a是一個指向常整型數的指針(也就是,指向的整型數是不可修改的,但指針可以,此最常見於函數的參數,當你只引用傳進來指針所指向的值時應該加上const修飾符,程序中修改編譯就不通過,可以減少程序的bug)。
第四個意思a是一個指向整型數的常指針(也就是說,指針指向的整型數是可以修改的,但指針是不可修改的)。最後一個意味著a是一個指向常整型數的常指針(也就是說,指針指向的整型數是不可修改的,同時指針也是不可修改的)。
const關鍵字至少有下列n個作用:
(1)欲阻止一個變量被改變,可以使用const關鍵字。在定義該const變量時,通常需要對它進行初始化,因為以後就沒有機會再去改變它了;
(2)對指針來說,可以指定指針本身為const,也可以指定指針所指的數據為const,或二者同時指定為const;
(3)在一個函數聲明中,const可以修飾形參,表明它是一個輸入參數,在函數內部不能改變其值;
(4)對於類的成員函數,若指定其為const類型,則表明其是一個常函數,不能修改類的成員變量;
(5)對於類的成員函數,有時候必須指定其返回值為const類型,以使得其返回值不為“左值”。例如:
constclassAoperator*(const classA& a1,const classA& a2);
operator*的返回結果必須是一個const對象。如果不是,這樣的變態代碼也不會編譯出錯:
classA a, b, c;
(a * b) = c; //對a*b的結果賦值
操作(a * b) = c顯然不符合編程者的初衷,也沒有任何意義。
1.5關鍵字volatile含義
一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器裡的備份。下面是volatile變量的幾個例子:
1.6什麼是野指針?產生的原因?
“野指針”不是NULL指針,是指向“垃圾”內存(不可用內存)的指針。“野指針”是很危險的,if無法判斷一個指針是正常指針還是“野指針”。有個良好的編程習慣是避免“野指針”的唯一方法。
野指針的成因主要有三種:
一、指針變量沒有被初始化。指針變量在創建的同時應當被初始化,要麼將指針設置為NULL,要麼讓它指向合法的內存。
二、指針p被free或者delete之後,沒有置為NULL,讓人誤以為p是個合法的指針。
三、指針操作超越了變量的作用範圍。比如不要返回指向棧內存的指針或引用,因為棧內存在函數結束時會被釋放。比如說某個地址的生命期,使用一個沒有生命期的指針是非常危險的。
1.7 sizeof和strlen區別
一、sizeof
sizeof(...)是運算符,在頭文件中typedef為unsigned int,其值在編譯時即計算好了,參數可以是數組、指針、類型、對象、函數等。它的功能是:獲得保證能容納實現所建立的最大對象的字節大小。
二、strlen
strlen(...)是函數,要在運行時才能計算。參數必須是字符型指針(char*)。當數組名作為參數傳入時,實際上數組就退化成指針了。
它的功能是:返回字符串的長度。該字符串可能是自己定義的,也可能是內存中隨機的,該函數實際完成的功能是從代表該字符串的第一個地址開始遍歷,直到遇到結束符NULL。返回的長度大小不包括NULL。
三、舉例:
eg1、char arr[10] = "What?";
intlen_one =strlen(arr);
intlen_two =sizeof(arr);
cout<
輸出結果為:5 and 10
點評:sizeof返回定義arr數組時,編譯器為其分配的數組空間大小,不關心裡面存了多少數據。strlen只關心存儲的數據內容,不關心空間的大小和類型。其中\0不計入strlen計數中去。
第二章 C語言的常見筆試題
2.1 在某工程中,要求設置一絕對地址為
嵌入式系統經常具有要求程序員去訪問某特定的內存位置的特點。在某工程中,要求設置一絕對地址為0x67a9的整型變量的值為0xaa66。編譯器是一個純粹的ANSI編譯器。寫代碼去完成這一任務。
int*ptr;
ptr =(int *)0x67a9;
*ptr =0xaa55;
2.2 寫出strcpy,strcmp,strlen,memcpy函數源碼的實現。
char * strcpy( char*strDest, const char *strSrc )
{
assert( (strDest !=NULL) && (strSrc != NULL) );
char *address =strDest;
while( (*strDest++= * strSrc++) !=‘\0’ );
return address;
}
intstrlen( constchar *str ) //輸入參數const
{
assert(strt != NULL); //斷言字符串地址非0
intlen;
while( (*str++) !='\0' )
{
len++;
}
returnlen;
}
2.3 請寫一個C函數,判斷大小端。
所謂的大端模式,是指數據的高位,保存在內存的低地址中,而數據的低位,保存在內存的高地址中
所謂的小端模式,是指數據的高位保存在內存的高地址中,而數據的低位保存在內存的低地址中
intcheckCPU()
{
{
union w
{
int a;
char b;
} c;
c.a = 1;
return (c.b == 1);
}
}
2.4用變量a 給出下面的定義
a) 一個整型數(An integer)
b)一個指向整型數的指針(A pointer to an integer)
c)一個指向指針的的指針,它指向的指針是指向一個整型數(A pointer to a pointer to anintege)r
d)一個有10 個整型數的數組(An array of 10 integers)
e) 一個有10 個指針的數組,該指針是指向一個整型數的。(An array of 10 pointers tointegers)
f) 一個指向有10 個整型數數組的指針(A pointer to an array of 10integers)
g) 一個指向函數的指針,該函數有一個整型參數並返回一個整型數(A pointer to a function that takes an integer asan argument
and returns an integer)
h)一個有10 個指針的數組,該指針指向一個函數,該函數有一個整型參數並返回一個整型數(An array of ten pointers tofunctions t
hat take an integer argument and return aninteger )
答案是:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to aninteger
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 10integers
g) int (*a)(int); // A pointer to a function athat takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointersto functions that take an integer argument and return an integer
2.5 位操作
嵌入式系統總是要用戶對變量或寄存器進行位操作。給定一個整型變量a,寫兩段代碼,第一個設置a的bit 3,第二個清除a的bit 3。在以上兩個操作中,要保持其它位不變。
#define BIT3 (0x1 << 3)
staticint a;
void set_bit3(void) {
a = BIT3;
}
void clear_bit3(void) {
a&= ~BIT3;
}#define BIT3 (0x1 << 3)
staticint a;
void set_bit3(void) {
a = BIT3;
}
void clear_bit3(void) {
a&= ~BIT3;
}
2.6 與“零值”比較
分別給出BOOL,int,float,指針變量與“零值”比較的 if語句(假設變量名為var)
解答:
BOOL 型變量:if(!var)
int型變量:if(var==0)
float 型變量:
const floatEPSINON = 0.00001;
if ((x >= -EPSINON) && (x <= EPSINON)
指針變量:if(var==NULL)
2.7 對齊方式
·使用偽指令#pragma pack (n),編譯器將按照n 個字節對齊;
·使用偽指令#pragma pack (),取消自定義字節對齊方式。
注意:如果#pragma pack (n)中指定的n 大於結構體中最大成員的size,則其不起作用,結構體仍然按照size 最大的成員進行對界。
另外,通過__attribute((aligned (n)))也可以讓所作用的結構體成員對齊在n 字節邊界上。
2.8 malloc分配的是物理地址還是虛擬地址
物理地址
2.9 不是使用變量,調換兩個變量的值
int a=10,b=20;a=a+b;b=a-b;a=a-b;結果:a=20;b=10;
第三章 C++常見問題
1. memcpy源碼
void * memcpy (
void * dst,
const void * src,
size_t count ){
void * ret = dst;
if((dst == NULL) (src == NULL) (count== 0))
return NULL;
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
return(ret);
}
也許這並不是最佳答案,可是我也懶不想寫太多,至少應該轉換成int這樣可以提供效率。
2. 冒泡排序
#define LEN 8
int a[LEN]={5,4,3,2,1,7,6,0};
intbubble_new_sort(void)
{
int i=0; int j = 0; int k = 8; int flage = 1; int tmp =0;
for(i=1;(i
flage = 0;
for(j=0;j
if(a[j]>a[j+1]){
tmp =a[j];
a[j] = a[j+1];
a[j+1] = tmp;
flage=1 ;
}
}
printf("i = %d : %d,%d,%d,%d,%d,%d,%d,%d\n",i,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]);
}
printf("%d,%d,%d,%d,%d,%d,%d,%d\n",a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]);
return 0;
}
3. 進程和線程的區別
進程是表示資源分配的基本單位,又是調度運行的基本單位。
線程是進程中執行運算的最小單位,亦即執行處理機調度的基本單位。
4. const char * char * const
5. 請問C++的類和C 裡面的struct 有什麼區別?
c++中的類具有成員保護功能,並且具有繼承,多態這類oo特點,而c 裡的struct 沒有
6. 交換兩個數,不用第三塊兒內存:
int a = 2;
int b = 3
a = a + b;
b = a - b;
a = a - b;
或者:
a = a ^ b;
b = a ^ b;
a = a ^ b;
7. 二分查找思想
int binary_search(int* a, int len, int goal)
{
int low = 0;
int high = len - 1;
while(low <= high)
{
int middle = (low +high)/2;
if(a[middle] == goal)
return middle; //在左半邊
else if(a[middle] >goal)
high = middle - 1;//在右半邊
else
low = middle + 1;
}
return -1; //沒找到
}
8. free
一個指針被free 後 內存被釋放,但並不是空的,指向的位置不確定。
第五章 C&C++
5.1 C和C++做程序的區別?
1.C是一個結構化語言,它的重點在於算法和數據結構。C程序的設計首要考慮的是如何通過一個過程,對輸入(或環境條件)進行運算處理得到輸出(或實現過程(事務)控制)。
2.C++,首要考慮的是如何構造一個對象模型,讓這個模型能夠契合與之對應的問題域,這樣就可以通過獲取對象的狀態信息得到輸出或實現過程(事務)控制。所以C與C++的最大區別在於它們的用於解決問題的思想方法不一樣。之所以說C++比C更先進,是因為“設計這個概念已經被融入到C++之中”。