《Linux C编程一站式学习》-笔记
hello world
1.
2.GDB
编译时候加上-g,生成的可执行文件才能用gdb进行调试
$gcc -g main.c -o main //-g选项的作用是在可执行文件中加入源代码的信息
$gdb main
(gdb)help //可以查看命令的类别
(gdb)help files //可以查看某一类别中有哪些具体命令
(gdb)list 1 //从第一行开始列出源代码 gdb的很多常用命令有简写形式,例如list命令可以写成l
// gdb提供了一个很方便的功能,在提示符下直接敲回车表示用适当的参数重复上一条命令。
(gdb)quit //退出gdb环境
$gdb main //调试main
(gdb)start //开始执行程序
(gdb)next //简写为n 控制这些语句一条一条地执行
(gdb)step //简称s 进入函数中去执行
(gdb)backtrack //简写为bt,可以查看函数调用的栈帧
(gdb) info locals //可以用info命令(简写为i)查看局部变量的值
(gdb)frame 1 //先用frame命令(简写为f)选择1号栈帧然后再查看局部变量
(gdb)print sum //然后用print命令(简写为p)打出变量sum的值
(gdb)finish //可以用finish命令让程序一直运行到从当前函数返回为止
(gdb)continue //用continue命令(简写为c)连续运行而非单步运行,程序到达断点会自动停下来
(gdb) break 9 if sum != 0 //条件断点
3.$ gcc main.c stack.c -o main //一步编译
$ gcc -c main.c //多步编译
$ gcc -c main.c
$ gcc -c stack.c$ gcc main.o stack.o -o main
$ gcc -c main.c -Wall //用-Wall选项编译main.c
$gcc -c main.c -Istack //编译。用-I选项告诉gcc头文件要到子目录stack里找
4.#、##运算符和可变参数
在函数式宏定义中,#运算符用于创建字符串,#运算符后面应该跟一个形参(中间可以有空格或Tab),例如:
#define STR(s) # sSTR(hello world)
用cpp命令预处理之后是"hello world",自动用"号把实参括起来成为一个字符串,并且实参中的连续多个空白字符被替换成一个空格
在宏定义中可以用##运算符把前后两个预处理Token连接成一个预处理Token,和#运算符不同,##运算符不仅限于函数式宏定义,变量式宏定义也可以用。
#define CONCAT(a, b) a##b
CONCAT(con, cat) //预处理之后是concat。
#define HASH_HASH # ## # //要定义一个宏展开成两个#号,可以这样定义:
中间的##是运算符,宏展开时前后两个#号被这个运算符连接在一起。注意中间的两个空格是不可少的,如果写成####,会被划分成##和##两个Token,而根据定义##运算符用于连接前后两个预处理Token,不能出现在宏定义的开头或末尾
函数式宏定义也可以带可变参数,同样是在参数列表中用...表示可变参数
5.Makefile
- 写一个Makefile文件和源代码放在同一个目录下:
main: main.o stack.o maze.o
gcc main.o stack.o maze.o -o main
main.o: main.c main.h stack.h maze.h gcc -c main.c
stack.o: stack.c stack.h main.h gcc -c stack.c
maze.o: maze.c maze.h main.h gcc -c maze.c
//main是这条规则的目标(Target),main.o、stack.o和maze.o是这条规则的条件(Prerequisite)。目标和条件之间的关系是:欲更//新目标,必须首先更新它的所有条件;所有条件中只要有一个条件被更新了,目标也必须随之被更新
//所谓“更新”就是执行一遍规则中的命令列表,命令列表中的每条命令必须以一个Tab开头,注意不能是空格,Makefile的格式不像C语言的//缩进那么随意
- 然后在这个目录下运行make编译
$ make //make命令会自动读取当前目录下的Makefile文件
gcc -c main.c
gcc -c stack.c
gcc -c maze.c
gcc main.o stack.o maze.o -o main
//对于Makefile中的每个以Tab开头的命令,make会创建一个Shell进程去执行它
- 1. 尝试更新Makefile中第一条规则的目标main,第一条规则的目标称为缺省目标,只要缺省目标更新了就算完成任务了,其它工作都是为这个目的而做的。由于我们是第一次编译,main文件还没生成,显然需要更新,但规则说必须先更新了main.o、stack.o和maze.o这三个条件,然后才能更新main。
- 2. 所以make会进一步查找以这三个条件为目标的规则,这些目标文件也没有生成,也需要更新,所以执行相应的命令(gcc -c main.c、gcc -c stack.c和gcc -c maze.c)更新它们。
- 3. 最后执行gcc main.o stack.o maze.o -o main更新main。
通常Makefile都会有一个clean规则,用于清除编译过程中产生的二进制文件,保留源文件
6.
const int *a;
int const *a; //这两种写法是一样的,a是一个指向const int型的指针,a所指向的内存单元不可改写
int * const a; //a是一个指向int型的const指针,*a是可以改写的,但a不允许改写
int const * const a; //a是一个指向const int型的const指针,因此*a和a都不允许改写。
7.
$umask //Shell进程的umask掩码可以用umask命令查看
$umask 0 //我们可以把Shell进程的umask改成0
$ touch file123$ ls -l file123-rw-r--r-- 1 akaedu akaedu 0 2009-03-08 15:07 file123
用touch命令创建一个文件时,创建权限是0666,而touch进程继承了Shell进程的umask掩码,所以最终的文件权限是0666&~022=0644。
8.gcc常用选项
-c
编译生成目标文件
-E
只做预处理而不编译,cpp命令也可以达到同样的效果
-g
在生成的目标文件中添加调试信息,所谓调试信息就是源代码和指令之间的对应关系,在gdb调试和objdump反汇编时要用到这些信息
-Idir
dir是头文件所在的目录
-Ldir
dir是库文件所在的目录
-M和-MM
输出".o文件: .c文件.h文件”这种形式的Makefile规则,-MM的输出不包括系统头文件
-o outfile
outfile输出文件的文件名
-O?
各种编译优化选项
-v
打印详细的编译链接过程
-Wall
打印所有的警告信息