《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
打印所有的警告信息

posted @ 2016-04-11 09:42  M_Lion  阅读(1116)  评论(0编辑  收藏  举报