[收集] 經典C/C++面試題(轉載) 續

) 什麼是預編譯,何時需要預編譯:

  總是使用不經常改動的大型代碼體。

    程序由多個模塊組成,所有模塊都使用一組標準的包含文件和相同的編譯選項。在這種情況下,可以將所有包含文件預編譯爲一個預編譯頭。

2)  char * const p;
  char const * p
  const char *p

  上述三個有什麼區別?

  char * const p; //常量指針,p的值不可以修改
  char const * p;//指向常量的指針,指向的常量值不可以改
  const char *p; //和char const *p

3) char str1[] = "abc";
  char str2[] = "abc";

  const char str3[] = "abc";
  const char str4[] = "abc";

  const char *str5 = "abc";
  const char *str6 = "abc";

  char *str7 = "abc";
  char *str8 = "abc";

  cout << ( str1 == str2 ) << endl;
  cout << ( str3 == str4 ) << endl;
  cout << ( str5 == str6 ) << endl;

  cout << ( str7 == str8 ) << endl;

  結果是:0 0 1 1

  解答:str1,str2,str3,str4是數組變量,它們有各自的內存空間;而str5,str6,str7,str8是指針,它們指向相同的常量區域。

 4) 以下代碼中的兩個sizeof用法有問題嗎?

  void UpperCase( char str[] ) // 將 str 中的小寫字母轉換成大寫字母
  {
      for( size_t i=0; i<sizeof(str)/sizeof(str[0]); ++i )
         if( 'a'<=str[i] && str[i]<='z' )
           str[i] -= ('a'-'A' );
  }
  char str[] = "aBcDe";
  cout << "str字符長度爲: " << sizeof(str)/sizeof(str[0]) << endl;
  UpperCase( str );
  cout << str << endl;

 答:函數內的sizeof有問題。根據語法,sizeof如用於數組,只能測出靜態數組的大小,無法檢測動態分配的或外部數組大小。函數外的str是一個靜態定義的數組,因此其大小爲6,函數內的str實際只是一個指向字符串的指針,沒有任何額外的與數組相關的信息,因此sizeof作用於上只將其當指針看,一個指針爲4個字節,因此返回4。

5) 一個32位的機器,該機器的指針是多少位?

 指針是多少位只要看地址總線的位數就行了。80386以後的機子都是32的數據總線。所以指針的位數就是4個字節了。

6) main()
  {
   int a[5]={1,2,3,4,5};
   int *ptr=(int *)(&a+1);
   printf("%d,%d",*(a+1),*(ptr-1));
  }

  輸出:2,5

  *(a+1)就是a[1],*(ptr-1)就是a[4],執行結果是2,5
  &a+1不是首地址+1,系統會認爲加一個a數組的偏移,是偏移了一個數組的大小(本例是5個int)
  int *ptr=(int *)(&a+1);
  則ptr實際是&(a[5]),也就是a+5
  
  原因如下:

  &a是數組指針,其類型爲 int (*)[5];
  而指針加1要根據指針類型加上一定的值,不同類型的指針+1之後增加的大小不同。
  a是長度爲5的int數組指針,所以要加 5*sizeof(int)
  所以ptr實際是a[5]
  但是prt與(&a+1)類型是不一樣的(這點很重要)
  所以prt-1只會減去sizeof(int*)
  a,&a的地址是一樣的,但意思不一樣,a是數組首地址,也就是a[0]的地址,&a是對象(數組)首地址,a+1是數組下一元素的地址,即a[1],&a+1是下一個對象的地址,即a[5].

7) 請問以下代碼有什麼問題:

  int  main()
  {
   char a;
   char *str=&a;
   strcpy(str,"hello");
   printf(str);
   return 0;
  }

  沒有爲str分配內存空間,將會發生異常。問題出在將一個字符串複製進一個字符變量指針所指地址。雖然可以正確輸出結果,但因爲越界進行內在讀寫而導致程序崩潰。

8)
  char* s="AAA";
  printf("%s",s);
  s[0]='B';
  printf("%s",s);

  有什麼錯?

  "AAA"是字符串常量。s是指針,指向這個字符串常量,所以聲明s的時候就有問題。

  cosnt char* s="AAA";

  然後又因爲是常量,所以對是s[0]的賦值操作是不合法的。

9) 寫一個“標準”宏,這個宏輸入兩個參數並返回較小的一個。

  .#define Min(X, Y) ((X)>(Y)?(Y):(X))//結尾沒有;

10) 嵌入式系統中經常要用到無限循環,你怎麼用C編寫死循環。

  while(1){}或者for(;;)
軟件開發網 www.mscto.cn
11) 關鍵字static的作用是什麼?

  定義靜態變量

12) 關鍵字const有什麼含意?

  表示常量不可以修改的變量。

13) 關鍵字volatile有什麼含意?並舉出三個不同的例子?

  提示編譯器對象的值可能在編譯器未監測到的情況下改變。

14) int (*s[10])(int) 表示的是什麼?

  int (*s[10])(int) 函數指針數組,每個指針指向一個int func(int param)的函數。

15) 有以下表達式:

  int a=248; b=4;
  int const c=21;
  const int *d=&a;
  int *const e=&b;
  int const *f const =&a;

  請問下列表達式哪些會被編譯器禁止?爲什麼?

  *c=32;d=&b;*d=43;e=34;e=&a;f=0x321f;
  *c 這是個什麼東東,禁止
  *d 說了是const, 禁止
  e = &a 說了是const 禁止
  const *f const =&a; 禁止

16) 交換兩個變量的值,不使用第三個變量。即a=3,b=5,交換之後a=5,b=3;

  有兩種解法, 一種用算術算法, 一種用^(異或)
  a = a + b;
  b = a - b;
  a = a - b;
  or
  a = a^b;// 只能對int,char..
  b = a^b;
  a = a^b;
  or
  a ^= b ^= a;

17) c和c++中的struct有什麼不同?

  c和c++中struct的主要區別是c中的struct不可以含有成員函數,而c++中的struct可以。c++中struct和class的主要區別在於默認的存取權限不同,struct默認爲public,而class默認爲private。

18) #include <stdio.h>
  #include <stdlib.h>
  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操作很危險

19) char szstr[10];
    strcpy(szstr,"0123456789");
    產生什麼結果?爲什麼?
 
  長度不一樣,會造成非法的OS

20) 列舉幾種進程的同步機制,並比較其優缺點。
  
  原子操作
  信號量機制
  自旋鎖
  管程,會合,分佈式系統

21) 進程之間通信的途徑

  共享存儲系統
  消息傳遞系統
  管道:以文件系統爲基礎

22) 進程死鎖的原因

  資源競爭及進程推進順序非法

23) 死鎖的4個必要條件

  互斥、請求保持、不可剝奪、環路

24) 死鎖的處理

  鴕鳥策略、預防策略、避免策略、檢測與解除死鎖

25) 操作系統中進程調度策略有哪幾種?

  FCFS(先來先服務),優先級,時間片輪轉,多級反饋

26) 類的靜態成員和非靜態成員有何區別?

  類的靜態成員每個類只有一個,非靜態成員每個對象一個

27) 純虛函數如何定義?使用時應注意什麼?

  virtual void f()=0;
  是接口,子類必須要實現

28) 數組和鏈表的區別

  數組:數據順序存儲,固定大小
  連表:數據可以隨機存儲,大小可動態改變

29) ISO的七層模型是什麼?tcp/udp是屬於哪一層?tcp/udp有何優缺點?

  應用層
  表示層
  會話層
  運輸層
  網絡層
  物理鏈路層
  物理層
  tcp /udp屬於運輸層
  TCP 服務提供了數據流傳輸、可靠性、有效流控制、全雙工操作和多路複用技術等。
  與 TCP 不同, UDP 並不提供對 IP 協議的可靠機制、流控制以及錯誤恢復功能等。由於 UDP 比較簡單, UDP 頭包含很少的字節,比 TCP 負載消耗少。
  tcp: 提供穩定的傳輸服務,有流量控制,缺點是包頭大,冗餘性不好
  udp: 不提供穩定的服務,包頭小,開銷小 

30) (void *)ptr 和 (*(void**))ptr的結果是否相同?
        其中ptr爲同一個指針(void *)ptr 和 (*(void**))ptr值是相同的

32)
  int main()
  {
   int x=3;
   printf("%d",x);
   return 1;
  }

  問函數既然不會被其它函數調用,爲什麼要返回1?

  mian中,c標準認爲0表示成功,非0表示錯誤。具體的值是某中具體出錯信息

33) 要對絕對地址0x100000賦值,我們可以用(unsigned int*)0x100000 = 1234;那麼要是想讓程序跳轉到絕對地址是0x100000去執行,應該怎麼做?


  *((void (*)( ))0x100000 ) ( );
  首先要將0x100000強制轉換成函數指針,即:
  (void (*)())0x100000
  然後再調用它:
  *((void (*)())0x100000)();
  用typedef可以看得更直觀些:
  typedef void(*)() voidFuncPtr;
  *((voidFuncPtr)0x100000)();

34) 已知一個數組table,用一個宏定義,求出數據的元素個數

  #define NTBL
  #define NTBL (sizeof(table)/sizeof(table[0]))

35) 線程與進程的區別和聯繫? 線程是否具有相同的堆棧? dll是否有獨立的堆棧?

  進程是死的,只是一些資源的集合,真正的程序執行都是線程來完成的,程序啓動的時候操作系統就幫你創建了一個主線程。

  每個線程有自己的堆棧。DLL中有沒有獨立的堆棧?

  這個問題不好回答,或者說這個問題本身是否有問題。因爲DLL中的代碼是被某些線程所執行,只有線程擁有堆棧,如果DLL中的代碼是EXE中的線程所調用,那麼這個時候是不是說這個DLL沒有自己獨立的堆棧?如果DLL中的代碼是由DLL自己創建的線程所執行,那麼是不是說DLL有獨立的堆棧?

  以上講的是堆棧,如果對於堆來說,每個DLL有自己的堆,所以如果是從DLL中動態分配的內存,最好是從DLL中刪除,如果你從DLL中分配內存,然後在EXE中,或者另外一個DLL中刪除,很有可能導致程序崩潰。

36) unsigned short A = 10;
  printf("~A = %u/n", ~A);

  char c=128;
  printf("c=%d/n",c);

 輸出多少?並分析過程

  第一題,~A =0xfffffff5,int值 爲-11,但輸出的是uint。所以輸出4294967285

  第二題,c=0x10,輸出的是int,最高位爲1,是負數,所以它的值就是0x00的補碼就是128,所以輸出-128。
這兩道題都是在考察二進制向int或uint轉換時的最高位處理。

 37) 分析下面的程序:

  void GetMemory(char **p,int num)
  {
   *p=(char *)malloc(num);
  }      
  int main()
  {
     char *str=NULL;
   GetMemory(&str,100);
   strcpy(str,"hello");
   free(str);
     if(str!=NULL)
     {
       strcpy(str,"world");
     }   
      printf("/n str is %s",str); 軟件開發網 www.mscto.com
     getchar();
  }  

  問輸出結果是什麼?

  輸出str is world。

  free 只是釋放的str指向的內存空間,它本身的值還是存在的.所以free之後,有一個好的習慣就是將str=NULL.
此時str指向空間的內存已被回收,如果輸出語句之前還存在分配空間的操作的話,這段存儲空間是可能被重新分配給其他變量的,
儘管這段程序確實是存在大大的問題(上面各位已經說得很清楚了),但是通常會打印出world來。
這是因爲,進程中的內存管理一般不是由操作系統完成的,而是由庫函數自己完成的。

  當你malloc一塊內存的時候,管理庫向操作系統申請一塊空間(可能會比你申請的大一些),然後在這塊空間中記錄一些管理信息(一般是在你申請的內存前面一點),並將可用內存的地址返回。但是釋放內存的時候,管理庫通常都不會將內存還給操作系統,因此你是可以繼續訪問這塊地址的。

  char a[10],strlen(a)爲什麼等於15?運行的結果

38) #include "stdio.h"
  #include "string.h"

  void main()
  {
   char aa[10];
   printf("%d",strlen(aa));
  }

  sizeof()和初不初始化,沒有關係;
  strlen()和初始化有關。

39) char (*str)[20]; /*str是一個數組指針,即指向數組的指針.*/
  char *str[20];   /*str是一個指針數組,其元素爲指針型數據.*/

40) long a=0x801010;
  a+5=?
  0x801010用二進制表示爲:“1000 0000 0001 0000 0001 0000”,十進制的值爲8392720,再加上5就是8392725羅

41) 給定結構
  struct A
  {
        char t:4;
        char k:4;
        unsigned short i:8;
        unsigned long m;
  };

  問sizeof(A) = ?
  
  給定結構
  struct A
  {
        char t:4; 4位
        char k:4; 4位
        unsigned short i:8; 8位    
        unsigned long m; // 偏移2字節保證4字節對齊
  }; // 共8字節

42) 下面的函數實現在一個數上加一個數,有什麼錯誤?請改正。
  int add_n ( int n )
  {
      static int i = 100;
      i += n;
      return i;
  }

  當你第二次調用時得不到正確的結果,難道你寫個函數就是爲了調用一次?問題就出在 static上?

43) 分析一下
  #include<iostream.h>
  #include <string.h>
  #include <malloc.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <memory.h>
  typedef struct  AA
  {
         int b1:5;
         int b2:2;
  }AA;
  void main()
  {
   AA aa;
   char cc[100];
   strcpy(cc,"0123456789abcdefghijklmnopqrstuvwxyz");
   memcpy(&aa,cc,sizeof(AA));
   cout << aa.b1 <<endl;
   cout << aa.b2 <<endl;
  }

  答案是 -16和1

  首先sizeof(AA)的大小爲4,b1和b2分別佔5bit和2bit.經過strcpy和memcpy後,aa的4個字節所存放的值是: 0,1,2,3的ASC碼,即00110000,00110001,00110010,00110011所以,最後一步:顯示的是這4個字節的前5位,和之後的2位分別爲:10000,和01,因爲int是有正負之分  

  所以:答案是-16和1

44) 求函數返回值,輸入x=9999;
  int func ( x )
  {
      int countx = 0;
      while ( x )
      {
          countx ++;
          x = x&(x-1);
      }
      return countx;
  }

  結果呢?

  知道了這是統計9999的二進制數值中有多少個1的函數,且有9999=9×1024+512+256+15

  9×1024中含有1的個數爲2;
  512中含有1的個數爲1;
  256中含有1的個數爲1;
  15中含有1的個數爲4; 軟件開發網 www.mscto.com
  故共有1的個數爲8,結果爲8。
  1000 - 1 = 0111,正好是原數取反。這就是原理。
  用這種方法來求1的個數是很效率很高的。
  不必去一個一個地移位。循環次數最少。

  int a,b,c 請寫函數實現C=a+b ,不可以改變數據類型,如將c改爲long int,關鍵是如何處理溢出問題
  bool add (int a, int b,int *c)
  {
   *c=a+b;
   return (a>0 && b>0 &&(*c<a || *c<b) || (a<0 && b<0 &&(*c>a || *c>b)));
  }

45) 分析:
  struct bit
  { 
     int a:3;
     int b:2;
      int c:3;
  };
  int main()
  {
    bit s;
    char *c=(char*)&s;
     cout<<sizeof(bit)<<endl;
    *c=0x99;
     cout << s.a <<endl <<s.b<<endl<<s.c<<endl;
     int a=-1;
    printf("%x",a);
    return 0;
  }

  輸出爲什麼是
  4
  1
  -1
  -4
  ffffffff

    因爲0x99在內存中表示爲 100 11 001 , a = 001, b = 11, c = 100。當c爲有符合數時, c = 100, 最高1爲表示c爲負數,負數在計算機用補碼錶示,所以c = -4;同理 b = -1;當c爲有符合數時, c = 100,即 c = 4,同理 b = 3。

46) 位域 : 

  有些信息在存儲時,並不需要佔用一個完整的字節, 而只需佔幾個或一個二進制位。例如在存放一個開關量時,只有0和1 兩種狀態, 用一位二進位即可。爲了節省存儲空間,並使處理簡便,C語言又提供了一種數據結構,稱爲“位域”或“位段”。所謂“位域”是把一個字節中的二進位劃分爲幾個不同的區域, 並說明每個區域的位數。每個域有一個域名,允許在程序中按域名進行操作。 這樣就可以把幾個不同的對象用一個字節的二進制位域來表示。一、位域的定義和位域變量的說明位域定義與結構定義相仿,其形式爲:   

  struct 位域結構名 { 位域列表 };  其中位域列表的形式爲: 類型說明符 位域名:位域長度
  
  例如:   
  struct bs  
  {  
   int a:8;  
   int b:2;  
   int c:6;  
  };  

  位域變量的說明與結構變量說明的方式相同。 可採用先定義後說明,同時定義說明或者直接說明這三種方式。例如:   
  struct bs  
  {  
   int a:8;  
   int b:2;  
   int c:6;  
  }data;  

  說明data爲bs變量,共佔兩個字節。其中位域a佔8位,位域b佔2位,位域c佔6位。對於位域的定義尚有以下幾點說明:  

  一個位域必須存儲在同一個字節中,不能跨兩個字節。如一個字節所剩空間不夠存放另一位域時,應從下一單元起存放該位域。也可以有意使某位域從下一單元開始。例如:   
  
  struct bs  
  {  
   unsigned a:4  
   unsigned :0 /*空域*/  
   unsigned b:4 /*從下一單元開始存放*/  
   unsigned c:4  
  }  

  在這個位域定義中,a佔第一字節的4位,後4位填0表示不使用,b從第二字節開始,佔用4位,c佔用4位。  

  由於位域不允許跨兩個字節,因此位域的長度不能大於一個字節的長度,也就是說不能超過8位二進位。  

  位域可以無位域名,這時它只用來作填充或調整位置。無名的位域是不能使用的。例如:   

  struct k  
  {  
   int a:1  
   int :2 /*該2位不能使用*/  
   int b:3  
   int c:2  
  };  

  從以上分析可以看出,位域在本質上就是一種結構類型, 不過其成員是按二進位分配的。  

  位域的使用位域的使用和結構成員的使用相同,其一般形式爲: 位域變量名?位域名 位域允許用各種格式輸出。  

  main()
  {  
   struct bs  
   {  
    unsigned a:1;  
    unsigned b:3;  
    unsigned c:4;  
   }
   bit,*pbit;  
   bit.a=1;  
   bit.b=7;  
   bit.c=15;  
   pri

47) 改錯:
  #include <stdio.h>
  int main(void)
  {
    int **p;
     int arr[100];
    p = &arr;
    return 0;
  }

  解答:搞錯了,是指針類型不同,int **p; //二級指針&arr; //得到的是指向第一維爲100的數組的指針
  
  #include <stdio.h>
  int main(void)
  {
   int **p, *q;
   int arr[100];
   q = arr;
   p = &q;
   return 0;
  }

48) 下面這個程序執行後會有什麼錯誤或者效果:

  #define MAX 255
  int main()
  {
     unsigned char A[MAX],i;//i被定義爲unsigned char
     for (i=0;i<=MAX;i++)
      A[i]=i;
       return 0;   
  }

  解答:死循環加數組越界訪問(C/C++不進行數組越界檢查)MAX=255 數組A的下標範圍爲:0..MAX-1,這是其一..
    其二.當i循環到255時,循環內執行:A[255]=255;這句本身沒有問題..但是返回for (i=0;i<=MAX;i++)語句時,由於unsigned char的取值範圍在(0..255),i++以後i又爲0了..無限循環下去。

49) struct name1
  {
     char  str;
     short x;
     int   num;
  }
  struct name2
  {
     char str;
     int num;
     short x;
  }

  sizeof(struct name1)=8,sizeof(struct name2)=12

  在第二個結構中,爲保證num按四個字節對齊,char後必須留出3字節的空間;同時爲保證整個結構的自然對齊(這裏是4字節對齊),在x後還要補齊2個字節,這樣就是12字節。

50) intel:

  A.c 和B.c兩個c文件中使用了兩個相同名字的static變量,編譯的時候會不會有問題?這兩個static變量會保存到哪裏(棧還是堆或者其他的)?
    static的全局變量,表明這個變量僅在本模塊中有意義,不會影響其他模塊。他們都放在數據區,但是編譯器對他們的命名是不同的。如果要使變量在其他模塊也有意義的話,需要使用extern關鍵字。


51) struct s1
  {
     int i: 8;
     int j: 4;
     int a: 3;
     double b;
  };

  struct s2
  {
     int i: 8;
     int j: 4;
     double b;
     int a:3;
  };

  printf("sizeof(s1)= %d/n", sizeof(s1));
  printf("sizeof(s2)= %d/n", sizeof(s2));
  result: 16, 24
  第一個struct s1
  {
     int i: 8;
     int j: 4;
     int a: 3;
     double b;
  };

  理論上是這樣的,首先是i在相對0的位置,佔8位一個字節,然後,j就在相對一個字節的位置,由於一個位置的字節數是4位的倍數,因此不用對齊,就放在那裏了,然後是a,要在3位的倍數關係的位置上,因此要移一位,在15位的位置上放下,目前總共是18位,折算過來是2字節2位的樣子,由於double 是8字節的,因此要在相對0要是8個字節的位置上放下,因此從18位開始到8個字節之間的位置被忽略,直接放在8字節的位置了,因此,總共是16字節。 

  第二個最後會對照是不是結構體內最大數據的倍數,不是的話,會補成是最大數據的倍數。

 

 

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