Linux C語言編程基本原理與實踐(2018-06-16 19:12:15)linux
高效的學習帶着目的性: 是什麼 -> 幹什麼 -> 怎麼用nginx
nginx:c apache:c++
linux嵌入式c++
Ubuntu:apache
PS: 儘可能在Linux環境下開發C語言程序編程
sudo apt install 【軟件名】
sudo apt update
Ctrl+Alt+T
:打開終端cd ~
:進入當前用戶的根目錄pwd
:查看當前所在路徑ls
:當前章目錄包含哪些文件ls -l
:顯示當前文件的類型,權限,建立時間,名字ls -al
或ll
:顯示隱藏文件若是前面是d
就是文件夾,-
就是普通類型的文件
touch **
:建立字符型文件rm **
:刪除mkdir **
:建立目錄(文件夾)vi **
:打開(進入)文件vi 一個不存在的文件,進入後沒法輸入內容,因當前在命令模式下;按字符i,可進入INSERT插入模式,就可輸入內容,按Esc返回命令模式;
在命令模式下:ubuntu
:w
:保存該文件:q
:退出i
:當前光標前面插入字符Shift+i
:跳到本行行首插入字符a
:當前光標後面插入字符Shift+a
:跳到本行末尾插入字符o
:在當前下一行插入字符Shift+o
:在當前上一行插入字符x
:刪除當前光標所處字符d+d
:刪除整行Linux下最好用的文本編輯器: emacs, vim小程序
cc -v(gcc -v)
: 查看編譯器版本clear
:清潔屏幕linux下通常不用void main
,最新c語言標準,int main
vim
#include <stdio.h> int main() { printf("hello,world!\n"); return 0; }
cc a.c
默認會爲咱們編譯並生成可執行文件a.out(可讀可寫可執行)數組
./
表示當前路徑下,./a.out
執行當前路徑下的a.out文件r
表示可讀 w
表示可寫 x
表示可執行三組重複的順序爲"建立者","用戶組","任意其餘用戶"併發
c語言是一個結構化的程序語言,是支持多函數的。程序可由若干個函數組成。
vim hello.c
最原始版本的實現(hello.c):
#include <stdio.h> int max(int a, int b) { if(a>b){ return a; }else{ return b; } } int main() { int a1 = 33; int a2 = 21; int maxNum = max(a1,a2); printf("the max value is %d\n",maxNum); return 0; }
附加知識: vim分屏顯示
:sp 文件名
//建立(打開)新文件ctrl+w+上箭頭
ctrl+w+下箭頭
:set nu
//這兩個不用點冒號
若是就是上圖代碼直接編譯會報錯,這是一個未聲明的函數。有兩種分離方案:
int max(int a,int b);
,在hello.c中聲明該方法,而後編譯的時候須要加上max.c#include "max.c"
而後編譯的時候就不須要加上max.c一塊兒編譯版本1:
0-hello.c:
#include <stdio.h> int max(int a,int b); int main() { int a1 = 33; int a2 = 21; int maxNum = max(a1,a2); printf("the max value is %d\n",maxNum); return 0; }
0-max.c:
int max (int a, int b) { if(a>b){ return a; }else{ return b; } }
編譯命令:
gcc 0-hello.c 0-max.c -o 0-hello.out
若是不加上0-max.c一塊兒編譯,會出現錯誤
gcc 0-hello.c -o 0-hello.out /tmp/cc8GuaAH.o:在函數‘main’中: 0-hello.c:(.text+0x21):對‘max’未定義的引用 collect2: error: ld returned 1 exit status
版本2
1-hello.c:
#include <stdio.h> #include "0-max.c" int main() { int a1 = 33; int a2 = 21; int maxNum = max(a1,a2); printf("the max value is %d\n",maxNum); return 0; }
0-max.c與原來的一致
編譯命令:
gcc 1-hello.c -o 1-hello.out
若是此時多加了0-max.c一塊兒編譯
gcc 1-hello.c 0-max.c -o 1-hello.out /tmp/ccjcCmVa.o:在函數‘max’中: 0-max.c:(.text+0x0): `max'被屢次定義 /tmp/cclIxMtD.o:1-hello.c:(.text+0x0):第一次在此定義 collect2: error: ld returned 1 exit status
終端下:
gcc 文件名.c -o 命名.out
#include <>
表示在預裝的庫裏查找#include "max.c"
表示在當前目錄內查找文件
include "max.c"
至關於把整個函數複製進來了。效果等同於寫進來
wqa
是將多個文件一塊兒保存把函數的聲明和定義分離開來
代碼沒有main函數不能執行,main是入口。
mtianyan@ubuntu:~/Desktop/zjuPlan/CSF878/CCode/linux_c/2-lesson/part1$ ls 0-max.c 1-hello.c
加快編譯速度
gcc -c 0-max.c -o 0-max.o
將max.c變成max.o以後,咱們須要把hello.c中的include註釋掉並添上方法聲明
#include <stdio.h> //#include "0-max.c" int max(int a,int b);
可讀可寫不可執行,max.o至關於計算器對於源代碼進行了翻譯,變成計算機可識別的機器碼
gcc 0-max.o 1-hello.c -o 1-hello.out
新建一個min.c
int min (int a, int b) { if(a<b){ return a; }else{ return b; } }
hello.c中進行minNum的調用
#include <stdio.h> //#include "0-max.c" int max(int a,int b); int min(int a,int b); int main() { int a1 = 33; int a2 = 21; int maxNum = max(a1,a2); int minNum = min(a1,a2); printf("the max value is %d\n",maxNum); printf("the min value is %d\n",minNum); return 0; }
編譯命令:
gcc -c min.c -o min.o
gcc 0-max.o min.o 1-hello.c -o 2-hello.out
加快編譯速度。不會再修改的函數,公共框架和公共類編譯生成靜態庫。
gcc的編譯流程分爲4步:
預處理(Pre-Processing) -> 編譯(Compling) -> 彙編(Assembling) -> 鏈接(Linking)
預處理: 處理#include
#define
#ifdef
等宏命令
編譯: 把預處理完的文件編譯爲彙編程序.s
彙編: 把彙編程序.s
編譯爲.o
二進制文件
gcc -c min.c -o min.o
//把文件min.c預編譯成文件min.ocp 文件名a 文件b
//把文件a拷貝成新的文件byy
複製一行 使用 行號n+yy
複製n行p
對複製的行進行粘貼cat
命令能夠查看那麼問題又來了,咱們如今的max函數和min函數是本身編寫的,即便不加聲明,咱們也知道須要哪些參數,參數是什麼類型,返回值是什麼類型。若是這個函數不是咱們編寫的,別人又編寫成了max.o min.o 咱們看不到源代碼,又不知道函數的傳入參數與返回。.h
文件的好處就來了
咱們建立一個文件夾part2
max.h 代碼:
int maxNum(int a, int b);
min.h 代碼:
int minNum(int a ,int b);
hello.c代碼:
#include <stdio.h> #include "max.h" #include "min.h" int main() { int a1 = 33; int a2 = 21; int maxNum = max(a1,a2); int minNum = min(a1,a2); printf("the max value is %d\n",maxNum); printf("the min value is %d\n",minNum); }
gcc max.o min.o hello.c -o hello.out
warning: implicit declaration of function ‘max’; did you mean ‘main’?
出現警告,但不影響正常的編譯運行。
make -v
sudo apt-get install make
make工具的內部也是使用的gcc
咱們本身開發仍是安裝軟件都要使用到make
和make install
這兩個命令
由於當咱們的源文件不少不少的時候
gcc max.c min.c hello.c -o hello.out
命令就會很長很長。
編寫一個Makefile能夠同時編譯多個文件,告訴依賴關係。
hello.out:max.o min.o hello.c gcc max.o min.o hello.c -o hello.out max.o:max.c gcc -c max.c min.o:min.c gcc -c min.c # 添加註釋
make
編寫 Makefile 縮進使用 tab 鍵(八個空格,不然出錯)
Makefile:3: *** 遺漏分隔符 (null)。 中止。
mv MakeFile makefile
從上往下找,從下往上編譯出來。 已經生成出來的文件不會再從新生成。
make: 「hello.out」已經是最新。 (up to date)
main.c:
#include <stdio.h> int main(int argc,char* argv[]) //main函數完整形式 { printf("hello,world\n"); return 101; }
gcc main.c -o main.out && ./main.out
gcc main.c -o main.out && ./ main.out
能夠依次執行兩條命令return 0
用來驗證程序運行是否成功。echo $?
用來查看返回值
./main.out && ls
打印出helloworld的同時,列出當前目錄。由於main.out的return值爲0,斷定爲成功執行。
./main2.out && ls
打印出helloworld,未列出當前目錄。由於main.out的return值爲101(非0),斷定爲不成功執行。
int main(int argc, char* argv[])//main函數完整形式
argc統計輸入參數的個數。只輸入文件名時個數爲1
argv[]
存放每一個參數的內容
如:
argc(argument count),存放着傳入main函數的參數個數是幾個。argv(argument vector),存放具體傳入的參數
main3.c:
#include <stdio.h> int main(int argc,char* argv[]) { printf("argc is %d\n",argc); //int i; for(int i =0;i<argc;i++){ printf("argv[%d]is %s\n",i,argv[i]); } return 101; }
$ ./main3.out -a -l argc is 3 argv[0]is ./main3.out argv[1]is -a argv[2]is -l
linux把全部東西看成文件處理
stdin
鍵盤stdout
顯示器stderr
fprintf fscanf
:將輸入輸出放入...printf("")
是對fprintf(stdout,"")
函數的封裝.scanf("")
是對fscanf(stdin,"")
函數的封裝#include <stdio.h> int main() { // printf("hello world!\n"); fprintf(stdout,"hello world \n"); int a; //scanf("%d",&a); fscanf(stdin,"%d",&a); if(a<0){ fprintf(stderr,"the value must >0\n"); # 返回值不等於0代表函數出錯 return 1; } //printf("input value is: %d\n",a); return 0; }
Linux幾乎能夠用於任何領域,這裏咱們不得不提出linux的通道。管道
起到了很重要的做用,不一樣應用程序之間要配合使用,就須要用到管道。
Demo:main.c
先理解輸入流,輸出流和錯誤流的 重定向機制,對於管道的理解會比較容易些。
#include <stdio.h> int main() { int i,j; printf("input the int value i:\n"); \\printf其實對fprintf的封裝,是從標準輸出流(即stdout)來輸出這個過程 scanf("%d", &i); //默認輸入流是鍵盤 printf("input the int value j:\n"); scanf("%d", &j); printf("i+j=%d\n", i+j); }
cc main.c
, 獲得a.out
,運行a.out,咱們分別輸入3和5輸入到終端../a.out 1>> a.txt
,其中>>
符號(不寫參數就是輸出流),以前默認輸出流是終端,如今咱們則改成輸出到a.txt中,咱們執行命令後,分別輸入3回車後再輸入5。再使用命令cat a.txt,咱們能夠看到咱們已經輸出到文件裏的內容。
咱們標準輸出流是1>>
,輸入流是0>>
./a.out >> a.txt
,咱們再次輸入參數,完成後咱們再次使用cat
來查看a.txt文件裏的內容,發現以前的內容還在,新的輸出內容追加到了後面。ls /etc >> etc.txt
,咱們將ls目錄下的內容輸入到了etc.txt文件中>>
改成單箭頭>
,則文件中先前的內容就會被覆蓋掉。18 9
./a.out < input.txt
,不存在追加模式,因此咱們用單箭頭<
,咱們能夠將要輸入的內容所有在input.txt中準備好,命令執行後,咱們便在終端上能夠看到結果。
#include <stdio.h> int main() { int i,j; printf("input the int value i:\n"); scanf("%d", &i); //默認輸入流是鍵盤 printf("input the int value j:\n"); scanf("%d", &j); if(0!=j){ printf("%d/%d=%d\n",i,j,i/j); }else{ fprintf(stderr,"j != 0\n"); return 1; } return 0; }
注意代碼規範小技巧,0和j的比較,0放前面若是漏了等號會報錯
echo $?
查看返回值
./main.out 1>true.txt 2>false.txt < input.txt
而當值錯誤,產生除零錯誤時。
上圖是當j=0時的運行狀況。
重定向到其餘地方。
管道:
把前面的輸出流做爲後面工具的輸入流,用一個|
表示.
grep
:查看指定文本,搜出包含字符的文本ls /etc/ | grep ab
ls /etc/
的輸出做爲grep
的輸入
ls /etc/ | grep ab
在/etc/文件下查找含有ab字符的文件名
ps -e
:查看系統運行的進程ps -e | grep ssh
查看包含ssh的進程
avg.c :
#include <stdio.h> int main() { int s,n; scanf("%d,%d",&s,&n); float v = s/n; printf("v =%f\n",v); return 0; }
編譯命令:
cc avg.c -o avg.out
input.c:
#include <stdio.h> int main() { int flag =1; int i; int s = 0; int count=0; while(flag){ scanf("%d",&i); if(0==i) break; #等於零跳出 count++; s+=i; } printf("%d,%d\n",s,count); return 0; }
cc input.c -o input.out
注意上面的代碼中應該將s初始化爲0.不然會產生問題。
使用時:
./input.out | ./avg.out
實現將input的數據之間求平均值。