• 第一步:如果用gdb调试,需要在gcc编译时添加调试符号,要用-g参数。例如:gcc test.c -o test -g
  • 第二步:使用gdb ./test载入程序。这时GDB会载入test但不会执行。
  • 第三步:使用以下命令进行调试,刚开始一定要先设置断点,否则直接运行完毕了。
    ** 直接回车 执行上次命令(单步调试时很有用)
    ** l 10 查看第十行上面五行和下面五行代码
    ** b 6 第六行设置断点(行号可以用l查看到)
    ** b main 在main函数设置断点
    ** info b 查看断点列表
    ** r 运行代码
    ** p n 查看变量n的值
    ** n 单步调试next(单步跳过)
    ** s 单步调试step(单步进入)
    ** c 恢复运行
    ** finish 执行到返回
    ** help [command] 查看命令帮助
    ** q 退出gdb
    ** set args 指定运行时参数(如:set args 10 20 30 40 50)
    ** show args 查看设置好的运行参数

一些基本概念

在进入主题之前,我们先了解一些基本内容。

C语言是面向过程的编程语言,C语言没有包(命名空间),没有明确的私有和公有函数,一旦你定义了函数,理论上可以被整个程序调用(这里先不讨论折中的static类型的函数)。不论你编辑了几万个还是几十万个.c源程序,最终经过编译,会变成单个二进制可执行文件。在这个文件里所有函数都是平等的,没有公私之分。如果反汇编,可以看到这些函数按顺序一个接一个的排在代码段,并且入口地址是固定的。Linux内核也不例外,Linux内核其实就是一个巨大的二进制可执行文件。

由上可以得出,.c文件的多少,只是根据人的”管理难易程度”决定的。如果你不介意,你可以将所有的程序都写在一个.c文件中,这样反而提高了gcc的效率,gcc不用再去拼接.c文件了。

对于特殊的static类型的函数,它的特殊之处在于,它并不是保存在代码段,而是保存在静态区,因此造成了它的特殊性,这里不再展开讨论。

继续阅读

gcc中char默认是有符号数,但是“char a = 255;”是正确的,a会自动保存为-1。如下代码:

char ch = 255;
printf("ch = %d\n", ch);

结果为-1。
那么,这里有这么一个现象:

char ch = 255;
ch -= 128;
printf("ch = %d\n", ch);

得到的结果为“ch = 127”,这并不是因为255 – 128 = 127,而是-1 – 128 = 127。因为char是单字节的,-1 – 128是不够减的,由于存储空间只有8位,第九位无法表示符号,所以被舍弃,丢了符号位的数字转换为二进制成为了127。
这里举个简单点的例子:
-128的8位二进制为1000 0000
-128 – 1 得到的8位二进制为 0111 1111 (DEC:127)
这是因为,8位二进制无法表示-129,舍弃第九位后,就变为127了。

如何查看自己的gcc对char的处理呢?方法如下:

#include <limits.h>
int main()
{
    printf("CHAR_MIN = %d\n", CHAR_MIN);
    return 0;
}

在limits.h中定义了char的最小值CHAR_MIN,如果CHAR_MIN = -128,则char是有符号数,如果CHAR_MIN = 0,则char是无符号数。

在说明复杂指针定义之前,我们先补习一下基本的指针定义形式。
普通指针
定义:int *p;
这个是定义一个指向int类型数据的指针。但是这样不容易理解,我自己的理解方式如下:
其实,p是一个32位的数字(64位系统为64位,这里以32位),只不过这个数字具体的意思是个内存地址,所以不能进行数学意义上的加减乘除(地址的加减乘除,是以类型大小sizeof(int)为单位的)。
如果我们直接使用p,则是一串数字地址,如果我们想要p地址处的数据,就需要加一个*。
因此,*p诞生了,意思是我要拿出地址p处的数据。
因此,p是一个单独的变量,里面存储的是地址。
因此,我们视觉上,可以这样写:
int* p;
即代表int*类型的变量p。
使用:
既然p是个单独的变量,那么直接使用p,就是在操作地址。为了操作p地址处的值,我们使用*,这时候,*p作为一个整体,含义就与变量一样了。我们直接认为它是个普通的变量即可。修改p地址处的值:
*p = 1;
数组指针与指针数组
数组指针是一个指向数组的指针,指针数组是一个存储指针的数组。这仍然是个抽象的定义,我们来看看例子。
指针数组:
int *p[10];
根据上文提示的,我们可以先转为
int* p[10];
格式,这样我们就很好理解了,p是一个存储int*指针类型的数组。
数组指针:
int (*p)[10];
根据上文理解,由于(*p)带了括号,说明强制*p为一个整体,而*p作为一个整体,可以认为是个普通变量,那我们用普通变量做一下类比,将*p替代为a:
int a[10];
这样就好理解了,*p类似于a,那么p变量保存的就是a的地址。这还有一点,”int (*p)[10];”仅仅是定义了一个指针,p仅占4个字节(32位机器)。
因此,p必须初始化才能用,比如:
int (*p)[10];
int a[10];
p = &a;
这样p初始化后才可以用,否则p是个随机数。
继续阅读