程序員面試寶典第四版第五章

1.在C++中同一個程序中可以定義相同名字的變量的,但是先定義的變量如i會被後定義的變量i給覆蓋,
int i = 1;
void main()
{
int i = i;
}
這裏,main函數裏面的i與main(外的i無關,而是一個未定義的值。)

2.1.“==” 與 “=”
“==”: 是判斷左右兩值是否相等,一般情況下,最好將常量作爲左值,因爲常量在“=”中不能不能作爲左值,如果將“==”寫成了“=”,這樣編譯器就會報錯。
“=”: 是賦值語句,即將右邊的值賦給左邊的變量中,因此,”=“左邊不能爲常量。
2.2位與 與 與運算(位或 與 或運算)
”&“: 這個是位與,計算時需要將數轉換爲二進制之後進行相應位之間的與運算。位或(|)同理。4 & 0 = 4.  4 | 0 = 4.
”&&“: 是與運算,即左右兩邊的值只有零與非零兩種情況,只有當兩邊的值都不爲零的時候才位1.或運算(||)也同樣的看待。4 && 0 = 0.   4 || 0 =1. 

3.二進制中包含1的個數
int func(int x)
{
int count = 0;
while(1)
{
count ++;
x = x &(x-1);
}
return count;
}
該函數返回的是x轉換爲二進制之後爲1的個數。假如x = 9999(10011100001111),count = 8;
count = 10011100001111 & 10011100001110 = 10011100001110
count = 10011100001110 & 10011100001101 = 10011100001100
count = 10011100001100 & 10011100001011 = 10011100001000
count = 10011100001000 & 10011100000111 = 10011100000000
count = 10011100000000 & 10011011111111 = 10011000000000
count = 10011000000000 & 10010111111111 = 10010000000000
count = 10010000000000 & 10001111111111 = 10000000000000
count = 10000000000000 & 01111111111111 = 0


4.1. "&&"與運算中,左右兩邊值真才爲真,當左邊的值爲假時右邊的條件就不執行了。
4.2. i++: 是先對i進行了操作之後,才進行i++;
++i: 是先進行i + 1之後纔對i進行操作。


5. C中printf計算參數時是從右到左壓棧的。
main()
{
int b = 3;
int arr[] = {6,7,8,9,10};
int * ptr = arr;
*(ptr++) += 123;
/* 打印結果:129 7 8 8 */
printf("%d %d %d %d\n",*(ptr - 2),*(ptr - 1),*ptr,*(++ptr));
/* 打印結果:7 7 8 8 */
printf("%d %d %d %d\n",*(ptr - 1),*(ptr - 1),*ptr,*(++ptr));
}
分析:首先一開始指針ptr是指向數組arr的第0個下標,即指向6,
但是*(ptr++) += 123;先對*ptr += 123 = 129進行了操作之後,ptr + 1,即指針指向數組的1下標,即7.
在printf計算參數時是從右到左壓棧的,所以第一個進行運算的參數也是最右邊的參數是*(++ptr):即指針ptr + 1,指向數組下標2的數,即8,然後取*(ptr)打印出來,打印8.
第二個參與運算的參數是*ptr,因爲這個時候指針沒有進行任何的移動,所以指針還是指向數組下標爲2的數,即8.
同樣的道理,下一個參數爲*(ptr - 1):指針先減1,即ptr指向數組下標爲1的數,即7.

疑惑:*(++ptr) 與 *(ptr + 1)的區別
*(++ptr): 操作結束之後,指針會進行ptr+ 1移動,而*(ptr + 1)進行操作的時候,只是根據指針ptr+ 1 計算對應的值出來,但是指針還是指向原來的位置,並沒有進行ptr + 1移動。

6.1. if ('A' == a)的寫法好過if (a == 'A'),因爲這時如果將”==“誤寫爲”=“,因爲編譯器不允許對常量賦值,就可以檢查到錯誤。
6.2. 在進行循環運算的時候,一些算法可以放到循環體外,這樣不需要每次進行循環的時候都要進行運算,可以提高程序的效率,但是缺點就是代碼不夠簡潔。


7.浮點數在內存中的存放方式:
任何數據在內存中都是以二進制的形式存儲的,在Intel CPU架構的系統中,存放的方式是小端的方式。
浮點數在目前所有的C/C++編譯器都是採用IEEE所制定的標準浮點格式,即二進制科學表示法。
V = (-1)s * M * 2E的形式來表示一個數:
s(sign)符號: 決定這個數是負數(s = 1)還是正數(s = 0).對於數值0的符號位作爲特殊情況處理。
M(significand)尾數:(規定M的整數部分恆爲1,所以這個1就不進行存儲了) 是一個二進制小數(有效數字位),它的範圍是1~2的n次方-1,或者是0~1-2的n次方。
E(exponent)階碼: 對浮點數據加權,這個權重的2的E次冪。
類型 符號位 階碼 尾數 Bias(偏置值)
float 1 8 23 127 (32位)
double 1 11 52 1023 (64位)
舉例:float型數據125.5轉爲標準浮點格式:
125的二進制表示形式1111101,小數部分表示爲二進制位1,則125.5二進制表示爲1111101.1,
由於規定尾數的整數部分恆爲1,則表示爲1.1111011 * 2^6, 階碼爲6,則加上偏置位127爲133,133的二進制:10000101
對於尾數將整數部分1去掉,爲1111011(1.1111011的尾數),在其後面補0使其位數達到23位:11110110000000000000000.
則125.5的二進制表現形式:0 10000101 11110110000000000000000
然後125.5在內存中的存放方式爲:
高地址 低地址
01000010 11111011 00000000 00000000

int main()
{
float a = 1.0f;
/* 強制輸出a 的整數部分。 */
cout << (int)a << endl;
/* 輸出的是在內存中a所對應的地址。*/
cout << &a << endl;
/*  浮點數在內存裏和整數的存儲方式不同。(int &)a相當於將該浮點數地址開始的sizeof(int)個字節當成(int)型的數據輸出,因此這取決於float型數據在內存中的存儲方式*/
cout << (int &)a << endl;
/* boolalpha: 函數名稱,功能是把bool值顯示爲true 或false. 否則輸出將爲1/0 */
cout << boolalpha << ((int)a == (int &)a) << endl;

float b = 1.0f;
cout << (int)b = endl;
cout << &b << endl;
cout << (int &)b << endl;
/* float a = 1.0f在內存中的表示都是3f800000,而浮點數和一般的整型不同,當(int &)b強制轉換時,會把內存值3f800000當做int型輸出,*/
cout << boolalpha << ((int)b == (int &)b) << endl;
return 0;
}


8.指針類型的轉換
時時刻刻記住一點,所有的指針都是佔四個字節。
int main()
{
unsigned int a = 0xFFFFFFF7;
/* 將int類型的a轉換爲unsigned char類型,由於int類型在內存中佔4個字節,而unsigned char 類型在內存中佔1個字節,所以輸出的時候只能輸出1個低字節,3位和高於3位的數據將被程序自動拋棄。*/
unsigned char i = (unsigned char)a;
/* 0x000000F7. */
printf("%08x\n",i);
/* 將&a是一個指向unsigned int的指針,現在被強制轉換爲char *型的指針,因爲所有類型的指針在內存中都是佔4個字節,所以沒有數據丟失,可以輸出原本的數據。*/
char *b = (char *)&a;
printf("%08x\n,"*b);
}

9.C語言中符號運算符的優先級
第1級: [] () . -> (從左到右)
第2級: -   ~ ++ -- * & ! (type) sizeof (右到左)
第3級: / * %
第4級: + -
第5級: << >>
第6級: > >= < <=
第7級: == !=
第8級: &
第9級: ^
第10級: |
第11級: &&
第12級: ||
第13級: ?:
第14級: = /= *= %= += -= <<= >>= &= ^= |=
第15級:
例子: b = ~a >> 4 + 1;
分析:b = (~a) >> (4 + 1);

10.判斷一個數是否是2的n次方
如果該數是2的n次方的話: 00000000 0
00000001 1
00000010 2
00000100 4
00001000 8
00010000 16
00100000 32
01000000 64
10000000 128
那麼該數x與(x-1) 相位與的話,結果便爲0. 00000100 & 00000011 =  0.
所以可以根據 !(x & (x - 1))。


11.
int f(int x, int y)
{
return (x & y) + ((x ^ y)>> 1);
}
那麼:(729,271) = __.
分析:(x & y) :是表示x 與 y 相應位都爲1的時候才爲1,這個結果是x與y的相同位的一半。
 (x ^ y) >> 1: 是表達的x 與 y 相應位不同的時候就爲1(即表示的是x 與 y 的不同位),>>1:可以表示除2(x 與 y 的不同位的一半).
那麼f(x,y)求的是x與y的平均值。

12.位運算實現兩個整數的加法運算。
int Add(int a, int b)
{
if(b == 0)
{
return a;
}

int sum, carry;
sum = a ^ b; //位運算沒有進位
carry = (a & b) << 1; //完成第二步進位的加法運算並且左移
return Add(sum, carry); //進行遞歸,相加。

}

13.找出兩個數的較大數
1. int max = ((a + b) + abs(a - b)) / 2;
/*根據兩數相減後的數的符號,如果爲負數,那麼31位存放的是1, 如果是正數,31位存放的是0. */
2. int c = a - b;
char * strs[2] = {"a Large ", “b Large ”};
c = unsigned(c) >> (sizeof(int) * 8 - 1);


14.獲得三個數中的中間數(非最大,非最小)
inline int max(int a, int b){return a>= b ? a : b;}
inline int min(int a, int b){return a<= b ? a :b;}
inline int medium(int a, int b, int c)
{ //求兩兩數的最大值,則可以排除最小值
int t1 = max(a,b);
int t2 = max(a,b);
int t3 = max(a,b);
//返回最大兩個數中的最小值,那麼就是中間值
return min(t1, min(t2, t3));
}

15.a,b兩個數不使用中間值進行交換
1. 缺點就是如果a,b都是較大的兩個數,a = a + b會越界
a = a + b; //兩個數相加,爲接下來做準備
b = a - b; //a - b得到的就是原來的a,賦給b
a = a - b; //a - b得到的就是原來的b,賦給a
2. 最好的方法
a = a ^ b; //得到相應位不同則爲1,
b = a ^ b; //又進行異或,則可以得到原來的a
a = a ^ b; //得到原來的b

16.在C++程序中被C編譯器編譯後的函數,爲什麼要用“exter C”?
因爲C++語言支持函數重載,C語言不支持函數重載。函數被C++編譯後在庫中的名字與C語言的不同。假設某個函數的原型爲void foo(int x, int y).
該函數被C編譯器編譯後在庫中的名字爲_foo,而C++編譯器則會產生像_foo_int_int之類的名字。
C++提供了C連接交換制定符合extern "C"解決名字匹配問題。

17.頭文件中的ifndef/define/endif是幹什麼用的?
防止該頭文件被重複引用。

18.switch語句:
當從滿足case 'c':開始,如果執行了相應的指令之後沒有break終止switch
語句,那麼餘下的case操作,不管滿不滿足相應的條件,都會被執行的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章