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
:撤销
gg/G
:回到文首/文尾,然后输入数字,就可以跳转到行首,输入一个暴大的值,跳到行尾
/或者?
:后面输入数字,可以搜搜文本,n 或者shift+n 可以跳到下一个或者上一个
0 或者$
:行首(HOME) 行尾(END)也行
(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
),可以直接
停在错误的地方
,可以看到错误的行数以及在哪个文件,方便查找错误,
如果直接运行程序,程序退出后,只是显示段错误,并不会提示其他信息。当然如果语法错误,编译的时候就会显示出来。
如有雷同,纯属我抄你,有问题可以直接联系邮箱,在个人资料里面。