三、Linux下编译与调试

gcc/g++编译器

对于.c格式的C文件,可以采用gcc或g++编译

对于 .cc、.cpp格式的C++文件,应该采用g++进行编译

常用的选项:

-c  表示编译源文件

-o  表示输出目标文件

-g  表示在目标文件中产生调试信息,用于gdb调试

-D<宏定义> 编译时将宏定义传入进去

-Wall  打开所有类型的警告。

1. gcc编译过程:预编译->编译->汇编->链接

当我们进行编译的时候,要使用一系列的工具,我们称之为工具链.

其中包括:预处理器,编译,汇编器as,连接器. 一个编译过程包括下面

几个阶段:

(1)预处理:预处理器将对源文件中的宏进行展开。

(2)编译:gcc将c文件编译成汇编文件。

(3)汇编:as将汇编文件编译成机器码。

(4)链接:将目标文件和外部符号进行连接,得到一个可执行二进制文件。

下面以一个很简单的test.c来探讨这个过程。

#include <stdio.h>

#define NUMBER  (1+2)

int main()

{

  int x = NUMBER;

  return 0;

}

(1) 预处理:gcc –E test.c -o test.i 我们用cat查看test.i的内容如下:

int  main()  int x=(1+2);  return 0;我们可以看到,文件中宏定义NUMBER出现的位置被(1+2)替换掉了,其它的内容保持不变。

(2) 编译:gcc -S test.i –o test.s 通过cat test.s查看test.s的内容为代码。

(3) 汇编:as test.s -o test.o 利用as将汇编文件编译成机器码。得到输出文件为test.o.  test.o中为目标机器上的二进制文件. 用nm查看文件中的符号: nm test.o输出如下:00000000 T main。有的编译器上会显示:00000000 b .bss  00000000 d .data    00000000

t .text   U ___main    U __alloca  00000000 T _main既然已经是二进制目标文件了,能不能执行呢?试一下./test.o,提示cannotexecute binary file.原来___main前面的U表示这个符号的地址还没有定下来,T表示这个符号属于代码。

(4)链接:gcc –o test test.o,将所有的.o文件链接起来生产可执行程序。

2. gcc所支持后缀名:

3. gcc常用选项:

预处理阶段:对包含的头文件(#include)和宏定义(#define、#ifdef等)进行处理gcc –E hello.c –o hello.i   //-o表示输出为指定文件类型  -E将源文件(*.c)转换为(*.i)

编译阶段:检查代码规范性、语法错误等,在检查无误后把代码翻译成汇编语言

gcc –S hello.i –o hello.s   //-S将已预处理的C原始程序(*.i)转换为(*.s)

 链接阶段:将.s的文件以及库文件整合起来链接为可执行程序 gcc –o hello.exe hello.s   //最后将汇编语言原始程序(*.s)和一些库函数整合成(*.exe)

Example1:

#include <stdio.h>

#define MAX 100

#define max(a,b) ((a)>(b)?(a):(b))   //宏定义,执行-E之后被替换

main()

{

printf("MAX=%d\n",MAX);

printf("max(3,4)=%d\n",max(3,4));

}

//法一:

gcc –E project1.c –o project1.i  //预编译,生成已预编译过的C原始程序*.i

gcc –S project1.i –o project1.s  //编译,生成汇编语言原始程序*.s

gcc –o project1.exe  project1.s //链接,生成可执行程序

//法二:

gcc –c project1.c –o project1.o   //编译

gcc –o project1.exe project1.o    //链接

//法三:

gcc –o project1.exe project1.c    //编译并链接

Example2:

#include <stdio.h>

main()

{

#ifdef cjy     //表示如果定义了cjy,即命令行参数传了cjy,就执行下面的输出

printf("cjy is defined!\n");

#else

printf("cjy is not defined!\n");

#endif

printf("main exit\n");

}

gcc –E project2.c –o project2.i –D cjy  //条件编译,用-D传递,如果没有传cjy则执行#else

gcc –S project2.i –o project2.s

gcc –o project2.exe  project2.s

或:gcc –o project2 project2.c –D cjy

4. gcc库选项

函数库分为静态库和动态库。

静态库是目标文件.a的归档文件(格式为libname.a)。如果在编译某个程序时链接静态库,则链接器将会搜索静态库并直接拷贝到该程序

的可执行二进制文件到当前文件中;

动态库(格式为libname.so[.主版本号.次版本号.发行号])。在程序编译时并不会被链接到目标代码中,而是在程序运行时才被载入。

创建静态库

$ gcc  -c  add.c    //编译add.c源文件生成add.o目标文件

$ ar  crsv  libadd.a  add.o  //对目标文件*.o进行归档,生成lib*.a,

此处lib要写

$ gcc  -o  mian  main.c  -L./  –ladd –I./     //不要忘记-L后面的那个. (即在库文件的搜索路径中添加当前路径   -ladd表示链接库文件libadd.a/.so  -I./表示包含在当前目录中的头文件)

$./main

创建动态库

$ gcc  -fPIC  -Wall  -c  add.c

$ gcc  -shared  -o  libadd.so  add.o

$ gcc  -o  main  main.c  -L.  –ladd

在运行main前,需要注册动态库的路径。方法有3种:修改

/etc/ld.so.conf或修改LD_LIBRARY_PATH环境变量或将库文件拷贝到

/lib或者/usr/lib下(系统默认搜索库路径)。

$ cp libadd.so /lib    //通常采用的方法,à cp  lib*.so  /lib

$ ./main

如果不拷贝,生成.so之后还有两种方法:

gcc –o main main.c –L. –Wl,-rpath,${PWD} –ladd

在main.c中修改如下:

#include <stdio.h>

#include <dlfcn.h>

#include "add.h"

#define  LIB_NAME  "./libadd.so" //注意这里的./不能少,要不然你

就写绝对路径,否则,会在系统的库搜索路径中查询,如/lib和/usr/lib

#define  FLAGS  RTLD_NOW   //表示现在就替换

int main(int argc, char *argv[])

{

  void *handle = NULL;

  void (*func)(int, int);//typedef void (*func)(int, int); 也就是add的类型

//open

handle = dlopen(LIB_NAME, FLAGS);

if(NULL == handle)

{

  printf("open err!\n");

  return -1;

}

  //find "my_printf"

  func = dlsym(handle, "add"); //add表示具体的函数名字

  //run

  func(3, 4);

  //close

  dlclose(handle);

  return 0;

}

最后执行:gcc –o main main.c –ldl

创建动态链接库之后,以后就可以使用该动态链接库了

例如在test.c里面调用了原来库中的函数,则执行gcc –o test test.c –lfunc就可以了。

静态库与动态库的比较:

动态库只在执行时才被链接使用,不是直接编译为可执行文件,并且一个动态库可以被多个程序使用故可称为共享库。

静态库将会整合到程序中,在程序执行时不用加载静态库。 因此,静态库会使你的程序臃肿并且难以升级,但比较容易部署。而动态库

会使你的程序轻便易于升级但难以部署。

Example:写一个求两个数 +,--,*,/ 的函数func.c(func.h),在main.c中调用执行相应的算术操作,但是不直接针对main.c编译链接,而

是在function.sh中对func函数创建静态库和动态库,并分别自动执行main函数。(注:当然可以直接针对main函数)

编写function.sh

#!/bin/bash

echo "==========================="

echo "***************************"

echo "    1.create static lib     " //静态库创建

echo "    2.create shared lib    " //动态库创建

echo "***************************"

echo "==========================="

echo "please input your operator:"

read op

case $op in

"1") //以静态库的方式

gcc -c ${1}.c     //${1}接收第一个传进来的参数func,并编

译它

ar rcsv lib${1}.a ${1}.o  //将其打包为静态库

gcc -o ${2} ${2}.c -L. -l${1}  //${2}接收第二个传进来的main

./${2}          //运行main程序输出结果

;;

"2") //以动态库的方式

gcc -fpic -c ${1}.c

gcc -shared -o lib${1}.so ${1}.o

gcc -o ${2} ${2}.c -L. -l${1}

sudo cp lib${1}.so /lib/     //切换到root用户下

./${2}

;;

*)

exit 3

;;

esac

编写func.h

#ifndef __FUNC_H

#define __FUNC_H

extern int add(int,int);

extern int sub(int,int);

extern int mul(int,int);

extern int div(int,int);

#endif

编写func.c

#include "func.h"

int add(int a,int b)

{

return a + b;

}

int sub(int a,int b)

{

return a - b;

}

int mul(int a,int b)

{

return a * b;

}

int div(int a,int b)

{

return a / b;

}

编写main.c

#include <stdio.h>

#include "func.h"

main()

{

printf("add(3,4)=%d\n",add(3,4));

printf("sub(4,1)=%d\n",sub(4,1));

printf("mul(3,2)=%d\n",mul(3,2));

printf("div(6,2)=%d\n",div(6,2));

}

# sh  function.sh  func  main   //将func,main作为参数传递进去

当然也可以不用写function.sh,可以直接针对main操作,分别采用静态库和动态库的方式将函数func.c打包

1. 将func.o打包为静态库函数libfunc.a,并执行程序:

# gcc –c func.c      //1. 将func.c编译为func.o

# ar rcsv libfunc.a func.o   //2. 用ar rcsv将func.o打包为静态库libfunc.a(前面的lib要写)

# gcc –o main.exe main.c –L. –lfunc //3. 链接库函数和执行main.c生成可执行程序main.exe

# ./main.exe   //4. 执行./main.exe

2. 将func.o打包为动态库函数libfunc.so,并执行程序:

# gcc –fpic –c func.c  //1. 用动态库的方式将func.c编译为func.o

# gcc –shared –o libfunc.so func.o //2. 用gcc -shared将func.o打包为动态库libfunc.so

# gcc –o main.exe main.c –L. –lfunc //3. 链接库函数和执行main.c生成可执行程序main.exe

# sudo cp libfunc.so /lib  //4. 非超级用户要用sudo将动态库libfunc.so拷贝到/lib目录下

# ./main.exe   //5. 执行./main.exe

5. gcc --- 警告选项

对于如下程序:

#include <stdio.h>

void main()

{

  long long temp = 1;

  printf(“This is a bad code!\n”);

  return 0;

}

-ansi : 生成标准语法(ANSI C标准)所要求的警告信息(并不列出所有警告)

$ gcc –ansi warning.c –o warning

warning.c: 在函数“main”中:

warning.c:7 警告:在无返回值的函数中,“return”带返回值

warning.c:4 警告:“main”的返回类型不是“int”

可以看出,该选项并没有发现“long long”这个无效数据类型的错误

-pedantic : 列出ANSI C标准的全部警告信息。

$ gcc –pedantic warning.c –o warning

warning.c: 在函数“main”中:

warning.c:5 警告:ISO C89不支持“long long”

warning.c:7 警告:在无返回值的函数中,“return”带返回值

warning.c:4 警告:“main”的返回类型不是“int”

-Wall : 列出所有的警告信息(常用)

$ gcc –Wall warning.c –o warning

warning.c:4 警告:“main”的返回类型不是“int”

warning.c: 在函数“main”中:

warning.c:7 警告:在无返回值的函数中,“return”带返回值

warning.c:5 警告:未使用的变量“tmp”

$gcc –Werror warning.c –o warming

通常用的是-Wall显示所有有用的报警信息。

6. gcc --- 优化选项

gcc对代码进行优化通过选项“-On”来控制优化级别(n是整数)。不同 的优化级别对应不同的优化处理工作。如使用优化选项“-O1”主要进行线程跳转和延迟退栈两种优化。使用优化选项“-O2”除了完成所有“-O1”级别的优化之外,还要进行一些额外的调整工作,如处理其指令调度等。选项“-O3”则还包括循环展开或其他一些与处理器特性相关的优化工作。虽然优化选项可以加速代码的运行速度,但对于调试而言将是一个很大的挑战。因为代码在经过优化之后,原先在源程序中声明和使用的变量很可能不再使用,控制流也可能会突然跳转到意外的地方,循环语句也有可能因为循环展开而变得到处都有,所有这些对调试来讲都是不好的。所以在调试的时候最好不要使用任何的优化选项,只有当程序在最终发行的时候才考虑对其进行优化。

通常用的是-O2

-D <宏定义>     编译时将宏定义传入进去

eg: # gcc –o hello –Wall –O2 hello.c

例:有两个文件main.cpp,func.cpp

其中

main.cpp内容为:

#include <stdio.h>

int MyFunc();

int main()

{

#ifdef _DEBUG

printf("Debug MyFunc is:%d\n",MyFunc());

#else

printf("NDEBUG MyFunc is:%d\n",MyFunc());

#endif

}

func.cpp内容为:

int MyFunc()

{

return 123;

}

编译和连接:

g++ -c func.cpp   //C++文件类型的,如.cpp的一定要用g++

将编译func.cpp,并且生成同名的但扩展名为.o的二进制目标文件 func.o

g++ -c main.cpp

将编译main.cpp,并且生成同名的但扩展名为.o的二进制目标文件 main.o

2、g++ -c func.cpp -o func.o

g++ -c main.cpp –o main.o

编译main.cpp,并输出目标文件main.o

链接

g++ main.o func.o  //默认情况下生成的是a.out可执行文件

g++ -o a.out main.o func.o

g++ -o a.out *.o

都将连接目标文件main.o和func.o最后形成可执行文件a.out

对于第一种,如果没有显式指定可执行文件名,g++默认为a.out

也可以将编译和链接的过程合为一块处理:

g++ *.cpp

g++ func.cpp main.cpp (不加任何参数表示编译并链接生成可执行文件a.out)

g++ -o a.out func.cpp main.cpp

都将先编译指定的源文件,如果成功的话,再链接成可执行文件a.out

如果希望在编译时传入宏定义,可使用-D参数,例如

g++  *.cpp  -D  _DEBUG

make工程管理器

可以试想一下,有一个上百个文件的代码构成的项目,如果其中只有一个活少数几个文件进行了修改,如果再从头到尾将每一个文件都重新编译是个比较繁琐的过程。为此,引入了Make工程管理器的概念,工程管理器指管理较多的文件,它是自动管理器能根据文件时间自动发现更新过的文件而减少编译的工作量,同时通过读入Makefile文件来执行大量的编译工作

makefile格式

target: dependency_files  //目标项:依赖项

<  TAB >command  //必须以tab开头,command编译命令

注意点:在写command命令行的时候,必须要在前面按TAB键

例如,有Makefile文件,内容如下:

使用make编译

对于该Makefile文件,程序make处理过程如下:

make程序首先读到第1行的目标文件main.exe和它的两个依赖文件

main.o和func.o;然后比较文件main.exe和main.o/func.o的产生时间,

如果main.exe比main.o/func.o旧的话,则执行第2条命令,以产生

目标文件main.exe。

在执行第2行的命令前,它首先会查看makefile中的其他定义,看有

没有以第1行main.o和func.o为目标文件的依赖文件,如果有的话,

继续按照(1)、(2)的方式匹配下去。

根据(2)的匹配过程,make程序发现第3行有目标文件main.o依赖于

main.cpp,则比较目main.o与它的依赖文件main.cpp的文件新旧,如

果main.o比main.cpp旧,则执行第4行的命令以产生目标文件main.o.

在执行第4条命令时,main.cpp在文件makefile不再有依赖文件的定

义,make程序不再继续往下匹配,而是执行第4条命令,产生目标文件

main.o

目标文件func.o按照上面的同样方式判断产生.

执行(3)、(4)产生完main.o和func.o以后,则第2行的命令可以顺利

地执行了,最终产生了第1行的目标文件main.exe。

特殊处理与伪目标

.PHONY是makefile文件的关键字,表示它后面列表中的目标均为伪

目标

.PHONY:b

b:

echo  ‘b’          //通常用@echo “hello”

伪目标通常用在清理文件、强制重新编译等情况下。

Example1:main.c函数,func.c函数为前面计算+,-,*,/运算的程序

#vi Makefile    //系统默认的文件名为Makefile

main.exe:main.o func.o   //表示要想生成main.exe文件,要依赖于

main.o和func.o文件

gcc -o main.exe main.o func.o//如果main.o,func.o已经存在

了,就链接成main.exe

main.o:main.c    //表示main.o文件依赖于main.c文件

gcc -c main.c   //编译main.c,默认生成main.o。可写为:

gcc –c main.c –o main.o

func.o:func.c     //表示func.o文件依赖于func.c文件

gcc -c func.c   //如果func.c存在,则编译func.c ,生成func.o

.PHONY:rebuild clean //表示后面的是伪目标,通常用在清理文件、强

制重新编译等情况下

rebuild:clean main.exe //先执行清理,在执行main.exe

clean:

rm –rf main.o func.o main.exe   //最后删除.o和.exe的文件

按ESC键之后,:wq保存退出

再执行下面的命令:

#make   //直接make,即从默认文件名(Makefile)的第一行开始

执行

#make clean  //表示执行clean: 开始的命令段

#make func.o  //表示执行func.o: 开始的命令段

#make rebuild //则先执行清除,再重新编译连接

如果不用系统默认的文件名Makefile,而是用户随便起的一个名字,

如:

#vi  Makefile11

则make 后面必须要加上 -f Makefile11 ,如:

#make –f Makefile11 clean   //表示执行clean: 开始的命令段

#make –f Makefile11 main.exe   //表示执行main.exe: 开始的命令段

变量、函数与规则

随着软件项目的变大、变复杂,源文件也越来越多,如果采用前面的方式写makefile文件,将会使makefile也变得复杂而难于维护。通过make支持的变量定义、规则和内置函数,可以写出通用性较强的makefile文件,使得同一个makefile文件能够适应不能的项目。

变量:用来代替一个文本字符串

定义变量的2种方法:

变量名=变量值 递规变量展开(几个变量共享一个值)  //不常用

变量名:=变量值 简单变量展开(类似于C++的赋值)    //通常采用这种形式

使用变量的一般方法:$(变量名)=???   赋值

???=$(变量名)  引用

例:将以前的那个可以写为:

变量分为:用户自定义变量,预定义变量,自动变量,环境变量

自动变量:指在使用的时候,自动用特定的值替换。

常用的有:


变量


说明


[email protected]


当前规则的目标文件


$<


当前规则的第一个依赖文件


$^


当前规则的所有依赖文件,以逗号分隔


$?


规则中日期新于目标文件的所有相关文件列表,逗号分隔


$(@D)


目标文件的目录名部分


$(@F)


目标文件的文件名部分

Examp:用自动变量:

OBJS:= main.o func.o//$(OBJS)相当于main.o func.o (原样替换)

EXE:= main.exe

CFLAGS:= -Wall -O2 –fpic   //显示所有警告信息,优化级别为2

LIBFUNCSO:= libfunc.so    //动态库

LIBFUNCA:= libfunc.a     //静态库

$(EXE):$(OBJS) $(LIBFUNCSO) $(LIBFUNCA)

gcc -o [email protected] $< -L. -lfunc

main.o: main.c

gcc -c $(CFLAGS) $< -o [email protected]

func.o: func.c

gcc -c $(CFLAGS) $< -o [email protected]

libfunc.a: func.o

ar rcsv [email protected] $<

libfunc.so: func.o

gcc -shared -o [email protected] $<

cp -f [email protected] /lib

.PHNOY:rebuild clean

rebuild:clean $(EXE)

clean:

rm -rf $(EXE) $(OBJS) $(LIBFUNCSO) $(LIBFUNCA)

预定义变量:内部事先定义好的变量,但是它的值是固定的,并且有些的值是为空的。

AR:库文件打包程序默认为ar

AS:汇编程序,默认为as

CC:c编译器默认为cc

CPP:c预编译器,默认为$(CC) –E

CXX:c++编译器,默认为g++

RM:删除,默认为rm –f

ARFLAGS:库选项,无默认

ASFLAGS:汇编选项,无默认

CFLAGS:c编译器选项,无默认

CPPFLAGS:c预编译器选项,无默认

CXXFLAGS:c++编译器选项

根据内部变量,可以将makefile改写为:

规则分为:普通规则,隐含规则,模式规则

隐含规则://*.o文件自动依赖*.c或*.cc文件,所以可以省略main.o:main.cpp等

OBJS := main.o fun.o

CFLAGS := -Wall –O2 -g

main.exe: $(OBJS)

gcc $^ -o [email protected]

模式规则:通过匹配模式找字符串, %匹配1或多个任意字符串

%.o: %.cpp任何目标文件的依赖文件是与目标文件同名的并且扩展名为.cpp的文件

OBJS := main.o fun.o

CFLAGS := -Wall –O2 –g

main.exe : $(OBJS)

gcc $^ -o [email protected]

%.o: %.cpp   //模式通配

gcc -o [email protected] -c $^

另外还可以指定将*.o、*.exe、*.a、*.so等编译到指定的目录中:

DIR:=./Debug/

EXE:=main.exe

OBJS:=main.o

LIBFUNCSO:=libfunc.so

CFLAGS:= -fpic

$(DIR)$(EXE):$(DIR)$(OBJS) $(DIR)$(LIBFUNCSO)

gcc -o [email protected] $< -L./ -lfunc

$(DIR)$(LIBFUNCSO):$(DIR)func.o

gcc -shared -o [email protected] $^

$(DIR)main.o:main.c

gcc -o [email protected] -c $^

$(DIR)func.o:func.c

gcc $(CFLAGS) -c $^ -o [email protected]

.PHONY:rebuild clean

rebuild:clean $(DIR)$(EXE)

clean:

rm -rf $(DIR)*.o $(DIR)*.exe $(DIR)*.so

注意:当OBJS里面有多项的时候,此时$(DIR)$(OBJS)只能影响到OBJS中第一个,后面的全部无效,因此需要全部列出来。

函数:

1. wildcard搜索当前目录下的文件名,展开成一列所有符合由其参数描述的文件名,文件间以空格间隔。SOURCES = $(wildcard  *.cpp)把当前目录下所有‘.cpp‘文件存入变量 SOURCES 里。

2. 字符串替换函数:$(patsubst要查找的子串,替换后的目标子串,源字符串)。将源字符串(以空格分隔)中的所有要查找的子串替换成目标子串。如OBJS = $(patsubst %.cpp,%.o,$(SOURCES))

把SOURCES中‘.cpp‘ 替换为‘.o‘ 。

3. $(addprefix 前缀,源字符串)函数把第二个参数列表的每一项前缀上第一个参数值

下面是一个较为通用的makefile:

DIR := ./debug

EXE := $(DIR)/Main.exe

CC := g++

LIBS :=

SRCS := $(wildcard *.cpp) $(wildcard *.c) $(wildcard *.cc)

OCPP := $(patsubst %.cpp, $(DIR)/%.o, $(wildcard *.cpp))

OC := $(patsubst %.c, $(DIR)/%.co, $(wildcard *.c))

OCC := $(patsubst %.cc, $(DIR)/%.cco, $(wildcard *.cc))

OBJS := $(OC) $(OCC) $(OCPP)

RM := rm -rf

CXXFLAGS := -Wall -g

start : mkdebug $(EXE)

mkdebug :

@if [ ! -d $(DIR) ]; then mkdir $(DIR); fi;

$(EXE)  : $(OBJS)

$(CC) -o [email protected] $(OBJS) $(addprefix -l,$(LIBS))

$(DIR)/%.o : %.cpp

$(CC) -c $(CXXFLAGS) $< -o [email protected]

$(DIR)/%.co : %.c

$(CC) -c $(CXXFLAGS) $< -o [email protected]

$(DIR)/%.cco : %.cc

$(CC) -c $(CXXFLAGS) $< -o [email protected]

.PHONY  : clean rebuild

clean :

@$(RM) $(DIR)/*.exe $(DIR)/*.o $(DIR)/*.co $(DIR)/*.cco

rebuild: clean start

(注意gcc和g++的区别:当main.c中调用了其他源文件的程序时,gcc -o main.o -c main.c或gcc -o Debug/main.o -c main.c都没有问题,而g++ -o main.o -c main.c没问题,但g++ -o Debug/main.o -c main.c有问题,如果想解决这个问题,需要在main.c中添加对于函数调用的函数原型声明即可)

make的命令行选项:

程序调试

gdb常用命令

Linux 包含了一个叫gdb的调试程序。gdb可以用来调试C和C++ 程序。

在程序编译时用 -g 选项可打开调试选项.

关于GDB常用命令,请参考《DGB常用命令.txt》

常见的调试程序的步骤如下:

gcc –o filename.o –Wall filename.c –g //进入调试用gcc -o fn.o  fn.c  -g

gdb filename.o //进入调试

l   //显示代码 (list)

b 4   //在第四行设置断点   相当于Windows的F9  (break)

r     //运行               相当于Windows的F5 (run)

n      //下一步不进入函数   相当于Windows的F10 (next)

s //表示单步进入函数, 相当于Windows的F11 (step)

p  I //打印变量I          相当于Windows的Watch窗口 (print)

c        //运行到最后     (continue)

q //退出               相当于Windows的Shift+F5 (quit)

按 Tab 键补齐命令,用光标键上下翻动历史命令. 用help up看帮助

gdb 应用举例

下面列出了将被调试的程序它显示一个简单的问候, 再用反序将它列出

main.cpp:

void MyPrint(const char *pszSrc);

void MyPrint2(const char *pszSrc);

int main ()

{

char szSrc[] = "hello there";

MyPrint(szSrc);

MyPrint2(szSrc);

}

func.cpp

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

void MyPrint(const char *pszSrc)

printf("The string is %s\n", pszSrc);

}

void MyPrint2(const char *pszSrc)

{

char *pszRev;

int i,iLen;

iLen=strlen(pszSrc);

pszRev=(char *)malloc(iLen+1);

for(i=0;i<iLen;i++)

pszRev[i]=pszSrc[iLen-i];  //经过调试,发现此处应为:[iLen-i-1]

pszRev[iLen]=‘\0‘;

printf("The revert string is:%s\n",pszRev);

free(pszRev);

}

用下面的命令编译它(注意加上-g的编译选项): 
g++ -g *.cpp  //或者为g++ -o main.o main.cpp func.cpp –g

gdb main.o //如果不用gdb main.o则可以利用gdb进入调试之后再输入(gdb) file main.o

(gdb) l[ist] func.cpp:1  列出源代码: 技巧: 在gdb 提示符下按回车健将重复上一个命令.

(gdb) break 17 //b

(gdb) run //r

(gdb) watch pszRev[i] //w

(gdb) next //n

(gdb) info b //[查看所有断点信息]

(gdb) continue //[继续全速运行]

(gdb) p i

(gdb) p iLen

小知识:

1. 在linux中利用system(“clear”);实现类似于windows里面的清屏函数system(“cls”);

2. LINUX中可以通过下面的方式可以实现system("pause");功能:

printf(“Press any key to continue…”);

getchar();

getchar();  //要用两个getchar()函数

3. linux中如何刷新输入缓冲区,利用getchar()函数即可。输出缓冲区可以利用fflush(stdout);

时间: 2024-11-10 14:08:19

三、Linux下编译与调试的相关文章

linux下编译qt5.6.0静态库——configure配置

 随笔 - 116  文章 - 4  评论 - 7 linux下编译qt5.6.0静态库--configure配置 linux下编译qt5.6.0静态库 linux下编译qt5.6.0静态库 configure生成makefile 安装选项 Configure选项 第三方库: 附加选项: QNX/Blackberry选项: Android 选项: 生成makefile 遇到链接检查失败的情况 生成makefile后进行编译 编译时的错误 多重定义'QT_MODBUS()'和'QT_MODBU

linux下编译安装php各种报错大集合

PHP开源脚本语言 PHP(外文名: Hypertext Preprocessor,中文名:"超文本预处理器")是一种通用开源脚本语言.语法吸收了C语言.Java和Perl的特点,入门门槛较低,易于学习,使用广泛,主要适用于Web开发领域.PHP的文件后缀名为php. 本文为大家整理汇总了一些linux下编译安装php各种报错大集合 ,感兴趣的同学参考下. 报错1:make 后报错如下: Generating phar.php /home/oldboy/tools/php-5.3.27

[转载]linux下编译php中configure参数具体含义

编译N次了   原来这么回事 原文地址:linux下编译php中configure参数具体含义作者:捷心特 php编译参数的含义 ./configure –prefix=/usr/local/php                      php 安装目录 –with-apxs2=/usr/local/apache/bin/apxs –with-config-file-path=/usr/local/php/etc      指定php.ini位置 –with-mysql=/usr/local

【转】Linux下编译ffmpeg

1.下载ffmpeg.下载网址:http://www.ffmpeg.org/download.html 2.解压缩tar -zxvf ffmpeg-2.0.1.tar.gz 3.配置,生成Makefile./configure --enable-shared --disable-yasm --prefix=/usr/local/ffmpeg如果执行结果不对,可以根据提示信息,并查看帮助,解决问题./configure --help 4.编译安装makemake install 5.安装之后在/u

Linux下的反调试技术

Linux下的反调试技术 2014年01月30日 ⁄ 综合 ⁄ 共 2669字 ⁄ 字号 小 中 大 ⁄ 评论关闭 转自  http://wangcong.org/blog/archives/310 如何防止自己的程序被调试器跟踪,这是一个很有趣的话题,也是反逆向工程中的一个重要话题.这里简单介绍一下Linux平台上的反调试技术. (本文主要参考:http://blog.txipinet.com/2006/10/05/37-tecnicas-anti-debugging-sencillas-pa

Linux 下编译并安装配置 Qt 4.53全过程

最近准备做 Nokia 的 Symbian,Maemo 下触摸屏开发.考虑到程序的跨平台可移植性,最终选择使用 Qt 开发.相对来说,国内关于 Qt 相关文档并不算很多.作者将 Linux 下编译并安装配置 Qt 全过程总结了一下,只希望可以方便更多的朋友! 1.获得源代码         src 官网下载地址:ftp://ftp.qt.nokia.com/qt/source/        2009 年 10 月 1 日发布的 qt-x11-opensource-src-4.5.3.tar.g

ffmpeg 在linux下编译

2012-06-25 19:36:06|  分类: 默认分类|举报|字号 订阅 1.下载ffmpeg.    wget http://down1.chinaunix.net/distfiles/ffmpeg-0.4.9-p20051120.tar.bz2 我是在这个网址上下载ffmpeg-0.4.9-p20051120.tar.bz2,看网上有人用的是svn下载,但是我的机子中没有svn客户端,然后就在网上搜索,下载了这个版本.如果利用svn可以下载ffmpeg最新的版本. svn checko

linux下编译QBittorrent-3.1.9.2

序言 QBittorrent是一个新的轻量级BitTorrent客户端,可运行于Linux.windows及其他可能系统,它简单易用,漂亮的外观,功能强大,是linux下的一款比较出色的bt下载工具.然而想使用它却不简单,废了我两天时间终于搞定了,记录一下解决的过程,算是对于两天辛苦的一个总结吧! 配置信息及需要的软件 操作系统:CentOS 6.5 32位 Qt版本:Qt4.6.2,安装系统时自带的. 编译器:gcc4.8.2,需自己编译. 调试器:gdb7.7,需自己编译. QBittorr

(转)Linux下编译安装log4cxx

Linux下编译安装log4cxx 一个项目的服务器端在Linux平台下,用到了开源日志库log4cxx,这个库是apache项目的一个子库.功能很不错.下面记录下它的编译和安装过程. log4cxx的官方下载地址是http://logging.apache.org/log4cxx/index.html ,我用的是0.10.0版本. 为了编译它,还需要两个辅助库,apr,和apr-util,可以在这里下载http://apr.apache.org/download.cgi ,我用的版本分别是ap