『壹』 c語言位運算
只舉一例,餘下的樓主自己查閱「與」、「或」及其它概念的定義。
位與:
resand=czs1&czs2
czs1=521=0x209=0b1000001001
czs2=123=0x7B= 0b01111011
位與的意思是:將某兩個數轉換位二進制(即上述的0bxx),自最低位起將二進制的每一位進行比較,兩個數的該位均為1時,最終位與的結果中該位也為1;否則最終位與結果中該位問0.
czs1與czs2位與後,最低位和第四位均為1,即最終位與結果為0b1001,即9.
『貳』 C語言中的位運算是怎樣的
C語言是為描述系統而設計的,它的第一個應用就是UNIX操作系統的設計,因此必須具有低級語言的特點。指針運算和位運算就是其代表。
我們知道,計算機中的信息以及控制信號均是以二進制碼的0、1的形式存儲和處理的。每一個0或1稱為一個「位」(bit),8位構成一個位元組(byte)。在內存中,每一個位元組均有一個編號,稱為內存地址。
在以前的各章節中,我們都是將一個位元組(如字元型)和若干位元組(如整型、實型等)中的內容作為一個整體進行處理的(賦值、運算、I/O操作)。
位運算則是針對這些位元組中的若干位進行操作。這在系統軟體設計中和自動控制中是十分有用的。
C語言的位運算符有~,<<,
>>,
&,
|,
^共6個,並可與賦值運算符相結合(除~外)成為位運算賦值操作。
應注意:參與位運算的量只能是整型和字元型。
『叄』 c語言位運算符的用法
c語言位運算符的用法如下:
一、位運算符C語言提供了六種位運算符:
& 按位與
| 按位或
^ 按位異或
~ 取反
<< 左移
>> 右移
1. 按位與運算
按位與運算符"&"是雙目運算符。其功能是參與運算的兩數各對應的二進位相與。只有對應的兩個二進位均為1時,結果位才為1 ,否則為0。參與運算的數以補碼方式出現。
例如:9&5可寫算式如下: 00001001 (9的二進制補碼)&00000101 (5的二進制補碼) 00000001 (1的二進制補碼)可見9&5=1。
按位與運算通常用來對某些位清0或保留某些位。例如把a 的高八位清 0 , 保留低八位, 可作 a&255 運算 ( 255 的二進制數為0000000011111111)。
main(){
int a=9,b=5,c;
c=a&b;
printf("a=%d/nb=%d/nc=%d/n",a,b,c);
}
2. 按位或運算
按位或運算符「|」是雙目運算符。其功能是參與運算的兩數各對應的二進位相或。只要對應的二個二進位有一個為1時,結果位就為1。參與運算的兩個數均以補碼出現。
例如:9|5可寫算式如下: 00001001|00000101
00001101 (十進制為13)可見9|5=13
main(){
int a=9,b=5,c;
c=a|b;
printf("a=%d/nb=%d/nc=%d/n",a,b,c);
}
3. 按位異或運算
按位異或運算符「^」是雙目運算符。其功能是參與運算的兩數各對應的二進位相異或,當兩對應的二進位相異時,結果為1。參與運算數仍以補碼出現,例如9^5可寫成算式如下: 00001001^00000101 00001100 (十進制為12)。
main(){
int a=9;
a=a^15;
printf("a=%d/n",a);
}
4. 求反運算
求反運算符~為單目運算符,具有右結合性。 其功能是對參與運算的數的各二進位按位求反。例如~9的運算為: ~(0000000000001001)結果為:1111111111110110。
5. 左移運算
左移運算符「<<」是雙目運算符。其功能把「<< 」左邊的運算數的各二進位全部左移若干位,由「<<」右邊的數指定移動的位數,高位丟棄,低位補0。例如: a<<4 指把a的各二進位向左移動4位。如a=00000011(十進制3),左移4位後為00110000(十進制48)。
6. 右移運算
右移運算符「>>」是雙目運算符。其功能是把「>> 」左邊的運算數的`各二進位全部右移若干位,「>>」右邊的數指定移動的位數。
例如:設 a=15,a>>2 表示把000001111右移為00000011(十進制3)。 應該說明的是,對於有符號數,在右移時,符號位將隨同移動。當為正數時, 最高位補0,而為負數時,符號位為1,最高位是補0或是補1 取決於編譯系統的規定。Turbo C和很多系統規定為補1。
main(){
unsigned a,b;
printf("input a number: ");
scanf("%d",&a);
b=a>>5;
b=b&15;
printf("a=%d/tb=%d/n",a,b);
}
請再看一例!
main(){
char a='a',b='b';
int p,c,d;
p=a;
p=(p<<8)|b;
d=p&0xff;
c=(p&0xff00)>>8;
printf("a=%d/nb=%d/nc=%d/nd=%d/n",a,b,c,d);
}
C語言位運算。所謂位運算,就是對一個比特(Bit)位進行操作。比特(Bit)是一個電子元器件,8個比特構成一個位元組(Byte),它已經是粒度最小的可操作單元了。
C語言提供了六種位運算符:
按位與運算(&)
一個比特(Bit)位只有 0 和 1 兩個取值,只有參與&運算的兩個位都為 1 時,結果才為 1,否則為 0。例如1&1為 1,0&0為 0,1&0也為 0,這和邏輯運算符&&非常類似。
C語言中不能直接使用二進制,&兩邊的操作數可以是十進制、八進制、十六進制,它們在內存中最終都是以二進制形式存儲,&就是對這些內存中的二進制位進行運算。其他的位運算符也是相同的道理。
例如,9 & 5可以轉換成如下的運算:
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001 (9 在內存中的存儲)
& 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101 (5 在內存中的存儲)
-----------------------------------------------------------------------------------
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0001 (1 在內存中的存儲)
也就是說,按位與運算會對參與運算的兩個數的所有二進制位進行&運算,9 & 5的結果為 1。
又如,-9 & 5可以轉換成如下的運算:
1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111 (-9 在內存中的存儲)
& 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101 (5 在內存中的存儲)
-----------------------------------------------------------------------------------
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101 (5 在內存中的存儲)
-9 & 5的結果是 5。
關於正數和負數在內存中的存儲形式,我們已在教程《整數在內存中是如何存儲的》中進行了講解。
再強調一遍,&是根據內存中的二進制位進行運算的,而不是數據的二進制形式;其他位運算符也一樣。以-9&5為例,-9 的在內存中的存儲和 -9 的二進制形式截然不同:
1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111 (-9 在內存中的存儲)
-0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001 (-9 的二進制形式,前面多餘的 0 可以抹掉)
按位與運算通常用來對某些位清 0,或者保留某些位。例如要把 n 的高 16 位清 0 ,保留低 16 位,可以進行n & 0XFFFF運算(0XFFFF 在內存中的存儲形式為 0000 0000 -- 0000 0000 -- 1111 1111 -- 1111 1111)。
【實例】對上面的分析進行檢驗。
00001. #include
00002.
00003. int main(){
00004. int n = 0X8FA6002D;
00005. printf("%d, %d, %X ", 9 & 5, -9 & 5, n & 0XFFFF);
00006. return 0;
00007. }
運行結果:
1, 5, 2D
按位或運算(|)
參與|運算的兩個二進制位有一個為 1 時,結果就為 1,兩個都為 0 時結果才為 0。例如1|1為1,0|0為0,1|0為1,這和邏輯運算中的||非常類似。
例如,9 | 5可以轉換成如下的運算:
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001 (9 在內存中的存儲)
| 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101 (5 在內存中的存儲)
-----------------------------------------------------------------------------------
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1101 (13 在內存中的存儲)
9 | 5的結果為 13。
又如,-9 | 5可以轉換成如下的運算:
1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111 (-9 在內存中的存儲)
| 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101 (5 在內存中的存儲)
-----------------------------------------------------------------------------------
1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111 (-9 在內存中的存儲)
-9 | 5的結果是 -9。
按位或運算可以用來將某些位置 1,或者保留某些位。例如要把 n 的高 16 位置 1,保留低 16 位,可以進行n | 0XFFFF0000運算(0XFFFF0000 在內存中的存儲形式為 1111 1111 -- 1111 1111 -- 0000 0000 -- 0000 0000)。
【實例】對上面的分析進行校驗。
00001. #include
00002.
00003. int main(){
00004. int n = 0X2D;
00005. printf("%d, %d, %X ", 9 | 5, -9 | 5, n | 0XFFFF0000);
00006. return 0;
00007. }
運行結果:
13, -9, FFFF002D
按位異或運算(^)
參與^運算兩個二進制位不同時,結果為 1,相同時結果為 0。例如0^1為1,0^0為0,1^1為0。
例如,9 ^ 5可以轉換成如下的運算:
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001 (9 在內存中的存儲)
^ 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101 (5 在內存中的存儲)
-----------------------------------------------------------------------------------
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1100 (12 在內存中的存儲)
9 ^ 5的結果為 12。
又如,-9 ^ 5可以轉換成如下的運算:
1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111 (-9 在內存中的存儲)
^ 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101 (5 在內存中的存儲)
-----------------------------------------------------------------------------------
1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0010 (-14 在內存中的存儲)
-9 ^ 5的結果是 -14。
按位異或運算可以用來將某些二進制位反轉。例如要把 n 的高 16 位反轉,保留低 16 位,可以進行n ^ 0XFFFF0000運算(0XFFFF0000 在內存中的存儲形式為 1111 1111 -- 1111 1111 -- 0000 0000 -- 0000 0000)。
【實例】對上面的分析進行校驗。
00001. #include
00002.
00003. int main(){
00004. unsigned n = 0X0A07002D;
00005. printf("%d, %d, %X ", 9 ^ 5, -9 ^ 5, n ^ 0XFFFF0000);
00006. return 0;
00007. }
運行結果:
12, -14, F5F8002D
取反運算(~)
取反運算符~為單目運算符,右結合性,作用是對參與運算的二進制位取反。例如~1為0,~0為1,這和邏輯運算中的!非常類似。。
例如,~9可以轉換為如下的運算:
~ 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001 (9 在內存中的存儲)
-----------------------------------------------------------------------------------
1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0110 (-10 在內存中的存儲)
所以~9的結果為 -10。
例如,~-9可以轉換為如下的運算:
~ 1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111 (-9 在內存中的存儲)
-----------------------------------------------------------------------------------
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1000 (9 在內存中的存儲)
所以~-9的結果為 8。
【實例】對上面的分析進行校驗。
00001. #include
00002.
00003. int main(){
00004. printf("%d, %d ", ~9, ~-9 );
00005. return 0;
00006. }
運行結果:
-10, 8
左移運算(<<)
左移運算符<<用來把操作數的各個二進制位全部左移若干位,高位丟棄,低位補0。
例如,9<<3可以轉換為如下的運算:
<< 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001 (9 在內存中的存儲)
-----------------------------------------------------------------------------------
0000 0000 -- 0000 0000 -- 0000 0000 -- 0100 1000 (72 在內存中的存儲)
所以9<<3的結果為 72。
又如,(-9)<<3可以轉換為如下的運算:
<< 1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111 (-9 在內存中的存儲)
-----------------------------------------------------------------------------------
1111 1111 -- 1111 1111 -- 1111 1111 -- 1011 1000 (-72 在內存中的存儲)
所以(-9)<<3的結果為 -72
如果數據較小,被丟棄的高位不包含 1,那麼左移 n 位相當於乘以 2 的 n 次方。
【實例】對上面的結果進行校驗。
00001. #include
00002.
00003. int main(){
00004. printf("%d, %d ", 9<<3, (-9)<<3 );
00005. return 0;
00006. }
運行結果:
72, -72
右移運算(>>)
右移運算符>>用來把操作數的各個二進制位全部右移若干位,低位丟棄,高位補 0 或 1。如果數據的最高位是 0,那麼就補 0;如果最高位是 1,那麼就補 1。
例如,9>>3可以轉換為如下的運算:
>> 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001 (9 在內存中的存儲)
-----------------------------------------------------------------------------------
0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0001 (1 在內存中的存儲)
所以9>>3的結果為 1。
又如,(-9)>>3可以轉換為如下的運算:
>> 1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111 (-9 在內存中的存儲)
-----------------------------------------------------------------------------------
1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 1110 (-2 在內存中的存儲)
所以(-9)>>3的結果為 -2
如果被丟棄的低位不包含 1,那麼右移 n 位相當於除以 2 的 n 次方(但被移除的位中經常會包含 1)。
【實例】對上面的結果進行校驗。
00001. #include
00002.
00003. int main(){
00004. printf("%d, %d ", 9>>3, (-9)>>3 );
00005. return 0;
00006. }
運行結果:
1, -2
一、位運算符
在計算機中,數據都是以二進制數形式存放的,位運算就是指對存儲單元中二進制位的運算。C語言提供6種位運算符。
二、位運算
位運算符 & |~<< >> ∧ 按優先順序從高到低排列的順序是:
位運算符中求反運算「~「優先順序最高,而左移和右移相同,居於第二,接下來的順序是按位與 「&「、按位異或 「∧「和按位或 「|「。順序為~ << >> & ∧ | 。
例1:左移運算符「<<」是雙目運算符。其功能把「<< 」左邊的運算數的各二進位全部左移若干位,由「<<」右邊的數指定移動的位數,高位丟棄,低位補0。
例如:
a<<4
指把a的各二進位向左移動4位。如a=00000011(十進制3),左移4位後為00110000(十進制48)。
例2:右移運算符「>>」是雙目運算符。其功能是把「>> 」左邊的運算數的各二進位全部右移若干位,「>>」右邊的數指定移動的位數。
例如:
設 a=15,
a>>2
表示把000001111右移為00000011(十進制3)。
應該說明的是,對於有符號數,在右移時,符號位將隨同移動。當為正數時,最高位補0,而為負數時,符號位為1,最高位是補0或是補1 取決於編譯系統的規定。
例3:設二進制數a是00101101 ,若通過異或運算a∧b 使a的高4位取反,低4位不變,則二進制數b是。
解析:異或運算常用來使特定位翻轉,只要使需翻轉的位與1進行異或操作就可以了,因為原數中值為1的位與1進行異或運算得0 ,原數中值為0的位與1進行異或運算結果得1。而與0進行異或的位將保持原值。異或運算還可用來交換兩個值,不用臨時變數。
如 int a=3 , b=4;,想將a與b的值互換,可用如下語句實現:
a=a∧b;
b=b∧a;
a=a∧b;
所以本題的答案為: 11110000 。
『肆』 C語言中的位運算符有哪些
C 語言中有以下位運算符:
&(按位與):將兩個數的二進制每一位同時與(AND)起來,並將結果賦給左操作數。
|(按位或):將兩個數的二進制每一位同時或(OR)起來,並將結果賦給左操作數。
^(按位異或):將兩個數的二進制每一位同時異或(XOR)起來,並將結果賦給左操作數。
<<(左移):將左操作數的二進制數向左移動右操作數指定的位數,並將結果賦給左操作數。
>>(右移):將左操作數的二進制數向右移動右操作數指定的位數,並將結果賦給左操作數。
~(按位取反):將左操作數的二進制數按位取反(即 1 變為 0,0 變為 1),並將結果賦給左操作數。
希望這些可以幫助到你。
『伍』 linux C - C基礎篇(一)
1) 最高位是符號位,0表示正數,1表示負數。
2) 如果是正數,正常表示(即原碼),如果是負數,先求反碼,再求補碼(補碼為反碼加1)即為他的二進製表示。例如char型的1,用二進製表示為00000001;char型的-1用二進製表示為11111111。
常量,就是在程序運行過程中值無法被改變;變數,就是在程序運行過程中內部存儲的值隨時可以被改變。變數是「可讀、可寫」,而常量,是「只讀」的。
整形常量:12、浮點型常量:12.35、實數型常量:1.3e-5、字元型常量:'a'、字元串型常量:"abc"、標識符常量:#define LEN 10。
變數一般存放在內存中:棧區, 靜態數據區,全局變數區, 堆區。函數中定義的變數,如果不加特殊修飾,都是保存在棧區,函數調用結束,棧空間被自動釋放。
定義變數:
<存儲類型> <數據類型> <變數名>
static/auto/... int/char a
例如:static int a;char c;
存儲類型:
自動類型轉換
將一種類型的數據賦值給另外一種類型的變數時就會發生自動類型轉換,例如:float a = 10;10是int類型的數據,需要先轉換成float類型然後再賦值給變數a。
在不同類型的混合運算中,編譯器也會自動地轉換數據類型,將參與運算的所有數據先轉換為同一種類型,然後再進行計算。轉換的規則如下:
強制類型轉換
自動類型轉換是編譯器根據代碼自己判斷的,有時候我們需要手動的進行數據類型轉換,稱之為強制類型轉換,例如:
(int)(a+b); //將a + b 求得結果的數據類型強制轉換為int型。
運算符的優先順序
特殊運算符>單目運算符>雙目運算符>三目運算>賦值運算>逗號運算
『陸』 c語言位運算問題
c語言位運算問題解答:
要解答這個問題我們先來看一個例子,代碼如下圖一,圖中右邊是問題中得到代碼,左邊是這段代碼的匯編指令。兩種情況的不同點詳細說明如下:
第一種情況:
printf("%d",2>>64);
由圖中的匯編代碼可以看出,如果兩個數都是常數的情況下,代碼中是不含對應的匯編指令的,因為編譯器已經省略了,編譯器算出結果為0,直接把0傳入給printf函數。
第二種情況:
int i=2;
printf("%d",i>>64);
此時i是個變數,編譯器沒法在編譯時算出結果,這時就需要通過SARL算術右移指令進行,這里考慮到溢出這個指令有一個調整機制,它會根據左操作數i類型來調整右操作數,比如這里i是4位元組32位,也就是它最多右移32位,如果右操作數大於32它會進行64%32=0,所以i>>64相當於i>>0,如果是i>>63,63%32=31相當於i>>31。這種調整不同的編譯器可能會不同,目前GCC是這樣的。
所以這個代碼在GCC環境下輸出結果為:
0
2
補充說明:如果想要了解更多可以寫出代碼,然後觀察編譯出來的匯編代碼。
gcc -S test.c
圖一
『柒』 求高手解答:Windows和Linux下運行C代碼運算,在速度上有什麼不同么
我覺得會有影響吧。
windows在啟動時會啟動很多程序,佔用系統資源。有很多程序和進程是不可控的。
也就是你必須要添加的。
而linux的運行是可控的,你可以使用系統啟動時,負載很小,把更多的資源分配給你所要使用
的應用程序。這樣一比,在處理速度上,你說誰更快。
『捌』 Linux C編程從初學到精通的目 錄
第1部分 基礎篇
第1章 Linux系統概述 1
1.1 什麼是Linux 2
1.2 Linux系統特點及主要功能 2
1.2.1 Linux系統特點 3
1.2.2 Linux系統的主要功能 3
1.3 Linux的內核版本和發行版本 5
1.4 系統的安裝 6
1.4.1 系統安裝前的准備工作 6
1.4.2 從光碟安裝Linux 6
1.4.3 從硬碟安裝Linux 22
1.4.4 在虛擬機下安裝Linux 22
1.5 Shell的使用 27
1.5.1 Shell簡介 27
1.5.2 常見Shell的種類 28
1.5.3 Shell的簡單使用 29
1.5.4 通配符 30
1.5.5 引號 31
1.5.6 注釋符 33
1.6 Linux常用命令 33
1.6.1 與目錄相關的命令 33
1.6.2 與文件相關的命令 34
1.6.3 與網路服務相關的命令 35
1.7 本章小結 35
實戰演練 36
第2章 C語言編程基礎 37
2.1 C語言的歷史背景 38
2.2 C語言的特點 38
2.3 C語言的基本數據類型 39
2.3.1 整型 39
2.3.2 實型 40
2.3.3 字元型 41
2.4 運算符與表達式 43
2.4.1 算術運算符與算術表達式 43
2.4.2 賦值運算符與賦值表達式 44
2.4.3 逗號運算符與逗號表達式 45
2.5 C程序的3種基本結構 46
2.5.1 順序結構 46
2.5.2 選擇結構 47
2.5.3 循環結構 51
2.6 C語言中的數據輸入與輸出 54
2.6.1 字元輸出函數putchar 54
2.6.2 字元輸入函數getchar 54
2.6.3 格式輸出函數printf 54
2.6.4 格式輸入函數scanf 56
2.7 函數 57
2.7.1 函數的定義 57
2.7.2 函數的調用 58
2.7.3 變數的存儲類別 59
2.8 數組 62
2.8.1 一維數組的定義和使用 63
2.8.2 二維數組的定義和使用 64
2.8.3 字元數組和字元串 65
2.8.4 常用字元串處理函數 66
2.9 指針 69
2.9.1 地址和指針 69
2.9.2 指針的定義和使用 70
2.9.3 數組與指針 71
2.9.4 字元串與指針 72
2.9.5 指向函數的指針 72
2.10 結構體和共用體 73
2.10.1 定義和引用結構體 73
2.10.2 結構體數組 74
2.10.3 指向結構體的指針 74
2.10.4 共用體 75
2.10.5 使用typedef定義類型 77
2.11 鏈表 77
2.11.1 鏈表概述 77
2.11.2 建立動態單向鏈表 78
2.11.3 單向鏈表的輸出 80
2.11.4 對單向鏈表的刪除操作 80
2.11.5 對單向鏈表的插入操作 81
2.11.6 循環鏈表 82
2.11.7 雙向鏈表 82
2.12 位運算符和位運算 83
2.12.1 「按位與」運算符(&) 84
2.12.2 「按位或」運算符(|) 84
2.12.3 「取反」運算符(~) 84
2.12.4 「異或」運算符(^) 84
2.12.5 移位運算符(<<和>>) 85
2.12.6 位域 85
2.13 C語言預處理命令 86
2.13.1 宏定義 86
2.13.2 文件包含 87
2.13.3 條件編譯 88
2.13.4 #error等其他常用預處理命令 89
2.14 本章小結 89
實戰演練 89
第3章 vi與Emacs編輯器 91
3.1 vi的使用 92
3.1.1 啟動與退出vi 92
3.1.2 vi的命令行模式 93
3.1.3 vi的插入模式 96
3.1.4 vi的底行模式 96
3.2 vi使用實例 97
3.3 Emacs的使用 100
3.3.1 啟動與退出Emacs 101
3.3.2 Emacs下的基本操作 102
3.4 Emacs使用實例 107
3.5 本章小結 109
實戰演練 109
第4章 gcc編譯器與gdb調試器 110
4.1 gcc編譯器簡介 111
4.2 如何使用gcc 112
4.2.1 gcc編譯初步 112
4.2.2 警告提示功能 114
4.2.3 優化gcc 116
4.2.4 連接庫 119
4.2.5 同時編譯多個源程序 120
4.2.6 管道 120
4.2.7 調試選項 121
4.3 gdb調試器 122
4.3.1 gdb簡介 122
4.3.2 gdb常用命令 123
4.3.3 gdb調試初步 124
4.4 gdb的使用詳解 126
4.4.1 調用gdb 127
4.4.2 使用斷點 127
4.4.3 查看運行時數據 129
4.4.4 查看源程序 133
4.4.5 改變程序的執行 135
4.5 xxgdb調試器簡介 138
4.6 本章小結 139
實戰演練 139
第5章 make的使用和Makefile的編寫 141
5.1 什麼是make 142
5.1.1 make機制概述 142
5.1.2 make與Makefile的關系 144
5.2 Makefile的書寫規則 147
5.2.1 Makefile的基本語法規則 148
5.2.2 在規則中使用通配符 149
5.2.3 偽目標 149
5.2.4 多目標 151
5.2.5 自動生成依賴性 151
5.3 Makefile的命令 152
5.4 變數 154
5.4.1 變數的基礎 154
5.4.2 賦值變數 154
5.4.3 define關鍵字 156
5.4.4 override指示符 156
5.4.5 目標變數和模式變數 157
5.5 常用函數調用 158
5.5.1 字元串處理函數 158
5.5.2 文件名操作函數 162
5.5.3 循環函數 164
5.5.4 條件判斷函數 165
5.5.5 其他常用函數 166
5.6 隱式規則 168
5.6.1 隱式規則舉例 168
5.6.2 隱式規則中的變數 169
5.6.3 使用模式規則 170
5.7 本章小結 173
實戰演練 173
第2部分 提高篇
第6章 文件I/O操作 174
6.1 Linux文件系統簡介 175
6.1.1 Linux的文件系統結構 175
6.1.2 文件類型 176
6.1.3 文件訪問許可權 179
6.2 基於文件描述符的I/O操作 179
6.2.1 文件描述符 180
6.2.2 標准輸入、標准輸出和標准出錯 180
6.2.3 文件重定向 181
6.2.4 文件的創建、打開與關閉 182
6.2.5 文件的定位 186
6.2.6 文件的讀寫 188
6.3 文件的屬性操作 192
6.3.1 改變文件訪問許可權 192
6.3.2 改變文件所有者 193
6.3.3 重命名 193
6.3.4 修改文件長度 194
6.4 文件的其他操作 195
6.4.1 stat、fstat和lstat函數 195
6.4.2 p和p2函數 196
6.4.3 fcntl函數 197
6.4.4 sync和fsync函數 197
6.5 特殊文件的操作 198
6.5.1 目錄文件的操作 198
6.5.2 鏈接文件的操作 201
6.5.3 管道文件的操作 204
6.5.4 設備文件 204
6.6 本章小結 205
實戰演練 205
第7章 基於流的I/O操作 206
7.1 流與緩存 207
7.1.1 流和FILE對象 207
7.1.2 標准輸入、標准輸出和標准出錯 207
7.1.3 緩存 207
7.1.4 對緩存的操作 210
7.2 流的打開與關閉 212
7.2.1 流的打開 212
7.2.2 流的關閉 214
7.2.3 流關閉前的工作 216
7.3 流的讀寫 216
7.3.1 基於字元的I/O 217
7.3.2 基於行的I/O 220
7.3.3 直接I/O 222
7.3.4 格式化I/O 224
7.4 本章小結 226
實戰演練 227
第8章 進程式控制制 228
8.1 進程的基本概念 229
8.1.1 Linux進程簡介 229
8.1.2 進程與作業 230
8.1.3 進程標識 230
8.2 進程式控制制的相關函數 232
8.2.1 fork和vfork函數 232
8.2.2 exec函數 237
8.2.3 exit和_exit函數 242
8.2.4 wait和waitpid函數 245
8.2.5 進程的一生 251
8.2.6 用戶ID和組ID 251
8.2.7 system函數 253
8.3 多個進程間的關系 255
8.3.1 進程組 255
8.3.2 會話期 256
8.3.3 控制終端 257
8.4 本章小結 259
實戰演練 259
第9章 信號 260
9.1 Linux信號簡介 261
9.1.1 信號的基本概念 261
9.1.2 信號處理機制 265
9.2 信號操作的相關函數 267
9.2.1 信號的處理 267
9.2.2 信號的發送 274
9.2.3 信號的阻塞 282
9.2.4 計時器與信號 284
9.3 本章小結 286
實戰演練 287
第10章 進程間通信 288
10.1 進程間通信簡介 289
10.2 管道 290
10.2.1 管道的概念 290
10.2.2 管道的創建與關閉 291
10.2.3 管道的讀寫 292
10.3 命名管道 297
10.3.1 命名管道的概念 297
10.3.2 命名管道的創建 297
10.3.3 命名管道的讀寫 299
10.4 消息隊列 303
10.4.1 消息隊列的概念 303
10.4.2 消息隊列的創建與打開 305
10.4.3 消息隊列的讀寫 306
10.4.4 獲得或設置消息隊列屬性 308
10.5 共享內存 312
10.5.1 共享內存的概念 312
10.5.2 共享內存的相關操作 313
10.6 信號量 318
10.6.1 信號量的概念 319
10.6.2 信號量集的相關操作 320
10.7 本章小結 325
實戰演練 326
第11章 網路編程 327
11.1 網路編程的基礎知識 328
11.1.1 計算機網路體系結構 328
11.1.2 傳輸控制協議TCP 333
11.1.3 用戶數據報協議UDP 335
11.1.4 客戶機/伺服器模式 336
11.2 套介面編程基礎 336
11.2.1 什麼是套介面 337
11.2.2 埠號的概念 338
11.2.3 套介面的數據結構 338
11.2.4 基本函數 340
11.3 TCP套介面編程 343
11.3.1 TCP套介面通信工作流程 343
11.3.2 TCP套介面Client/Server程序實例 356
11.4 UDP套介面編程 360
11.4.1 UDP套介面通信工作流程 360
11.4.2 UDP套介面Client/Server程序實例 362
11.5 原始套介面編程 365
11.5.1 原始套介面的創建 365
11.5.2 原始套介面程序實例 365
11.6 本章小結 376
實戰演練 376
第12章 Linux圖形界面編程 377
12.1 Linux下的圖形界面編程簡介 378
12.1.1 Qt簡介 378
12.1.2 GTK+簡介 378
12.2 界面基本元件 381
12.2.1 一個簡單的例子 381
12.2.2 窗口 383
12.2.3 標簽 385
12.2.4 按鈕 386
12.2.5 文本框 387
12.3 界面布局元件 389
12.3.1 表格 390
12.3.2 框 393
12.3.3 窗格 395
12.4 其他常用元件 398
12.4.1 進度條、微調按鈕、組合框 398
12.4.2 單選按鈕、復選按鈕 402
12.4.3 下拉菜單 404
12.5 信號與回調函數 406
12.6 本章小結 409
實戰演練 409
第3部分 實戰篇
第13章 設計Linux下的計算器 411
13.1 軟體功能分析 412
13.2 程序模塊的劃分 413
13.3 軟體的具體實現 415
13.3.1 頭文件 415
13.3.2 十六進制界面顯示函數 416
13.3.3 十進制界面顯示函數 417
13.3.4 八進制界面顯示函數 418
13.3.5 二進制界面顯示函數 419
13.3.6 進制間轉換函數 420
13.3.7 信號處理模塊 423
13.3.8 主函數 432
13.4 軟體使用效果演示 438
13.5 本章小結 439
第14章 Linux平台下聊天軟體的設計 440
14.1 軟體功能概述 441
14.1.1 伺服器端功能需求 441
14.1.2 客戶端功能需求 442
14.1.3 錯誤處理需求 442
14.2 Glade集成開發工具簡介 443
14.3 軟體功能模塊劃分 444
14.3.1 伺服器功能模塊劃分 444
14.3.2 客戶端功能模塊劃分 445
14.3.3 消息標識的定義 445
14.3.4 消息結構體的設計 446
14.4 伺服器程序的具體實現 447
14.4.1 伺服器消息處理流程 447
14.4.2 伺服器主要函數和變數 448
14.4.3 伺服器消息處理模塊的設計與實現 449
14.4.4 伺服器數據存儲的方法 450
14.4.5 用戶注冊流程 450
14.5 客戶端程序的具體實現 451
14.5.1 客戶端操作流程 451
14.5.2 客戶端發送和接收消息流程 451
14.5.3 客戶端主要函數和變數 452
14.5.4 客戶端功能模塊的設計與實現 453
14.6 聊天軟體使用效果演示 455
14.7 本章小結 459
第15章 Linux遠程管理工具的設計 460
15.1 軟體功能概述 461
15.1.1 Webmin簡介 461
15.1.2 軟體總體設計 461
15.2 伺服器端程序設計 463
15.2.1 伺服器端工作流程 463
15.2.2 系統用戶管理操作 464
15.2.3 系統用戶組的管理操作 466
15.2.4 系統服務啟動管理 468
15.2.5 DNS管理操作 469
15.2.6 Apache服務管理操作 471
15.2.7 FTP服務管理操作 474
15.3 客戶端程序 475
15.3.1 連接界面 475
15.3.2 主界面 477
15.4 本章小結 479
第16章 Linux下簡易防火牆軟體的設計 480
16.1 Netfilter基礎 481
16.1.1 什麼是Netfilter 481
16.1.2 Netfilter的HOOK機制 482
16.1.3 HOOK的調用 485
16.1.4 HOOK的實現 486
16.1.5 IPTables簡介 488
16.1.6 Netfilter可以實現的控制功能 489
16.2 軟體設計概述 491
16.2.1 軟體整體框架 491
16.2.2 管理端的設計 492
16.2.3 控制端的設計 493
16.3 用Netfilter設計控制端功能模塊 495
16.3.1 ICMP管理控制模塊 495
16.3.2 FTP管理控制模塊 497
16.3.3 HTTP管理控制模塊 499
16.3.4 模塊的編譯、載入與卸載 499
16.4 軟體功能測試 501
16.5 本章小結 503
第17章 基於Linux的嵌入式家庭網關遠程交互操作平台的設計 504
17.1 嵌入式技術簡介 505
17.1.1 嵌入式系統的概念 505
17.1.2 嵌入式操作系統 506
17.1.3 嵌入式處理器 507
17.2 家庭網關的概念及其網路體系結構 509
17.2.1 智能家庭網路的概念 509
17.2.2 家庭網關的遠程交互操作技術簡介 510
17.2.3 嵌入式家庭網關的網路體系結構 510
17.3 嵌入式家庭網關的開發平台 511
17.3.1 S3C2410微處理器簡介 511
17.3.2 交叉編譯環境的建立 513
17.4 遠程交互平台的設計 515
17.4.1 應用軟體的開發模式 515
17.4.2 嵌入式Web伺服器 516
17.4.3 通用網關介面CGI 519
17.5 Linux下軟體模塊的具體實現 520
17.5.1 登錄驗證模塊 521
17.5.2 串口通信模塊 521
17.5.3 中央空調控制模塊 522
17.5.4 智能水表數據採集模塊 528
17.5.5 試驗結果 528
17.6 本章小結 529
『玖』 LinuxC編程一站式學習的前 言
本書最初是為北京亞嵌教育研究中心的嵌入式Linux系統工程師就業班課程量身定做的教材之一。該課程是為期四個月的全日制職業培訓,要求學員畢業時具備非常Solid的C語言編程能力,能熟練地使用Linux系統,同時對計算機體系結構與指令集、操作系統原理和設備驅動程序都有比較深入的了解。然而學員入學時的水平是非常初級而且參差不齊的:學歷有專科、本科也有研究生;專業有和計算機相關的,也有很不相關的(例如會計專業);以前從事的職業有和技術相關的也有完全不相關的(例如HR);年齡從二十歲出頭到三十五六歲的都有。這么多背景、基礎、思維習慣和理解能力完全不同的人來聽同一堂課,大家都迫切希望學會嵌入式開發技術,投身IT行業,這就是職業教育的特點,也是我編寫本書時需要考慮的主要問題。
學習編程絕不是一件簡單的事,尤其是對於零基礎的初學者來說。大學的計算機專業有四年時間從零基礎開始培養一個人,微積分、線性代數、概率論、離散數學、組合數學、自動機、編譯原理、操作系統、計算機組成原理等一堆基礎課,再加上C/C++、Java、資料庫、網路工程、軟體工程、計算機圖形學等一堆專業課,最後培養出一個能找到工作的學生。很遺憾這最後一條很多學校沒有做好,據我們考查,來亞嵌培訓的很多學生基礎幾乎為零,我不知道為什麼。與之形成鮮明對比的是,只給我們四個月的時間,同樣要求從零基礎開始,最後培養出一個能找到工作的學生,而且還要保證他找到好工作,這就是職業教育的特點。
為什麼我說「只給我們四個月的時間」?我們倒是想教四年呢,但學時的長短我們做不了主,是由市場規律決定的。四年的任務要求四個月做好,要怎麼完成這樣一個幾乎不可能的任務呢?有些職業教育給出的答案是「實用主義」,打出了「有用就學,沒有用就不學」的口號,大肆貶低說大學里教的基礎課都是過時的、無用的,只有他們教的技術才是實用的。這種炒作很不好,我認為大學里教的每一門課都是非常有用的,基礎知識在任何時候都不會過時,倒是那些時髦的「實用技術」有可能很快就會過時了。
四年的任務怎麼才能用四個月做好?我們給出的答案是「優化」。現在大學里安排的課程體系最大的缺點就是根本不考慮優化。每個過來人都會有這樣的感覺:大一大二學了好多數學課,卻不知道都是干什麼用的,不明白為什麼要學。連它有什麼用都不知道怎麼能有興趣學好呢?到大三大四學專業課時,用到以前的知識了,才發現以前學的數學是多麼有用,然而早就忘得一干二凈了,考完試都還給老師了。回頭重新學,才發現很多東西以前根本沒學明白,現在真的學明白了,那麼前兩年的時間豈不是都浪費了?大學里的課程體系還有一個缺點就是不靈活,每門課必須佔用一個學期,必須由一個老師教,不同課程的老師之間沒有任何溝通和銜接,其實這些課程之間是相互依賴的,把它們強行拆開是不符合人的認知規律的。比如我剛上大學的時候,大一上半學期就被逼著學習C語言,其實C語言是一門很難的編程語言,不懂編譯原理、操作系統和計算機體系結構根本不可能學明白,那半個學期自然就浪費掉了。當時幾乎所有學校的計算機相關專業都是這樣,大一剛來就學C語言,有的學校更瘋狂,上來就學C++,導致大多數學生都以為自己會C語言,但其實都是半吊子水平,到真正寫代碼的時候經常為一個Bug搞得焦頭爛額,卻沒有機會再系統地學一遍C語言。因為在學校看來,C語言早在大一就給你「上完了」,就像一頓飯已經吃完了,不管你吃飽沒吃飽,不會再讓你重吃一遍了。顯而易見,如果要認真地對這些課程進行優化,的確是有很多水分可以擠的。
本書有什麼特點
本書不是孤立地講C語言,而是和編譯原理、操作系統、計算機體系結構結合起來講。或者說,本書的內容只是以C語言為載體,真正講的是計算機和程序的原理。
強調基本概念和基本原理,在編排順序上重視概念之間的依賴關系,每次引入一個新的概念,只依賴於前面章節已經講過的概念,而絕不會依賴於後面章節要講的概念。有些地方為了敘述得完整,也會引用後面要講的內容,比如說「有關××我們到第×章再仔細講解」,凡是這種引用都不是必要的依賴,可以當它不存在,只管繼續往下學習就行了。
盡量做到每個知識點直到要用的時候才引入。過早引入一個知識點,講完了又不用它,讀者很快就會遺忘,這是不符合認知規律的。
本書面向什麼樣的讀者
這是一本從零基礎開始學習編程的書,不要求讀者有任何編程經驗,但讀者至少需要具備以下素質:
熟悉Linux系統的基本操作。如果不具備這一點,請先參考其他教材學習相關知識,熟練之後再學習本書,《鳥哥的Linux私房菜》據說是Linux系統管理和應用方面比較好的一本書。但學習本書並不需要會很多系統管理技術,只要會用基本命令、會自己安裝系統和軟體包就足夠了。
具有高中畢業的數學水平。本書會用到高中的數學知識。事實上,如果不具有高中畢業的數學水平,也不必考慮做程序員了。但並不是說只要具有高中畢業的數學水平就足夠做程序員了,只能說看這本書應該沒有問題,數學是程序員最重要的修養,計算機科學其實就是數學的一個分支,如果你的數學功底很差,日後還需要惡補一下。
具有高中畢業的英文水平。理由同上。
對計算機的原理和本質深感興趣,不是為就業而學習,不是為拿高薪而學習,而是真的感興趣,想把一切來龍去脈搞得清清楚楚而學習。
勤於思考。本書盡最大努力理清概念之間的依賴關系,力求一站式學習,讀者不需要為了找一個概念的定義去翻閱其他書籍,也不需要為了搞清楚一個概念在本書中亂翻一通,只需要從前到後按順序學習即可。但一站式學習並不等於傻瓜式學習,有些章節有一定的難度,需要讀者積極思考才能領會。本書可以替你節省時間,但不能替你思考,不要指望像看小說一樣走馬觀花看一遍就能學會。
為什麼要學這本書而不是K&R
《The C Programming Language》(後文簡稱[K&R])是公認的世界上最經典的C語言教程之一,這點毫無疑問。在C標准出台之前,K&R第一版就是事實上的C標准。C89標准出台之後,K&R跟著推出了第二版,可惜此後就沒有更新過了,所以不能反映C89之後C語言的發展以及最新的C99標准。本書在這方面做了很多補充。本書與其說是講C語言,不如說是以C語言為載體講計算機和操作系統的原理,而K&R只是為了講C語言而講C語言,側重點不同,內容編排也很不相同。K&R寫得非常好,代碼和語言都非常簡潔,但很可惜,只有會C語言的人才懂得欣賞它,K&R是非常不適合入門學習的,尤其不適合零基礎的學生學習。
本書「是什麼」和「不是什麼」
本書包括兩大部分:
C語言入門。介紹基本的C語法,幫助沒有任何編程經驗的讀者理解什麼是程序以及怎麼寫程序,培養程序員的思維習慣,找到編程的感覺。前半部分改編自《How To Think Like A Computer Scientist: Learning with C++》(後文簡稱[ThinkCpp])。
C語言本質。結合計算機和操作系統的原理講解C程序是怎麼編譯、鏈接、運行的,同時全面介紹C的語法。位運算的章節改編自林小竹老師的講義;鏈表和二叉樹的章節改編自朱仲濤老師的講義;匯編語言的章節改編自《Programming from the Ground Up: An Introction to Programming using Linux Assembly Language》(後文簡稱[GroundUp]),在該書的最後一章中提到,學習編程有兩種Approach,一種是「Bottom Up」,一種是「Top Down」,它們各有優缺點,而我們需要將兩者結合起來。所以我編寫本書的思路是:第一部分Top Down;第二部分Bottom Up;第三部分可以算填補了中間的空隙,三部分全都圍繞C語言展開。
這本書定位在入門級,雖然內容很多,但不是一本網路全書,除了C語言的基礎知識要講透之外其他內容都不深入,書中列出了很多參考資料,是讀者進一步學習的起點。[K&R]的第1章是一個Whirlwind Tour,把全書的內容簡單概括了一遍,然後再逐個深入講解。本書也可以看作是計算機專業課程體系的一個Whirlwind Tour,學習完本書之後讀者有了一個全局觀,再去學習那些參考資料就應該很容易上手了。
為什麼要在Linux平台上學C語言?
用Windows學C語言不好嗎?
用Windows還真的是學不好C語言。C語言是一種面向底層的編程語言,要寫好C程序,必須對操作系統的工作原理非常清楚,因為操作系統也是用C語言編寫的,我們用C語言編寫應用程序可以直接使用操作系統提供的介面。既然你選擇了本書,你一定了解:Linux是一種開源的操作系統,你有任何疑問都可以從源代碼和文檔中找到答案,即使你看不懂源代碼,也找不到文檔,也很容易找個高手教你,各種郵件列表、新聞組和論壇上從來都不缺樂於助人的高手;而Windows是一種封閉的操作系統,除了微軟的員工別人都看不到它的源代碼,只能通過文檔去猜測它的工作原理。更糟糕的是,微軟向來喜歡藏著掖著,好用的功能留著自己用,而不會寫到文檔里公開。本書的第一部分在Linux或Windows平台上學習都可以,但第二部分和第三部分介紹了很多Linux操作系統的原理以幫助讀者更深入地理解C語言,所以後兩部分只能在Linux平台上學習。
Windows平台上的開發工具往往和各種集成開發環境(Integrated Development Environment,IDE)綁在一起,例如Visual Studio、Eclipse等。使用IDE確實很便捷,但IDE對於初學者絕對不是好東西。微軟喜歡宣揚傻瓜式編程的理念,告訴你用滑鼠拖幾個控制項,然後單擊一個按鈕就可以編譯出程序來,但是真正有用的程序有哪個是這么拖出來的?很多從Windows平台入門學編程的人,編了好幾年程序,還是只知道編完程序單擊一個按鈕就完事了,把幾個源文件拖到一個項目里就可以編譯到一起了,如果有更復雜的需求他們就傻眼了,因為他們腦子里只有按鈕、菜單的概念,根本沒有編譯器、鏈接器、Makefile的概念,甚至連命令行都沒用過,然而這些都是初學編程就應該建立起來的基本概念。另一方面,編譯器、鏈接器和C語言的語法有著密切的關系,不了解編譯器、鏈接器的工作原理,也不可能真正掌握C語言的語法。所以,IDE並沒有幫助你學習,而是阻礙了你的學習,本來要學好C編程只要把語法和編譯命令學會就行了,現在有了IDE,除了學會語法和編譯命令,你還得弄清楚編譯命令和IDE是怎麼集成的,這才算學明白了,本來就很復雜的學習任務被IDE搞得更加復雜了。Linux用戶的使用習慣從來都是以敲命令為主,以滑鼠操作為輔,從學編程的第一天起就要敲命令編譯程序,等到你把這些基本概念都搞清楚了,你覺得哪個IDE好用你再去用,不過到那時候你可能會更喜歡vi或emacs而不是IDE了。
體例說明
像The quick brown fox jumps over the lazy dog這樣的字體在本書中是代碼字體。這種字體的名稱是Dejavu Sans Mono,為什麼我要提倡用這種字體呢?第一,它是等寬字體,因此適合做代碼字體。第二,它的1和l、0和O區分得非常清楚(我在教學中發現初學者很容易把這些字元抄錯),因此它比Courier New更適合做代碼字體。第三, 它是我的Linux圖形終端的默認字體,採用這種字體排版可以使得看書和看屏幕的感覺很一致,希望讀者在看這本書時也會有這種Dejavu(似曾相識)的感覺。
像下面這樣有邊線的是代碼:
#! /bin/sh
VAR=1
VAR=$(($VAR+1))
echo $VAR
沒有邊線的是終端顯示,包括輸入的命令和程序運行結果,例如:
$ VAR=1
$ VAR=$(($VAR+1))
$ echo $VAR
2
本書中統一用$表示Shell提示符。
加粗的字句表示強調。
在定義一個名詞時會給出它的英文名稱,例如集成開發環境(Integrated Development Environment,IDE),通過書後的索引可以找到這些定義在書中首次出現的位置。
致謝
本書的寫作得到了北京亞嵌教育研究中心的全力支持,尤其感謝李明老師和何家勝老師。沒有公司的支持,我不可能有時間有條件寫這本書,也不可能有機會將這本書公開在網上。
然後要感謝亞嵌教育的歷屆學員和各位老師,在教學和討論的過程中我經常會得到有益的啟發,這些都促使本書更加完善。在本書的寫作過程中,很多讀者為本書提出了很有價值的建議,很多建議是熱心網友通過在線評論提出的,有些網友我只知道ID或E-mail。在此向他們表示感謝。
感謝幫助過我的老師們:李明、何家勝、邸海霞、郎鐵山、朱仲濤、廖文江、韓超、秦蔚、吳岳、張 、邢文鵬、何曉龍、林小竹、衛劍釩、郭同彬、王波、王磊。
感謝熱心網友:ddd、wuyulei、commapopo、田偉、田雨、daidai、鄧楠、杜朴風、Zoom.Quiet、陳莉君老師、楊景、章鈺、chen、Jiawei Zhang、waterloo、張現超、曾宇、董俊波、RobinXiang、劉艷明、been2100、cleverd、juicerococo、徐斌、cyy、Linux_Xfce、馮海雲、侯延祥、churchmice、codycody23、syfeagle、王公僕、劉敏、Laciq、yuchen、陸楊、陳楊希、love_wc3、姚磊、芝麻、wadenx、沈震、sunbingfly、mick、baaluck、曹帥軍、zhoudy、朱夜光、刺蝟、leezhenfeng、王兆宏、徐凱、碼匠、況海斌、尹志偉、王星。
還要感謝電子工業出版社博文視點資訊有限公司的周筠老師和李冰老師的大力支持,感謝江立編輯嚴謹細致的工作。
在寫作過程中我遇到過很多困難:工作繁忙、對未來迷茫、生活壓力大、缺乏安全感、個人瑣事等。然而有這么多熱心的同學、老師、朋友、網友在等著閱讀我的書在線更新的內容,給我提建議,希望我把書改得更完善,這是我堅持寫下去的最大動力。謝謝你們!
由於作者水平十分有限,沒寫過C編譯器和C標准庫,所以疏漏之處在所難免,如有錯誤歡迎廣大讀者朋友批評指正。寫書是一件嚴肅的事,書中的錯誤所有人都看得見,白紙黑字賴不掉的。我教過的很多學生都在大學里學過C語言,甚至考過二級,但程序寫得一塌糊塗,連最基本的概念都搞錯了,以前學過的C語言教材中的錯誤在他們腦子里根深蒂固,即使我糾正多次,他們仍然只記得以前學過的錯誤概念。這種有基礎的學生還不如沒有任何基礎的學生教起來容易。我非常害怕我教給別人的知識也是錯的,所以我仔細研究了C99之後才敢動筆寫書。這本書涵蓋的話題比較廣泛,我竭盡全力也不足以保證書中的內容全部正確,還要依靠社區的力量一起來完善這本書,這樣才能真正對讀者負責,所以我選擇將這本書開源。。
希望本書能成為你求學道路上的第一個夥伴。
宋勁杉
2009年7月22日
『拾』 LinuxC編程一站式學習的目 錄
上篇 C語言入門
第1章 程序的基本概念 2
1.1 程序和編程語言 2
1.2 自然語言和形式語言 6
1.3 程序的調試 7
1.4 第一個程序 9
第2章 常量、變數和表達式 12
2.1 繼續Hello World 12
2.2 常量 15
2.3 變數 16
2.4 賦值 18
2.5 表達式 19
2.6 字元類型與字元編碼 23
第3章 簡單函數 24
3.1 數學函數 24
3.2 自定義函數 26
3.3 形參和實參 31
3.4 全局變數、局部變數和作用域 35
第4章 分支語句 41
4.1 if語句 41
4.2 if/else語句 43
4.3 布爾代數 45
4.4 switch語句 49
第5章 深入理解函數 51
5.1 return語句 51
5.2 增量式開發 54
5.3 遞歸 58
第6章 循環語句 64
6.1 while語句 64
6.2 do/while語句 66
6.3 for語句 67
6.4 break和continue語句 69
6.5 嵌套循環 70
6.6 goto語句和標號 71
第7章 結構體 74
7.1 復合類型與結構體 74
7.2 數據抽象 78
7.3 數據類型標志 82
7.4 嵌套結構體 84
第8章 數組 85
8.1 數組的基本概念 85
8.2 數組應用實例:統計隨機數 88
8.3 數組應用實例:直方圖 91
8.4 字元串 94
8.5 多維數組 95
第9章 編碼風格 100
9.1 縮進和空白 100
9.2 注釋 104
9.3 標識符命名 107
9.4 函數 108
9.5 indent工具 108
第10章 gdb 110
10.1 單步執行和跟蹤函數調用 110
10.2 斷點 117
10.3 觀察點 121
10.4 段錯誤 125
第11章 排序與查找 128
11.1 演算法的概念 128
11.2 插入排序 129
11.3 演算法的時間復雜度分析 131
11.4 歸並排序 133
11.5 線性查找 138
11.6 折半查找 139
第12章 棧與隊列 144
12.1 數據結構的概念 144
12.2 堆棧 144
12.3 深度優先搜索 146
12.4 隊列與廣度優先搜索 152
12.5 環形隊列 156
本階段總結 159
下篇 C語言本質
第13章 計算機中數的表示 162
13.1 為什麼計算機用二進制計數 162
13.2 不同進制之間的換算 164
13.3 整數的加減運算 165
13.3.1 Sign and Magnitude表示法 165
13.3.2 1's Complement表示法 166
13.3.3 2's Complement表示法 167
13.3.4 有符號數和無符號數 168
13.4 浮點數 169
第14章 數據類型詳解 172
14.1 整型 172
14.2 浮點型 176
14.3 類型轉換 177
14.3.1 Integer Promotion 177
14.3.2 Usual Arithmetic Conversion 178
14.3.3 由賦值產生的類型轉換 179
14.3.4 強制類型轉換 179
14.3.5 編譯器如何處理類型轉換 179
第15章 運算符詳解 182
15.1 位運算 182
15.1.1 按位與、或、異或、取反運算 182
15.1.2 移位運算 183
15.1.3 掩碼 184
15.1.4 異或運算的一些特性 185
15.2 其他運算符 186
15.2.1 復合賦值運算符 186
15.2.2 條件運算符 186
15.2.3 逗號運算符 187
15.2.4 sizeof運算符與typedef類型聲明 187
15.3 Side Effect與Sequence Point 189
15.4 運算符總結 191
第16章 計算機體系結構基礎 193
16.1 內存與地址 193
16.2 CPU 193
16.3 設備 196
16.4 MMU 198
16.5 Memory Hierarchy 201
第17章 x86匯編程序基礎 205
17.1 最簡單的匯編程序 205
17.2 x86的寄存器 208
17.3 第二個匯編程序 209
17.4 定址方式 211
17.5 ELF文件 212
17.5.1 目標文件 213
17.5.2 可執行文件 218
第18章 匯編與C之間的關系 224
18.1 函數調用 224
18.2 main函數、啟動常式和退出狀態 230
18.3 變數的存儲布局 237
18.4 結構體和聯合體 244
18.5 C內聯匯編 248
18.6 volatile限定符 250
第19章 鏈接詳解 255
19.1 多目標文件的鏈接 255
19.2 定義和聲明 260
19.2.1 extern和static關鍵字 260
19.2.2 頭文件 264
19.2.3 定義和聲明的詳細規則 268
19.3 靜態庫 271
19.4 共享庫 274
19.4.1 編譯、鏈接、運行 274
19.4.2 函數的動態鏈接過程 281
19.4.3 共享庫的命名慣例 282
19.5 虛擬內存管理 284
第20章 預處理 290
20.1 預處理的步驟 290
20.2 宏定義 291
20.2.1 函數式宏定義 291
20.2.2 內聯函數 294
20.2.3 #、##運算符和可變參數 296
20.2.4 #undef預處理指示 298
20.2.5 宏展開的步驟 299
20.3 條件預處理指示 300
20.4 其他預處理特性 303
第21章 Makefile基礎 306
21.1 基本規則 306
21.2 隱含規則和模式規則 313
21.3 變數 317
21.4 自動處理頭文件的依賴關系 321
21.5 常用的make命令行選項 324
第22章 指針 327
22.1 指針的基本概念 327
22.2 指針類型的參數和返回值 331
22.3 指針與數組 332
22.4 指針與const限定符 335
22.5 指針與結構體 337
22.6 指向指針的指針與指針數組 337
22.7 指向數組的指針與多維數組 340
22.8 函數類型和函數指針類型 341
22.9 不完全類型和復雜聲明 344
第23章 函數介面 349
23.1 本章的預備知識 349
23.1.1 strcpy與strncpy 349
23.1.2 malloc與free 354
23.2 傳入參數與傳出參數 358
23.3 兩層指針的參數 360
23.4 返回值是指針的情況 362
23.5 回調函數 365
23.6 可變參數 368
第24章 C標准庫 372
24.1 字元串操作函數 373
24.1.1 給字元串賦初值 373
24.1.2 取字元串的長度 374
24.1.3 拷貝字元串 375
24.1.4 連接字元串 377
24.1.5 比較字元串 378
24.1.6 搜索字元串 379
24.1.7 分割字元串 380
24.2 標准I/O庫函數 383
24.2.1 文件的基本概念 383
24.2.2 fopen/fclose 384
24.2.3 stdin/stdout/stderr 387
24.2.4 errno與perror/strerror函數 388
24.2.5 以位元組為單位的I/O函數 391
24.2.6 操作讀寫位置的函數 393
24.2.7 以字元串為單位的I/O函數 395
24.2.8 以記錄為單位的I/O函數 397
24.2.9 格式化I/O函數 399
24.2.10 C標准庫的I/O緩沖區 406
24.2.11 本節綜合練習 410
24.3 數值字元串轉換函數 412
24.4 分配內存的函數 414
第25章 鏈表、二叉樹和哈希表 415
25.1 鏈表 415
25.1.1 單鏈表 415
25.1.2 雙向鏈表 421
25.1.3 靜態鏈表 425
25.1.4 本節綜合練習 426
25.2 二叉樹 426
25.2.1 二叉樹的基本概念 426
25.2.2 排序二叉樹 432
25.3 哈希表 437
本階段總結 439
附錄A 字元編碼 442
索引 449
參考書目 474