1. 程式人生 > >嵌入式軟體工程師面試題

嵌入式軟體工程師面試題

1、int a[10]={1,2,3,4,5,6,7,8,9,0};
   int *p=&a[1];
   則p[6]等於8
2、整數陣列清零:bzero(),memset()。
3、siezof();測試變數所佔地址的位元組數
4、 main()
 {
  char *str[]={"ab","cd","ef","gh","ij","kl"};
  char *t;
  t=(str+4)[-1];
  printf("%s",t);
 }則顯示"gh"
5、小端:低位位元組資料儲存在低地址
 大端:高位位元組資料儲存在低地址
 例如:int a=0x12345678;(a首地址為0x2000)
 0x2000  0x2001  0x2002  0x2003
  0x12    0x34    0x56    0x78      大端格式
6、非同步IO和同步IO區別
 如果是同步IO,當一個IO操作執行時,應用程式必須等待,直到此IO執行完,相反,非同步IO操作在後臺執行,
 IO操作和應用程式可以同時執行,提高系統性能,提高IO流量; 在同步檔案IO中,執行緒啟動一個IO操作然後就立即進入等待狀態,直到IO操作完成後才醒來繼續執行,而非同步檔案IO中,
 執行緒傳送一個IO請求到核心,然後繼續處理其他事情,核心完成IO請求後,將會通知執行緒IO操作完成了。
7、用變數a定義
 一個整型數   int a;
 一個指向整型數的指標  int *a;
 一個指向指標的指標,它指向的指標式指向一個整型數  int **a;
 一個有10個整型數的陣列   int a[10];
 一個有10指標的陣列,該指標是指向一個整型數  int *a[10];
 一個指向有10個整型數陣列的指標   int (*a)[10];
 一個指向函式的指標,該函式有一個整型數引數並返回一個整型數   int (*a)(int);
 一個有10個指標的陣列,該指標指向一個函式,該函式有一個整型數引數並返回一個整型  int (*a[10])(int);
8、int foo(void)
{
 int i;
 char c=0x80;
 i=c;
 if(i>0)
  return 1;
 return 2;
}返回值為2;因為i=c=-128;如果c=0x7f,則i=c=127。
9、a=b*2;a=b/4;a=b%8;a=b/8*8+b%4;a=b*15;效率最高的演算法
 a=b*2 -> a=b<<1;
 a=b/4 -> a=b>>2;
 a=b%8 -> a=b&7;
 a=b/8*8+b%4 -> a=((b>>3)<<3)+(b&3)
 a=b*15 -> a=(b<<4)-b
10、c關鍵字

 c的關鍵字共32個
 *資料型別關鍵字(12)
 char,short,int,long,float,double,unsigned,signed,union,enum,void,struct
 *控制語句關鍵字(12)
 if,else,switch,case,default,for,do,while,break,continue,goto,return
 *儲存類關鍵字(5)
 auto,extern,register,static,const
 *其他關鍵字(3)
 sizeof,typedef,volatile
11、int main(void)
 {
  unsigned int a = 6;
  int b = -20;
  char c;
  (a+b>6)?(c=1):(c=0);
 }則c=1,但a+b=-14;如果a為int型別則c=0。
 原來有符號數和無符號數進行比較運算時(==,<,>,<=,>=),有符號數隱式轉換成了無符號數(即底層的補碼不變,但是此數從有符號數變成了無符號數),
 比如上面 (a+b)>6這個比較運算,a+b=-14,-14的補碼為1111111111110010。此數進行比較運算時,
 被當成了無符號數,它遠遠大於6,所以得到上述結果。
12、給定一個整型變數a,寫兩段程式碼,第一個設定a的bit3,第二個清除a的bit,在以上兩個操作中,
 要保持其它位不變。
 #define BIT3 (0x1<<3)
  static int a;
  void set_bit3(void)
  {
   a |= BIT3;
  }
  void clear_bit3(void)
  {
   a &= ~BIT3;
  }
13、要求設定一絕對地址為0x67a9的整型變數的值為0xaa66。
  int *ptr;
    ptr = (int *)0x67a9;
     *ptr = 0xaa66;(建議用這種)
  
    一個較晦澀的方法是:
    *(int * const)(0x67a9) = 0xaa66;
14、中斷是嵌入式系統中重要的組成部分,這導致了很多編譯開發商提供一種擴充套件—讓標準C支援中斷。
 具代表性的是,產生了一個新的關鍵字__interrupt。下面的程式碼就使用了__interrupt關鍵字去定義了一箇中斷服務子程式(ISR),請評論一下這段程式碼的。
 __interrupt void compute_area (void)
 {
   double area = PI * radius * radius;
   printf(" Area = %f", area);
   return area;
 }
 ISR不可能有引數和返回值的!
 ISR儘量不要使用浮點數處理程式,浮點數的處理程式一般來說是不可重入的,而且是消耗大量CPU時間的!!
 printf函式一般也是不可重入的,UART屬於低速裝置,printf函式同樣面臨大量消耗CPU時間的問題!
15、評價下面的程式碼片斷:
  
  unsigned int zero = 0;
  unsigned int compzero = 0xFFFF;
  /*1's complement of zero */
  對於一個int型不是16位的處理器為說,上面的程式碼是不正確的。應編寫如下:
  unsigned int compzero = ~0;
16、main()
 {
  char *ptr;
   if ((ptr = (char *)malloc(0)) == NULL)
   puts("Got a null pointer");
   else
   puts("Got a valid pointer");
 }
 該程式碼的輸出是“Got a valid pointer”。還可以*ptr='a';不出現段錯誤
17、Typedef 在C語言中頻繁用以宣告一個已經存在的資料型別的同義字。也可以用前處理器做類似的事。
    例如,思考一下下面的例子:
  #define dPS struct s *
  typedef struct s * tPS;
  
  以上兩種情況的意圖都是要定義dPS 和 tPS 作為一個指向結構s指標。哪種方法更好呢?(如果有的話)為什麼?
  這是一個非常微妙的問題,任何人答對這個問題(正當的原因)是應當被恭喜的。答案是:typedef更好。思考下面的例子:
  dPS p1,p2;
  tPS p3,p4;
  
  第一個擴充套件為
  struct s * p1, p2;
  
  上面的程式碼定義p1為一個指向結構的指,p2為一個實際的結構,這也許不是你想要的。第二個例子正確地定義了p3 和p4 兩個指標。
18、int a = 5, b = 7, c;
  c = a+++b;
 則c=12。
19、int main()
 {
  int j=2;
  int i=1;
  if(i = 1) j=3;
  if(i = 2) j=5;
  printf("%d",j);
 } 
 輸出為5;如果再加上if(i=3)j=6;則輸出6。
20、巨集定義是在預編譯階段被處理的。
21、Norflash與Nandflash的區別
 (1)、NAND快閃記憶體的容量比較大
 (2)、由於NandFlash沒有掛接在地址總線上,所以如果想用NandFlash作為系統的啟動盤,就需要CPU具備特殊的功能,
 如s3c2410在被選擇為NandFlash啟動方式時會在上電時自動讀取NandFlash的4k資料到地址0的SRAM中。
 (3)、NAND Flash一般地址線和資料線共用,對讀寫速度有一定影響。NOR Flash快閃記憶體資料線和地址線分開,
    所以相對而言讀寫速度快一些。
22、反碼:對原碼除符號位外的其餘各位逐位取反就是反碼
 補碼:負數的補碼就是對反碼加1
 正數的原碼、反碼、補碼都一樣
23、pthread_t tid;
 pthread_create(&tid,NULL,pthread_func,NULL);//建立執行緒
 pthread_join(tid,NULL);//等待子執行緒結束,並回收資源
 pthread_detach(tid);//與當前程序分離
 pthread_exit(NULL);//退出呼叫執行緒
 pthread_cancel(tid);//取消執行緒
 pthread_mutex mutex=PTHREAD_MUTEX_INITIALIZER;
 pthread_mutex_init(&mutex,NULL);//初始化一個互斥鎖
 pthread_mutex_lock(&mutex);//對互斥鎖上鎖
 pthread_mutex_unlock(&mutex);//對互斥鎖解鎖
 sem_t sem;
 sem_init(&sem,0,1);//建立訊號量並初始化它的值
 sem_wait(&sem);//訊號量的值減1
 sem_post(&sem);//訊號量的值加1
24、記憶體管理MMU的作用
  *記憶體分配和回收
  *記憶體保護
  *記憶體擴充
  *地址對映
25、ROM是隻讀儲存器,掉電不丟失
 RAM是讀寫儲存器,掉電丟失
26、SRAM:CPU的快取就是SRAM,靜態的隨機存取儲存器,加電情況下,不需要重新整理,資料不會丟失
 DRAM,動態隨機存取儲存器最為常見的系統記憶體,需要不斷重新整理,才能儲存資料
 SDRAM:同步動態隨機儲存器,即資料的讀取需要時鐘來同步。
27、signed char 的取值範圍-128~127.
28、編譯和連結有什麼不同?(如外部符號的處理)
 編譯生成的是目標檔案(object  *.o);
 編譯過程中對於外部符號不做任何解釋和處理。外部符號對應的就是“符號”

 連結生成的是可執行程式
 連結將會解釋和處理外部符號。外部符號對應的是地址
29、已知strcpy函式的函式原型是:
 char *strcpy(char *strDest, const char *strSrc)。其中,strDest是目的字串,strSrc是源字串。
 不呼叫C++/C的字串庫函式,請編寫函式strcpy
 char *strcpy(char *strDest, const char *strSrc)
 {
  int i=0;
  if(!(strDest && strSrc))
   return;
  while(strDest[i++] = *strSrc++);
  return strDest;
 }
30、strcpy能把strSrc的內容複製到strDest,為什麼還要char *型別的返回值?
 為了實現鏈式表示式
 int len = strlen(strcpy(strDest, strSrc));
31、寫一個“標準”巨集MIN,這個巨集輸入兩個引數並返回較小的一個
 #define MIN(a, b) (a) <= (b) ? (a) : (b)
32、關鍵字static的作用是什麼
 static用來修飾一個區域性的變數的時候,
  生命域是全域性的
  作用域是區域性的

 static用來修飾一個模組內的(某一個C的源程式檔案)全域性變數的時候
  生命域不變
  作用域減小,只在本模組內有效

 static用來修飾一個函式的時候
  作用域減小,只在本模組內有效

33、說明下面的宣告的含義:
  A.
  const int a;     // a是一個常數
  int const a;     // a是一個常數
  B.
  const int *a;     // a是一個指向整型常數的指標
  int * const a;     // a是一個指向整型變數的常指標
  int const * a const;   // a是一個指向整型常數的常指標
 C.
 char *strcpy(char *strDest, const char *strSrc);
       // 引數在函式內部不會被修改
 const int strcmp(char *source, char *dest);
       // 函式的返回值不能被修改
 const int a = strcmp(xx, yy);
 if(strcmp(xx,yy) != 0)
34、說明關鍵字volatile有什麼含意,並給出例子。
 volatile表示被修飾的符號是易變的。告訴編譯器不要隨便優化我的程式碼!!
 *一個硬體暫存器
 *中斷中用到的變數
 *執行緒之間共享變數
 volatile int a = 10;
 while((a & 0x01) == 0);
 #define P_UART_STATUS ((const volatile unsigned int *)0x88000000);
       // volatile表示硬體會修改這個暫存器的內容
       // const表示該暫存器只讀,寫無意義
35、printf可以接受多個引數,為什麼,請寫出printf的原型。
 int printf(const char *fmt, ...);
36、什麼是堆疊,簡述為什麼需要堆疊?
 堆疊是計算機中最常用的一種資料結構,儲存資料的一塊連續記憶體;比如函式的呼叫是用堆疊實現的。
37、請列舉常用的序列通訊方式(兩種以上),並簡述序列通訊和並行通訊不同之處、優缺點。
 非同步通訊和同步通訊;並行速度快,序列口線間干擾小
38、列舉一下你熟悉7層OSI協議中的幾層。說說你最熟悉的一層協議的功能。
    應用層,表示層,會話層,傳輸層,網路層,資料鏈路層,物理層。
39、路由協議:閘道器-閘道器協議,外部閘道器協議,內部閘道器協議(RIP-1、RIP-IGRP、EIGRP、IS-IS和OSPF)
40、位轉換
 位 8   7   6   5   4   3   2    1
 數 v8  v7  v6  v5  v4  v3  v2   v1

 轉換後:
 位 8   7   6   5   4    3   2   1
 數 v1  v2  v3  v4  v5   v6  v7  v8
 unsigned char  bit_reverse(unsigned char  c)
 {
   unsigned char buf = 0;
   int bit = 8;
   while(bit)
   {
   bit--;
   buf |= ((c & 1) << bit);
   c >>=1;
   }
   return buf;
 }
41、字串倒序
 1)、inverted_order(char *p)
  {
   char *s1,*s2,tem;
   s1=p;
   s2=s1+strlen(p)-1;
   while(s1<s2)
   {
    tem=*s1;
    *s1=*s2;
    *s2=tem;
    s1++;
    s2--;
   }
  }
 2)、inverted_order(char *p)
  {
   int len = strlen(src);
   char *des = (char *)malloc(len + 1);
   char *s = &src[len -1];
   char *d = des;
   while(len-- != 0)
    *d++ = *s--;
   *d = 0;
   free(des);
  }
42、引用和指標的區別
  (1). 指標是一個實體,而引用僅是個別名;
   (2). 引用使用時無需解引用(*),指標需要解引用;
   (3). 引用只能在定義時被初始化一次,之後不可變;指標可變;
   (4). 引用沒有 const,指標有 const,const 的指標不可變;
   (5). 引用不能為空,指標可以為空;
   (6). “sizeof 引用”得到的是所指向的變數(物件)的大小,
    而“sizeof指標”得到的是指標本身(所指向的變數或物件的地址)的大小;
   (7). 指標和引用的自增(++)運算意義不一樣;
43、佇列和棧的區別
 佇列是先進先出,只能在一端插入另一端刪除,可以從頭或尾進行遍歷(但不能同時遍歷),
 棧是先進後出,只能在同一端插入和刪除,只能從頭部取資料
44、四層模型?七層模型?TCP/IP協議包括?
 這7層是:物理層、資料鏈路層、網路層、傳輸層、話路層、表示層和應用層
 這4層分別為:應用層、傳輸層、網路層、鏈路層。
 TCP/IP協議族包括(IP)、(ARP)、(RARP)、(ICMP)、(UDP)、(TCP)、(RIP)、Telnet、(SMTP)、DNS等協議。
45、TCP通訊建立和結束的過程?埠的作用
 三次握手和四次揮手;埠是一個軟體結構,被客戶程式或服務程序用來發送和接收資訊。一個埠對應一個16位元的數。服務程序通常使用一個固定的埠。
    21埠:21埠主要用於FTP(File Transfer Protocol,檔案傳輸協議)服務。    23埠:23埠主要用於Telnet(遠端登入)服務,是Internet上普遍採用的登入和模擬程式。  
    25埠:25埠為SMTP(Simple Mail TransferProtocol,簡單郵件傳輸協議)伺服器所開放,
    主要用於傳送郵件,如今絕大多數郵件伺服器都使用該協議。  
    53埠:53埠為DNS(Domain Name Server,域名伺服器)伺服器所開放,
    主要用於域名解析,DNS服務在NT系統中使用的最為廣泛。  
    67、68埠:67、68埠分別是為Bootp服務的Bootstrap Protocol Server
    (載入程式協議服務端)和Bootstrap Protocol Client(載入程式協議客戶端)開放的埠。  
    69埠:TFTP是Cisco公司開發的一個簡單檔案傳輸協議,類似於FTP。  
    79埠:79埠是為Finger服務開放的,主要用於查詢遠端主機線上使用者、作業系統型別以及是否緩衝區溢位等使用者的詳細資訊。880埠:80埠是為HTTP(HyperText Transport Protocol,超文字傳輸協議)開放的,
    這是上網衝浪使用最多的協議,主要用於在WWW(World Wide Web,全球資訊網)服務上傳輸資訊的協議。  
    99埠:99埠是用於一個名為“Metagram Relay”(亞對策延時)的服務,
    該服務比較少見,一般是用不到的。  
    109、110埠:109埠是為POP2(Post Office Protocol Version 2,郵局協議2)服務開放的,
    110埠是為POP3(郵件協議3)服務開放的,POP2、POP3都是主要用於接收郵件的。 
    111埠:111埠是SUN公司的RPC(Remote Procedure Call,遠端過程呼叫)服務所開放的埠,
     主要用於分散式系統中不同計算機的內部程序通訊,RPC在多種網路服務中都是很重要的元件。  
    113埠:113埠主要用於Windows的“Authentication Service”(驗證服務)。  
    119埠:119埠是為“Network News Transfer Protocol”(網路新聞組傳輸協議,簡稱NNTP)開放的。  
    135埠:135埠主要用於使用RPC(Remote Procedure Call,遠端過程呼叫)協議並提供DCOM(分散式元件物件模型)服務。  
    137埠:137埠主要用於“NetBIOS Name Service”(NetBIOS名稱服務)。  
    139埠:139埠是為“NetBIOS Session Service”提供的,主要用於提供Windows檔案和印表機共享以及Unix中的Samba服務。  
    143埠:143埠主要是用於“Internet Message Access Protocol”v2(Internet訊息訪問協議,簡稱IMAP)。  
    161埠:161埠是用於“Simple Network Management Protocol”(簡單網路管理協議,簡稱SNMP)。  
    443埠:43埠即網頁瀏覽埠,主要是用於HTTPS服務,是提供加密和通過安全埠傳輸的另一種HTTP。  
    554埠:554埠預設情況下用於“Real Time Streaming Protocol”(實時流協議,簡稱RTSP)。  
    1024埠:1024埠一般不固定分配給某個服務,在英文中的解釋是“Reserved”(保留)。  
    1080埠:1080埠是Socks代理服務使用的埠,大家平時上網使用的WWW服務使用的是HTTP協議的代理服務。  
    1755埠:1755埠預設情況下用於“Microsoft Media Server”(微軟媒體伺服器,簡稱MMS)。  
    4000埠:4000埠是用於大家經常使用的QQ聊天工具的,再細說就是為QQ客戶端開放的埠,QQ服務端使用的埠是8000。  
    5554埠:在今年4月30日就報道出現了一種針對微軟lsass服務的新蠕蟲病毒——震盪波(Worm.Sasser),該病毒可以利用TCP 5554埠開啟一個FTP服務,主要被用於病毒的傳播。  
    5632埠:5632埠是被大家所熟悉的遠端控制軟體pcAnywhere所開啟的埠。  
    8080埠:8080埠同80埠,是被用於WWW代理服務的,可以實現網頁瀏覽。
46、實體地址轉換成IP地址的協議?反之?
 地址解析協議(ARP)的作用是將IP地址轉換成實體地址;反地址解析協議(RARP)則負責將實體地址轉換成IP地址。
47、WLAN:無線區域網絡:利用射頻技術進行資料傳輸的系統。
   WLAN是指應用無線通訊技術將計算機裝置互聯起來,構成可以互相通訊和實現資源共享的網路體系。無線區域網本質的特點是不再使用通訊電纜將計算機與網路連線起來,而是通過無線的方式連線,從而使網路的構建和終端的移動更加靈活。

48、已知陣列table,用巨集求元素個數。
  COUNT(table)  (sizeof(table)/sizeof(table[0]));

49、定義一個兩個引數的標準巨集MAX,總是輸出最大值。
  #define  MAX(a,b)  ((a)>(b)?(a):(b))
50、什麼是平衡二叉樹?
 當且僅當兩個子樹的高度差不超過1時,這個樹是平衡二叉樹。
51、全域性變數和區域性變數的區別。
  全域性變數,儲存在靜態區.進入main函式之前就被建立.生命週期為整個源程式; 
 區域性變數,在棧中分配.在函式被呼叫時才被建立.生命週期為函式內。
52、陣列與連結串列的區別。
  陣列中的資料在記憶體中的按順序儲存的,而連結串列是隨機儲存的!
 要訪問陣列中的元素可以按下標索引來訪問,速度比較快,如果對他進行插入操作的話, 就得移動很多元素,所以對陣列進行插入操作效率很低!由於連表是隨機儲存的,
 連結串列在插入,刪除操作上有很高的效率(相對陣列),如果要訪問連結串列中的某個元素的話,
 那就得從連結串列的頭逐個遍歷,直到找到所需要的元素為止,
 所以連結串列的隨機訪問的效率就比陣列要低  
53、死鎖的四個條件及處理方法。
  (1)互斥條件:一個資源每次只能被一個程序使用。   
 (2) 請求與保持條件:一個程序因請求資源而阻塞時,對已獲得的資源保持不放。
    (3)不剝奪條件:程序已獲得的資源,在末使用完之前,不能強行剝奪。   
    (4)迴圈等待條件:若干程序之間形成一種頭尾相接的迴圈等待資源關係。 
      解決死鎖的方法分為死鎖的預防,避免,檢測與恢復三種 
54、程序排程策略。
  先進先出演算法,最短CPU執行期優先排程演算法,輪轉法,多級佇列方法
55、Linux驅動程式流程及功能。
  裝置驅動程式的功能:
  對裝置初始化和釋放
  把資料從核心傳送到硬體和從硬體讀取資料
  讀取應用程式傳送給裝置檔案的資料和回送應用程式請求的資料
  檢測和處理裝置出現的錯誤
56、時間換空間、空間換時間的例子。
  氣泡排序 --- 時間換空間
  快速排序,堆排序 --- 空間換時間  
57、MAC層通訊協議有哪些?
  ISO2110,IEEE802,IEEE802.2  
58、關鍵字static的作用是什麼?
 *在函式體內,一個被宣告為靜態的變數在這一函式被呼叫過程中維持其值不變(該變數存放在靜態變數區)。
    *在模組內(但在函式體外),一個被宣告為靜態的變數可以被模組內所用函式訪問,但不能被模組外其它函式訪問。
     它是一個本地的全域性變數。
    *在模組內,一個被宣告為靜態的函式只可被這一模組內的其它函式呼叫。
  那就是,這個函式被限制在宣告它的模組的本地範圍內使用。
59、引數的傳遞方式有幾種?
 1、值傳遞
    2、指標傳遞
    嚴格來看,只有一種傳遞,值傳遞,指標傳遞也是按值傳遞的,複製的是地址。
60、區域性變數能否和全域性變數重名?    
   答:能,區域性會遮蔽全域性。要用全域性變數,需要使用 ":: "  區域性變數可以與全域性變數同名,在函式內引用這個變數時,會用到同名的區域性變數,而不會用到全域性變數。
   對於有些編譯器而言,在同一個函式內可以定義多個同名的區域性變數,比如在兩個迴圈體內都定義一個同名的區域性變數,
   而那個區域性變數的作用域就在那個迴圈體內。 
61、如何引用一個已經定義過的全域性變數?    
   答:extern 可以用引用標頭檔案的方式,也可以用extern關鍵字,如果用引用標頭檔案方式來引用某個在標頭檔案中宣告的全域性變理,
   假定你將那個變數寫錯了,那麼在編譯期間會報錯,如果你用extern方式引用時,假定你犯了同樣的錯誤,
   那麼在編譯期間不會報錯,而在連線期間報錯。 
62、全域性變數可不可以定義在可被多個.C檔案包含的標頭檔案中?為什麼?    答:可以,在不同的C檔案中以static形式來宣告同名全域性變數。    可以在不同的C檔案中宣告同名的全域性變數,前提是其中只能有一個C檔案中對此變數賦初值,此時連線不會出錯  
63、語句for(   ;1   ;)有什麼問題?它是什麼意思?    答:和while(1)相同。

64、static全域性變數與普通的全域性變數有什麼區別?static區域性變數和普通區域性變數有什麼區別?static函式與普通函式有什麼區別?    全域性變數(外部變數)的說明之前再加static   就構成了靜態的全域性變數。全域性變數本身就是靜態儲存方式,   
    靜態全域性變數當然也是靜態儲存方式。   這兩者在儲存方式上並無不同。
    這兩者的區別雖在於非靜態全域性變數的作用域是整個源程式,   當一個源程式由多個原始檔組成時,
    非靜態的全域性變數在各個原始檔中都是有效的。   而靜態全域性變數則限制了其作用域,  
  即只在定義該變數的原始檔內有效。 從以上分析可以看出,把區域性變數改變為靜態變數後是改變了它的儲存
 方式即改變了它的生存期。把全域性變數改變為靜態變數後是改變了它的作用域,   
 限制了它的使用範圍。static函式與普通函式作用域不同。僅在本檔案。只在當前原始檔中使用的函式應該說明為內部函式(static)
 ,內部函式應該在當前原始檔中說明和定義。對於可在當前原始檔以外使用的函式,應該在一個頭檔案中說明,
 要使用這些函式的原始檔要包含這個標頭檔案 static全域性變數與普通的全域性變數有什麼區別:
 static全域性變數只初使化一次,防止在其他檔案單元中被引用static區域性變數和普通區域性變數有什麼區別:
 static區域性變數只被初始化一次,下一次依據上一次結果值; static函式與普通函式有什麼區別:
 static函式在記憶體中只有一份,普通函式在每個被呼叫中維持一份拷貝
65、程式的區域性變數存在於(堆疊)中,全域性變數存在於(靜態區   )中,動態申請資料存在於(   堆)中。
66、-1,2,7,28,,126請問28和126中間那個數是什麼?為什麼?    
    答案應該是4^3-1=63 規律是n^3-1(當n為偶數0,2,4)n^3+1(當n為奇數1,3,5)
67、用兩個棧實現一個佇列的功能?要求給出演算法和思路!    
    設2個棧為A,B, 一開始均為空. 
 入隊: 將新元素push入棧A;    
 出隊: (1)判斷棧B是否為空;    
       (2)如果不為空,則將棧A中所有元素依次pop出並push到棧B;    
    (3)將棧B的棧頂元素pop出;
68、.軟體測試都有那些種類? 
  人工測試:個人複查、抽查和會審
  機器測試:黑盒測試(針對系統功能的測試 )和白盒測試 (測試函式功能,各函式介面) 
69、堆疊溢位一般是由什麼原因導致的?
      沒有回收垃圾資源。
70、寫出float x 與“零值”比較的if語句。
      if(x>0.000001&&x<-0.000001)
71、P地址的編碼分為哪倆部分?
     IP地址由兩部分組成,網路號和主機號。不過是要和“子網掩碼”按位與上之後才能區分哪些是網路位哪些
     是主機位
72、寫一個程式, 要求功能:求出用1,2,5這三個數不同個數組合的和為100的組合個數。
   如:100個1是一個組合,5個1加19個5是一個組合。。。。 請用C++語言寫。

 答案:最容易想到的演算法是:
 設x是1的個數,y是2的個數,z是5的個數,number是組合數
 注意到0<=x<=100,0<=y<=50,0<=z=20,所以可以程式設計為:
 number=0;
 for (x=0; x<=100; x++)
  for (y=0; y<=50; y++)
   for (z=0; z<=20; z++)
    if ((x+2*y+5*z)==100)
     number++;
73、記憶體對齊問題的原因?
 平臺原因(移植原因):不是所有的硬體平臺都能訪問任意地址上的任意資料;
 效能原因:資料結構(尤其是棧)應該儘可能地在自然邊界上對齊,因為為了訪問未對齊的記憶體,處理器需要做兩次記憶體訪問,
     而對齊的記憶體訪問僅需要一次。
74、比較一下程序和執行緒的區別?
 (1)、排程:執行緒是CPU排程和分派的基本單位
 (2)、擁有資源:
  *  程序是系統中程式執行和資源分配的基本單位
  *  執行緒自己一般不擁有資源(除了必不可少的程式計數器,一組暫存器和棧),但他可以去訪問其所屬程序的資源,
     如程序程式碼,資料段以及系統資源(已開啟的檔案,I/O裝置等)。
 (3)系統開銷:
  *  同一程序中的多個執行緒可以共享同一地址空間,因此它們之間的同步和通訊的實現也比較簡單
  *  在程序切換的時候,涉及到整個當前程序CPU環境的儲存以及新被排程執行的程序的CPU環境的設定;
     而執行緒切換隻需要儲存和設定少量暫存器的內容,並不涉及儲存器管理方面的操作,從而能更有效地使用系統資源和
     提高系統吞吐量。
75、main()
 {
   int a[5]={1,2,3,4,5};
    int *ptr=(int *)(&a+1);//&a相當於變成了行指標,加1則變成了下一行首地址
    printf("%d,%d",*(a+1),*(ptr-1));
 }
 *(a+1)就是a[1],*(ptr-1)就是a[4],執行結果是2,5
76、 void getmemory(char *p)
   {
  p=(char *) malloc(100);
  strcpy(p,"hello world");
   }
   int main( )
   {
  char *str=NULL;
  getmemory(str);
  printf("%s/n",str);
  free(str);
  return 0;
    }
 程式崩潰,getmemory中的malloc 不能返回動態記憶體, free()對str操作很危險
77、void GetMemory(char *p)
 {
  p = (char *)malloc(100);
 }
 void Test(void)
 {
  char *str = NULL;
  GetMemory(str); 
  strcpy(str, "hello world");
  printf(str);
 }

 請問執行Test函式會有什麼樣的結果?
 答:程式崩潰。因為GetMemory並不能傳遞動態記憶體,Test函式中的 str一直都是 NULL。strcpy(str, "hello world");將使程式崩潰。

 void GetMemory2(char **p, int num)
 {
  *p = (char *)malloc(num);
 }
 void Test(void)
 {
 char *str = NULL;
 GetMemory(&str, 100);
 strcpy(str, "hello"); 
 printf(str); 
 }
 請問執行Test函式會有什麼樣的結果?
 答:(1)能夠輸出hello
     (2)記憶體洩漏
78、char *GetMemory(void)
 { 
  char p[] = "hello world";
  return p;
 }
 void Test(void)
 {
  char *str = NULL;
  str = GetMemory(); 
  printf(str);
 }
 請問執行Test函式會有什麼樣的結果?
 答:可能是亂碼。因為GetMemory返回的是指向“棧記憶體”的指標,該指標的地址不是 NULL,
 但其原現的內容已經被清除,新內容不可知。
79、void Test(void)
 {
   char *str = (char *) malloc(100);
  strcpy(str, “hello”);
  free(str);    
  if(str != NULL)
  {
    strcpy(str, “world”); 
    printf(str);
   }
 }
 請問執行Test函式會有什麼樣的結果?
 答:篡改動態記憶體區的內容,後果難以預料,非常危險。因為free(str);之後,str成為野指標,
 if(str != NULL)語句不起作用。
 野指標不是NULL指標,是指向被釋放的或者訪問受限記憶體指標。
 造成原因:指標變數沒有被初始化任何剛建立的指標不會自動成為NULL;
     指標被free或delete之後,沒有置NULL;
     指標操作超越了變數的作用範圍,比如要返回指向棧記憶體的指標或引用,因為棧記憶體在函式結束時會被釋放。
80、unsigned char *p=(unsigned char *)0x0801000
    unsigned char *q=(unsigned char *)0x0810000
 p+5 =?    0x0801005
 q+5 =?    0x0810005
81、程序間通訊方式:管道、命名管道、訊息佇列、共享記憶體、訊號、訊號量、套接字。
 (1)、 管道( pipe ):管道是一種半雙工的通訊方式,資料只能單向流動,而且只能在具有親緣關係的程序間使用。
       程序的親緣關係通常是指父子程序關係。
 (2)、有名管道 (named pipe) :有名管道也是半雙工的通訊方式,但是它允許無親緣關係程序間的通訊。
 (3)、訊號量( semophore ) :訊號量是一個計數器,可以用來控制多個程序對共享資源的訪問。
      它常作為一種鎖機制,防止某程序正在訪問共享資源時,其他程序也訪問該資源。
      因此,主要作為程序間以及同一程序內不同執行緒之間的同步手段。
 (4)、訊息佇列( message queue ) : 訊息佇列是由訊息的連結串列,存放在核心中並由訊息佇列識別符號標識。
      訊息佇列克服了訊號傳遞資訊少、管道只能承載無格式位元組流以及緩衝區大小受限等缺點。
 (5)、訊號 ( sinal ) : 訊號是一種比較複雜的通訊方式,用於通知接收程序某個事件已經發生。
 (6)、共享記憶體( shared memory ) :共享記憶體就是對映一段能被其他程序所訪問的記憶體,
      這段共享記憶體由一個程序建立,但多個程序都可以訪問。共享記憶體是最快的 IPC 方式,它是針對其他程序間通訊方式執行效率低而專門設計的。它往往與其他通訊機制,
      如訊號兩,配合使用,來實現程序間的同步和通訊。
 (7)、套接字( socket ) : 套接字也是一種程序間通訊機制,與其他通訊機制不同的是,它可用於不同及其間的程序通訊。
82、巨集和函式的優缺點?
 (1)、函式呼叫時,先求出實參表示式的值,然後帶入形參。而使用帶引數的巨集只是進行簡單的字元替換。
 (2)、函式呼叫是在程式執行時處理的,分配臨時的記憶體單元;而巨集展開則是在編譯時進行的,在展開時並不分配記憶體單元,
   不進行值的傳遞處理,也沒有“返回值”的概念。
 (3)、對函式中的實參和形參都要定義型別,二者的型別要求一致,應進行型別轉換;而巨集不存在型別問題,巨集名無型別,
   它的引數也是無型別,只是一個符號代表,展開時帶入指定的字元即可。巨集定義時,字串可以是任何型別的資料。
 (4)、呼叫函式只可得到一個返回值,而巨集定義可以設法得到幾個結果。
 (5)、使用巨集次數多時,巨集展開後源程式長,因為每次展開一次都使程式增長,而函式呼叫不使源程式變長。
 (6)、巨集替換不佔執行時間,只佔編譯時間;而函式呼叫則佔執行時間(分配單元、保留現場、值傳遞、返回)。
83、C和c++的不同
 c和c++的一些不同點(從語言本身的角度):
 1)c++源於c,c++最重要的特性就是引入了面向物件機制,class關鍵字。
 2)c++中,變數可以再任何地方宣告;c中,區域性變數只能在函式開頭宣告。
 3)c++中,const型常量是編譯時常量;c中,const常量只是只讀的變數。
 4)c++有&引用;c沒有
 5)c++的struct宣告自動將結構型別名typedef;c中struct的名字只在結構標籤名字空間中,不是作為一種型別出現
 6)c語言的main函式可以遞迴呼叫;c++中則不可以
 7)c中,void *可以隱式轉換成其他指標型別;c++中要求現實轉換,否則編譯通不過
84、6.大小端格式問題。
 方法一:
 void checkCpuMode(void)
 {
  int i = 0x12345678;
  char *cp = (char *)&i;
  if(*cp == 0x78)
   printf("little endian");
  else
   printf("big endian\n");
 }
 方法二:
 void checkCpuMode(void)
 {
  int a = 0x12345678;
  if((char)a == 0x12)
   printf("big endian\n");
  else
   printf("little endian\n");
 }
 方法三:
 void checkCpuMode(void)
    {
  union
  {
   short s;
   char c[sizeof(short)];
  }un;
  un.s=0x0102;
  if(un.[0]==1&&un.c[1]==2)
   printf("big endian\n");
  else
   printf("little endian\n");
 }
85、由C/C++編譯的程式佔用的記憶體分為以下幾個部分
 1、棧區(stack): 由編譯器自動分配釋放 ,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。
 2、堆區(heap): 一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由OS回收。
                 注意它與資料結構中的堆是兩回事,分配方式倒是類似於連結串列。
 3、全域性區(static): 全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域,
                    未初始化的全域性變數和未初始化的靜態變數在相鄰的另一塊區域,程式結束後有系統釋放 。
 4、文字常量區: 常量字串就是放在這裡的, 程式結束後由系統釋放。
 5、程式程式碼區: 存放函式體的二進位制程式碼。
87、for(m=5;m--;m<0)
 {
  printf("m=%d\n",m);
 }輸出:4、3、2、1、0
88、5["abcdef"]能夠編譯通過,請問編譯後的結果是什麼?
 printf("%d\n",5["abcdef"]);輸出'f'的ACSII值,如果是4["abcdef"]則輸出'e'的ACSII的值。
89、執行緒同步的方法:訊號量、條件變數、互斥鎖。
90、for(i=0;i<2,i<3,i<4;i++)
  printf("%d \n",i);     輸出:0,1,2,3。

100、實體地址,虛擬地址,邏輯地址和匯流排地址的區別
     邏輯地址(Logical Address)是指由程式產生的與段相關的偏移地址部分。
 例如,你在進行C語言指標程式設計中,可以讀取指標變數本身值(&amp;操作),實際上這個值就是邏輯地址,
 它是相對於你當前程序資料段的地址,不和絕對實體地址相干。只有在Intel真實模式下,
 邏輯地址才和實體地址相等(因為真實模式沒有分段或分頁機制, Cpu不進行自動地址轉換);
 邏輯也就是在Intel 保護模式下程式執行程式碼段限長內的偏移地址(假定程式碼段、資料段如果完全一樣)。
 應用程式設計師僅需與邏輯地址打交道,而分段和分頁機制對您來說是完全透明的,僅由系統程式設計人員涉及。
 應用程式設計師雖然自己可以直接操作記憶體,那也只能在作業系統給你分配的記憶體段操作。
     線性地址(Linear Address)是邏輯地址到實體地址變換之間的中間層。程式程式碼會產生邏輯地址,
 或者說是段中的偏移地址,加上相應段的基地址就生成了一個線性地址。如果啟用了分頁機制,
 那麼線性地址可以再經變換以產生一個實體地址。若沒有啟用分頁機制,那麼線性地址直接就是實體地址。
 Intel 80386的線性地址空間容量為4G(2的32次方即32根地址匯流排定址)。
     實體地址(Physical Address) 是指出現在CPU外部地址總線上的定址實體記憶體的地址訊號,是地址變換的最終結果地址。
 如果啟用了分頁機制,那麼線性地址會使用頁目錄和頁表中的項變換成實體地址。
 如果沒有啟用分頁機制,那麼線性地址就直接成為實體地址了。
  在x86下,外設的i/o地址是獨立的,即有專門的指令訪問外設i/o,i/o地址就是你所說的“匯流排地址”。
 而“實體地址”就是ram地址。在arm中,i/o和ram統一編址,但linux為了統一各個平臺,仍然保留這個概念,其實就是實體地址。 
101、編寫核心程式中申請記憶體和編寫應用程式時申請記憶體有什麼區別
     應用程式使用C函式庫中的記憶體分配函式malloc()申請記憶體核心會為程序使用的程式碼和資料空間維護一個當前位置的值brk,
 這個值儲存在每個程序的資料結構中。它指出了程序程式碼和資料(包括動態分配的資料空間)在程序地址空間中的末端位置。
 當malloc()函式為程式分配記憶體時,它會通過系統呼叫brk()把程式要求新增的空間長度通知核心,
 核心程式碼從而可以根據malloc()所提供的資訊來更新brk的值,但此時並不為新申請的空間對映實體記憶體頁面。
 只有當程式定址到某個不存在對應物理頁面的地址時,核心才會進行相關實體記憶體頁面的對映操作。
     當用戶使用記憶體釋放函式free()動態釋放已申請的記憶體塊時,c庫中的記憶體管理函式就會把所釋放的記憶體塊標記為空閒,
 以備程式再次申請記憶體時使用。在這個過程中核心為該程序所分配的這個物理頁面並不會被釋放掉。
 只有當程序最終結束時核心才會全面收回已分配和對映到該程序地址空間範圍內的所有實體記憶體頁面。
102、如何用C語言實現讀寫暫存器變數
 #define rBANKCON0  (*(volatile unsigned long *)0x48000004)
 rBankCON0 = 0x12; 
103、sscanf("123456 ", "%4s", buf);                     1234
 sscanf("123456 asdfga","%[^ ]", buf);               123456
    sscanf("123456aafsdfADDEFF", "%[1-9a-z]", buf);     123456aafsdf
    sscanf("123afsdfADJKLJ", "%[^A-Z]", buf);          123afsdf
104、char* s="AAA";
 s[0]='B';
 printf("%s",s);
 有什麼錯?
 "AAA"是字串常量。s是指標,指向這個字串常量,所以宣告s的時候就有問題。
 cosnt char* s="AAA";
 然後又因為是常量,所以對是s[0]的賦值操作是不合法的。 
105、陣列a[N],存放了1至N-1個數,其中某個數重複一次。寫一個函式,找出被重複的數字.時間複雜度必須為o(N)函式原型:
  int do_dup(int a[],int N)                 int do_dup(int a[],int N)//a[0]為監視哨 
  {                                           {
   int i;                                       int temp;
   int s;                                       while (a[0]!=a[a[0]])
   int num;                                     {
   for(i=0;i<N;i++)                                 temp=a[0];
    s+=a[i];                                     a[0]=a[temp];
   num=s-N*(N-1)/2;(num即為重複數)                  a[temp]=temp;        
  }                                                 }
              return a[0];
              }
106、tcp/udp是屬於哪一層?tcp/udp有何優缺點?
 tcp /udp屬於運輸層
 TCP 服務提供了資料流傳輸、可靠性、有效流控制、全雙工操作和多路複用技術等。
 與 TCP 不同, UDP 並不提供對 IP 協議的可靠機制、流控制以及錯誤恢復功能等。由於 UDP 比較簡單, UDP 頭包含很少的位元組,比 TCP 負載消耗少。
 tcp: 提供穩定的傳輸服務,有流量控制,缺點是包頭大,冗餘性不好
 udp: 不提供穩定的服務,包頭小,開銷小
107、 char a = 100;
   char b =  150;//10010110//01101010
   unsigned char c ;
      c = (a < b)? a:b;   --》 c=150;
108、應用程式ping發出的是ICMP請求報文
109、在C語言中memcpy和memmove是一樣的嗎?
 memmove()與memcpy()一樣都是用來拷貝src所指向記憶體內容前n個位元組到dest所指的地址上,不同是,當src和dest所指的記憶體區域重疊時,
 memmove()仍然可以正確處理,不過執行效率上略慢些。
110、C語言程式程式碼優化方法
  * 選擇合適的演算法和資料結構
  * 使用盡量小的資料型別
  * 使用自加、自減指令
  * 減少運算的強度
   求餘運算(a=a%8改為a=a&7)
   平方運算(a=pow(a,2.0)改為a=a*a)
   用移位實現乘除法運算
  * 延時函式的自加改為自減
  * switch語句中根據發生頻率來進行case排序
 
111、找出一個字串中一個最長的連續的數字,並標註出位置和個數。
 void main() 
  {
  char input[100]; 
  char output[100] = {0};
  int  count = 0, maxlen = 0, i = 0;
  char *in=input, *out=output,*temp=NULL,*final=NULL;
 
  printf("Please input string(length under 100):\n");
  scanf("%s", input);
  printf("Input string is %s\n", input);

  while(*in!='\0')
  {
    if(*in>='0'&&*in<='9')
    {
      count=0;
      temp=in;
      for(;(*in>='0')&&(*in<='9');in++)
     count++;
      if (maxlen<count)
      {
     maxlen=count;
     =temp;  
      }
    }
    in++;
  } 

  for(i=0; i<maxlen; i++)
  *(out++) = *(final++);
  *out='\0';

  printf("Maxlen is %d\n", maxlen);
  printf("Output is %s\n", output);
}
112、寫出螺旋矩陣
 void Matrix(int m,int n)  //順時針
 {
  int i,j,a=1;
  int s[100][100];
  int small = (m<n)?m:n;
  int k=small/2;
  for(i=0;i<k;i++)
  {
   for(j=i;j<n-i-1;j++)
    s[i][j]=a++;
   for(j=i;j<m-i-1;j++)
    s[j][n-i-1]=a++;
   for(j=n-i-1;j>i;j--)
    s[m-i-1][j]=a++;
   for(j=m-i-1;j>i;j--)
    s[j][i]=a++;
  }
  if(small & 1)
  {
   if(m<n)
    for(i=k;i<n-k;++i)
     s[k][i]=a++;
   else
    for(i=k;i<m-k;++i)
     s[i][k]=a++;
  }
  for(i=0;i<m;i++)
  {
   for(j=0;j<n;j++)
    printf("=",s[i][j]);
   printf("\n");
  }
 }
113、int *a = (int *)2;
  printf("%d",a+3); 答案是2+3*4=14;int型別地址加1 相當於加4個位元組
114、main()
 {
  
  char a,b,c,d;
  scanf("%c%c",&a,&b);
  c=getchar();d=getchar();
  printf("%c%c%c%c\n",a,b,c,d);
 } 輸出:12
    3
115、int a[2] = {1, 2};
 //指向常量的指標,指標可以變,指標指向的內容不可以變
 const int *p = a;//與int const *p = a;等價
 p++;//ok
 *p = 10;//error
 //常指標,指標不可以變,指標指向的內容可以變
 int* const p2 = a;
 p2++;//error
 *p2 = 10;//ok
 //指向常量的常指標,都不可以改變
 p3++;//error
 *p3 = 10;//error
116、#error 預處理指令的作用是,編譯程式時,只要遇到#error 就會生成一個編譯錯誤提
  示訊息,並停止編譯
117、中斷活動的全過程大致為:
 1、中斷請求:中斷事件一旦發生或者中斷條件一旦構成,中斷源提交“申請報告”,
   與請求CPU暫時放下目前的工作而轉為中斷源作為專項服務
 2、中斷遮蔽:雖然中斷源提交了“申請報告”,但是,是否得到CPU的響應,
   還要取決於“申請報告”是否能夠通過2道或者3道“關卡”(中斷遮蔽)送達CPU
  (相應的中斷遮蔽位等於1,為關卡放行;反之相應的中斷遮蔽位等於0,為關卡禁止通行);
 3、中斷響應:如果一路放行,則CPU響應中斷後,將被打斷的工作斷點記錄下來
  (把斷點地址保護到堆疊),掛起“不再受理其他申請報告牌”
  (清除全域性中斷標誌位GIE=0),跳轉到中斷服務子程式
 4、保護現場:在處理新任務時可能破壞原有的工作現場,所以需要對工作現場和工作環境進行適當保護;
 5、調查中斷源:檢查“申請報告”是由哪個中斷源提交的,以便作出有針對性的服務;
 6、中斷處理:開始對查明的中斷源進行有針對性的中斷服務;
 7、清除標誌:在處理完畢相應的任務之後,需要進行撤消登記(清除中斷標誌),以避免造成重複響應;
 8、恢復現場:恢復前面曾經被保護起來的工作現場,以便繼續執行被中斷的工作;
 9、中斷返回:將被打斷的工作斷點找回來(從堆疊中恢復斷點地址),
 並摘下“不再受理其他申請報告牌”(GIE=1),繼續執行原先被打斷的工作。   
118、linux程序間通訊的幾種方式的特點和優缺點,和適用場合。分類:
 #管道( pipe ):管道是一種半雙工的通訊方式,資料只能單向流動,
    而且只能在具有親緣關係的程序間使用。程序的親緣關係通常是指父子程序關係。 
 #有名管道(named pipe) : 有名管道也是半雙工的通訊方式,但是它允許無親緣關係程序間的通訊。
 #訊號量( semophore ) : 訊號量是一個計數器,可以用來控制多個程序對共享資源的訪問。
   它常作為一種鎖機制,防止某程序正在訪問共享資源時,其他程序也訪問該資源。因此,主要作
   為程序間以及同一程序內不同執行緒之間的同步手段。 
 #訊息佇列( message queue ) : 訊息佇列是由訊息的連結串列,存放在核心中並由訊息佇列標識
   符標識。訊息佇列克服了訊號傳遞資訊少、管道只能承載無格式位元組流以及緩衝區大小受限等缺點。
 #訊號 ( sinal ) : 訊號是一種比較複雜的通訊方式,用於通知接收程序某個事件已經發生。
 #共享記憶體( shared memory):共享記憶體就是對映一段能被其他程序所訪問的記憶體,
   這段共享記憶體由一個程序建立,但多個程序都可以訪問。共享記憶體是最快的IPC方式,
   它是針對其他程序間通訊方式執行效率低而專門設計的。它往往與其他通訊機制,
   如訊號量,配合使用,來實現程序間的同步和通訊。
 #套接字( socket ) : 套解口也是一種程序間通訊機制,與其他通訊機制不同的是,
   它可用於不同及其間的程序通訊。
119、關鍵字volatile有什麼含意?並給出三個不同的例子。
 一個定義為volatile的變數是說這變數可能會被意想不到地改變,這樣,編譯器就不會去假設
 這個變數的值了。精確地說就是,優化器在用到這個變數時必須每次都小心地重新讀取這個變數的值,
 而不是使用儲存在暫存器裡的備份。
 下面是volatile變數的幾個例子:
 1). 並行裝置的硬體暫存器(如:狀態暫存器)
 2). 一箇中斷服務子程式中會訪問到的非自動變數(Non-automatic variables)
 3). 多執行緒應用中被幾個任務共享的變數 回答不出這個問題的人是不會被僱傭的。我認為這是區分C程式設計師和嵌入式系統程式設計師的最基本的問題。嵌入式系統程式設計師經常同硬體、中斷、RTOS等等打交道,所用這些都要求volatile變數。不懂得volatile內容將會帶來災難。
 假設被面試者正確地回答了這是問題(嗯,懷疑這否會是這樣),我將稍微深究一下,
 看一下這傢伙是不是直正懂得volatile完全的重要性。
 1). 一個引數既可以是const還可以是volatile嗎?解釋為什麼。
 2). 一個指標可以是volatile 嗎?解釋為什麼。
 3). 下面的函式有什麼錯誤:
 int square(volatile int *ptr) {
    return *ptr * *ptr;
  }
 下面是答案:
 1). 是的。一個例子是隻讀的狀態暫存器。它是volatile因為它可能被意想不到地改變。
     它是const因為程式不應該試圖去修改它。
 2). 是的。儘管這並不很常見。一個例子是當一箇中服務子程式修該一個指向一個buffer的指標時。
 3). 這段程式碼的有個惡作劇。這段程式碼的目的是用來返指標*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;
  }
  Volatile 關鍵字告訴編譯器不要持有變數的臨時性拷貝。一般用在多執行緒程式中,
  以避免在其中一個執行緒操作該變數時,將其拷貝入暫存器。
 請看以下情形: A執行緒將變數複製入暫存器,然後進入迴圈,反覆檢測暫存器的值是否滿足
 一定條件(它期待B執行緒改變變數的值。在此種情況下,當B執行緒改變了變數的值時,
 已改變的值對其在暫存器的值沒有影響。所以A執行緒進入死迴圈。
  volatile 就是在此種情況下使用。  
120、如何防止同時產生大量的執行緒,方法是使用執行緒池,執行緒池具有可以同時提高排程效率和
  限制資源使用的好處,執行緒池中的執行緒達到最大數時,其他執行緒就會排隊等候 
121、main()
 {
  char *p1=“name”;
  char *p2;
  p2=(char*)malloc(20);
  memset (p2, 0, 20);
  while(*p2++ = *p1++);
  printf(“%s\n”,p2);
 }
 答案:Answer:empty string. 因p2++已經指到了'\0'了;
122、作業系統的記憶體分配一般有哪幾種方式,各有什麼優缺點?
 定長和變長。
 變長:記憶體時比較靈活,但是易產生記憶體碎片。
 定長:靈活性差,但分配效率較高,不會產生記憶體碎片 
123、全域性變數可不可以定義在可被多個.C檔案包含的標頭檔案中?為什麼?
 答:可以,在不同的C檔案中以static形式來宣告同名全域性變數。可以在不同的C檔案中宣告同名的全域性變數,
 前提是其中只能有一個C檔案中對此變數賦初值,此時連線不會出錯 
124、確定模組的功能和模組的介面是在軟體設計的那個隊段完成的?概要設計階段 
125、#define N 500
 unsigned char count;
 for(count=0;count < N;count++)
 {
  printf("---%d---\n",count);
 }死迴圈,因為unsigned char 最大為255
126、給定結構struct A
   {      
    char t:4; 4位      
    char k:4; 4位      
    unsigned short i:8; 8位            
    unsigned long m; // 偏移2位元組保證4位元組對齊
   }; // 共8位元組 
127、ICMP(ICMP協議對於網路安全具有極其重要的意義)功能主要有:
 · 偵測遠端主機是否存在。
 · 建立及維護路由資料。
 · 重導資料傳送路徑。
 · 資料流量控制。
 
 
 單向、雙向連結串列操作、寫一個快速排序演算法(原理:找一個基準值,分別將大於和小於基準值的資料放到基準值左右兩邊,即一次劃分。由於處在兩邊的資料也是無序的,所以再用同樣的劃分方法對左右兩邊的序列進行再次劃分,直到劃分元素只剩1個時結束,)
/******************************************************************/
1、單向連結串列逆序
LONDE *link_reverse(LNODE *head)
{
 LNODE *pb,*pt;
 if(head == NULL)
  return head;
 pb = head->next;
 head->next=NULL;
 while(pb != NULL)
 {
  pt = pb->next;
  pb->next = head;
  head = pb;
  pb = pt;
 }
 return head;
}
2、快速排序
void quick_sort(int num,int start_num,int end_num)
{
 if(start_num < end_num)
 {
  int i = start_num;
  int j = end_num;
  int temp = num[start_num];
  while(i < j)
  {
   while(i < j && num[j] < temp)
    j--;
   if(i < j)
    num[i++] = num[j];//把小於基準值放在左邊
   while(i < j && num[i] >= temp)
    i++;
   if(i < j)
    num[j--] = num[i];//把大於基準值放在右邊
  }
  num[i] = temp;
  quick_sort(num,start_num,i-1);
  quick_sort(num,i+1,end_num);
 }
}
3、//二分擦找
 int binary_search(int array[],int value,int size)
{
 int low=0,high=size-1,mid;
 
 while(low<=high)    //只要高低不碰頭就繼續二分查詢
 {
  mid=(low+high)/2;
  if(value==array[mid])  //比較是不是與中間元素相等
   return mid;
  else if(value > array[mid]) //每查詢一次,就判斷一次所要查詢變數所在範圍,並繼續二分
   low=mid;     //如果大小中間值,下限移到中間的後一個位,上限不變,往高方向二分
  else
   high=mid;        //上限移到中間的前一個位,往低方向二分
 }
 return -1;
}
/*雙向迴圈連結串列插入函式*/
TYPE *insert_link(TYPE *head,TYPE *p_in)
{
 TYPE *p_mov = head,p_front = head;
 
 if(head == NULL)
 {
  head = p_in;
  p_in->next = head;
  p_perior = head;
 }
 else
 {
  while((p_in->[] > p_mov->[]) && (p_mov->next != head))
  {
   p_front = p_mov;
   p_mov = p_mov->next;
  }
  
  if(p_in->[] <= p_mov->[])
  {
   if(head == p_mov)
   {
    p_in->prior = head->prior;
    head->prior->next = p_in;
    p_in->next = p_mov;
    p_mov->prior = p_in;
    head = p_in;
   }
   else
   {
    pf->next = p_in;
    p_in->prior = p_front;
    p_in->next = p_mov;
    p_mov->prior = p_in;
   }
  }
  else
  {
   p_mov->next = p_in;
   p_in->prior = p_mov;
   p_in->next = head;
   head->prior = p_in;
  }
 }
 return head;
}

/*雙向連結串列刪除函式*/
TYPE *delete_link(TYPE *head,int num)
{
 TYPE *p_mov = head,p_front = head;
 
 if(head == NULL)
  printf("Not link\n");
 while((p_mov->num != num) && (p_mov->next != head))
 {
  p_front = p_mov;
  p_mov = p_mov->next;
 }
 if(p_mov->num == num)
 {
  if(p_mov == head)
  {
   if(p_mov->next == head && p_mov->prior == head)
   {
    free(pb);
    head =NULL;
    return head;
   }
   head->next->prior = head->prior;
   head->prior->next = head->next;
   head = head->next;
  }
  else
  {
   p_front->next = p_mov->next;
   p_mov->next->prior = p_front;
  }
  free(p_mov);
  printf("The node is delete\n");
 }
 else
 {
  printf("The node not been found\n");
 }
 return head;
}