Linux環境下C語言編程
1 .序
筆者又來更新博客了,每次立下Flag說要一週一次更新博客,但總是拖,最後奈何4月份只更新了一次,說明四月份又渾渾噩噩度過了一個月,不過值得可喜的是筆者在4月份找了一份實習工作,在裏面做嵌入式軟件開發,目前負責QT的開發。
筆者目前的Linux環境是ubantu16.04,gnome桌面環境,使用Linux系統時間久了不免產生審美疲勞,所以筆者就換了個MacOS的主題環境,果然界面煥然一新,又可以開開心心的編程了。附上桌面環境圖。
2.Vim的相關操作
在Linux下各種文件的配置信息,難免會用到編輯器,而Vim就是一款強大的編輯器,當然C語言的編程也需要Vim編輯器輸入,有很多人覺得VIm的操作繁瑣,不容易記住很多指令,不過筆者覺得只要記住常用幾個指令就足以應付這些了。
(1)命令行模式,
控制光標的移動、進行復制、粘貼、刪除撤銷等操作。通過 Ctrl+[ 可以返回到命令行模式。
光標的移動通過上下左右 鍵來進行移動,當然也可以通過k j h l 來移動,使用字母鍵進行字符移動時,可以在其前面加數字,表示在對應的放下上進行移動。
dd:表示刪除一行,類似的ndd刪除本行及下面的n行
yy:複製一行,nyy複製當前行和下面的n行
p:粘貼
u:撤銷
(2)插入模式,
只有在插入模式下,纔可以進行文字的輸入,按字母 i 可以進入插入模式,就能正常的進行操作了,就類似於word的操作一樣了,然後上文也說過了,通過 Ctrl + [ 可以退出插入模式,進入命令行模式。
(3)底行模式
在退出插入模式後,按:可以進入底行模式,在底行模式下,可以進行文件的保存和退出。
:q :不保存直接退出vim編輯器。
:q! :不保存強制退出vim編輯器。
:wq :保存退出vim編輯器。
3.C語言編程
C語言的編程,必然需要編譯鏈接成可執行文件。當然一般的C語言運行過程需要經過:預處理→編譯→彙編→鏈接→可執行文件。在Linux環境下,可以使用gcc來編譯鏈接生成可執行文件。
- 預處理
下文中可以看到,經過預處理之後,編譯器已經把需要包含的頭文件編譯到.i文件當中。-E
#include<stdio.h>
int main()
{
int i,j;
for(i=0,j=5;i<j;i++)
printf("%d Hello World!",i);
return 0;
}
gcc test.c -o test.i -E
extern int pclose (FILE *__stream);
extern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__));
#912 "/usr/include/stdio.h" 3 4
extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
#942 "/usr/include/stdio.h" 3 4
#2 "test.c" 2
#2 "test.c"
int main()
{
int i,j;
for(i=0,j=5;i<j;i++)
printf("%d Hello World!",i);
return 0;
}
- 編譯
編譯階段,就是編譯器把源代碼編譯成.s彙編程序,可以試着點開.s文件查看一下。-s
gcc test.i -o test.s -S
.LC0:
.string "%d Hello World!"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $0, -8(%rbp)
movl $5, -4(%rbp)
jmp .L2
- 彙編
將彙編語言源程序彙編成目標文件。-c
gcc test.s -o test.o -c
- 鏈接
將目標程序鏈接相應的庫函數生成可執行文件,之後執行,可以看到成功正確執行函數功能。
gcc test.o -o test
./test
0 Hello World!1 Hello World!2 Hello World!3 Hello World!4 Hello World!
*****不過正常直接一步就可以生成可執行文件來運行。
gcc test.c -o test
./test
0 Hello World!1 Hello World!2 Hello World!3 Hello World!4 Hello World!
makefile文件的使用
當然這只是單文件的C語言運行,如果涉及到多個文件的編譯的時候,就需要注意到編譯順序了,被調用的文件需要先編譯產生.o文件,然後再配合調用函數的.o文件一起生成可執行文件。接下來舉個栗子。
主函數test.c文件,主要包含子文件:print.c、hello.c、和Add.c文件,都在主函數中調用。
#include<stdio.h>
#include"print.h"
#include"hello.h"
#include"Add.h"
int main()
{
int a,b;
hello();
printf("請輸入兩個計算的數\n");
scanf("%d\n %d",&a,&b);
int c=Add(a,b);
print(c);
printf("\n結束!");
printf("終於學會make編譯了!\n");
return 0;
}
hello.c和hello.h文件:也主要是打印,負責剛開始的內容輸出。
#include"hello.h"
void hello()
{
printf("hello,world,今天開始學習make編譯!\n");
}
//接下來是hello.h文件 ,只是負責對使用的函數進行聲明。
#include<stdio.h>
void hello();
print.c和printf.h文件:主要負責打印,當然直接就可以使用printf,我這裏是爲了說明生成可執行文件的編譯順序。
#include<stdio.h>
#include"print.h"
void print(int c)
{
printf("c=%d ",c);
}
//接下來是print.h文件 ,只是負責對使用的函數進行聲明。
#include"stdio.h"
void print(int c);
Add.c和Add.h文件:主要是對輸入的兩個數進行相加,返回輸出結果,也是在主函數中調用。
#include<stdio.h>
#include"Add.h"
int Add(int a,int b)
{
int c=a+b;
return c;
}
//接下來是Add.h文件 ,只是負責對使用的函數進行聲明。
#include"stdio.h"
int Add(int a,int b);
由於在主函數中先調用的hello.c文件,所以你就先編譯hello.c生成.o文件,當然你如果先編譯Add.c或者print.c也是可以的,因爲他們三個沒有互相調用,當如果hello.c調用print.c中的函數,這個時候你就需要先編譯print.c文件,然後才能編譯hello.c文件。
之後再將生成的目標文件鏈接相應的庫生成可執行文件。接下來,就按照講的順序來編譯運行鏈接運行一下程序。
gcc hello.c -o hello.o -c
gcc Add.c -o Add.o -c
gcc print.c -o print.o -c
gcc test.c -o test.o -c
gcc test.o print.o hello.o Add.o -o test
./test
hello,world,今天開始學習make編譯!
請輸入兩個計算的數
12
34
c=46
結束!終於學會make編譯了!
是不是有一點小開心,自己今天終於有收穫了,進步就是這樣一點點來的。
當然如果每次修改一下程序都要這樣編譯一下,是不是會覺得很浪費時間,當然這還是隻有幾個文件,如果出現上百個文件,豈不是時間都花在編譯指令上了,調試起來太麻煩了,所以就出現了makefile文件。
makefile文件中主要放的就是你程序的編譯指令,通過make就可以直接執行makefile文件中的指令,就進行編譯了,無需再慢慢每個文件進行編譯了。
接下來就看看makefile文件的編寫規則。首先放一個我自己寫的剛剛的那個工程的makefile文件。
test:test.o Add.o hello.o print.o
gcc -o test test.o Add.o hello.o print.o
test.o: test.c Add.h hello.h print.h
gcc -c test.c -o test.o
Add.o: Add.c Add.h
gcc -c Add.c -o Add.o
hello.o: hello.c hello.h
gcc -c hello.c -o hello.o
print.o: print.c print.h
gcc -c print.c -o print.o
clean:
rm *.o test
頂層需要寫自己最後生成的目標文件,也就是可執行文件test,:感嘆號後面的就是生成可執行文件需要用到的目標文件,
下面就是生成可執行文件的指令,需要把所有的目標文件鏈接形成可執行文件。
接下來就是目標文件的生成,需要用到-c這個指令,把每個.c文件都生成.o文件就可以了。
最後一行的話,是清除生成的.o文件。接下來我們用makefile指令編譯一下,make之後,就可以看到編譯器有相應的輸出,之後可以看到有可執行文件test了。
make
#編譯器輸出
gcc -c test.c -o test.o
gcc -c Add.c -o Add.o
gcc -c hello.c -o hello.o
gcc -c print.c -o print.o
gcc -o test test.o Add.o hello.o print.o
./test
hello,world,今天開始學習make編譯!
請輸入兩個計算的數
12 34
c=46
結束!終於學會make編譯了!
其中比較關鍵的兩點需要注意的就是:
第一點是默認的makefile文件名有兩種:makefile或者Makefile,
這兩種可以直接使用make指令,編譯器是可以識別的,如果換成其他的文件,就需要用到-f指令了,例如
make
#編譯器輸出
make: *** 沒有指明目標並且找不到 makefile。 停止。
make -f MakeFile
#編譯器輸出
make: 'test' is up to date.
第二點是makefile文件在書寫編譯指令的時候(第二行中gcc那一行),需要使用Tab鍵來空格,要不然會出現錯誤。
GDB調試
習慣了Windows上面軟件自帶調試系統,可以隨意設置斷點、單步、運行等調試手段,Linux上面的調試方式還真有點不習慣 ,有點原生態,什麼都需要自己去寫,去設置,比如上面的編譯。
- 首先想用gdb調試,在寫makefile文件時,要加入 -g調試選項。
- 然後gdb+可執行文件,即可進入調試界面。
- b + 行數,然後r 即可運行到指定的程序行數
- print + 變量,即可查看變量的內容
- n可以單步調試,但是不進入程序內部
- s單步調試,但進入程序內部
- 如果不設斷點,可以直接r運行程序。
基本上面這些就滿足日常的調試需求。
gdb+運行程序的好處就是,如果遇到段錯誤(segmentation fault),可以直接停在錯誤的地方,可以看到錯誤的行數以及在哪個文件,方便查找錯誤,
如果直接運行程序,程序退出後,只是顯示段錯誤,並不會提示其他信息。當然如果語法錯誤,編譯的時候就會顯示出來。
如有雷同,純屬我抄你,有問題可以直接聯繫郵箱,在個人資料裏面。