首页 > 技术文章 > Linux下编译与调试

cpsmile 2015-03-29 16:41 原文

gcc/g++编译器

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

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

常用的选项:

-c  表示编译源文件

-o  表示输出目标文件

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

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

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

 

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

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

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

几个阶段: 

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

(2)编译:gccc文件编译成汇编文件。 

(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 rcsvfunc.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 -sharedfunc.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.ofunc.o;然后比较文件main.exemain.o/func.o的产生时间,

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

目标文件main.exe

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

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

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

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

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

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

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

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

main.o

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

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

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

特殊处理与伪目标

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

目标

.PHONY:b

b:

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

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

  

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

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

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

main.ofunc.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++的赋值)    //通常采用这种形式

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

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

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

 

 

 

 

 

 

 

 

 

 

 

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

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

常用的有:

变量

说明

$@

当前规则的目标文件

$<

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

$^

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

$?

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

$(@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 $@ $< -L. -lfunc

main.o: main.c

        gcc -c $(CFLAGS) $< -o $@

func.o: func.c

        gcc -c $(CFLAGS) $< -o $@

libfunc.a: func.o

        ar rcsv $@ $<

libfunc.so: func.o

        gcc -shared -o $@ $<

        cp -f $@ /lib

.PHNOY:rebuild clean

rebuild:clean $(EXE)

clean:

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

 

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

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

AS:汇编程序,默认为as

CCc编译器默认为cc

CPPc预编译器,默认为$(CC) –E

CXXc++编译器,默认为g++

RM:删除,默认为rm –f

ARFLAGS:库选项,无默认

ASFLAGS:汇编选项,无默认

CFLAGSc编译器选项,无默认

CPPFLAGSc预编译器选项,无默认

CXXFLAGSc++编译器选项

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

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

OBJS := main.o fun.o

CFLAGS := -Wall –O2 -g

main.exe: $(OBJS)

      gcc $^ -o $@

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

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

OBJS := main.o fun.o

CFLAGS := -Wall –O2 –g

main.exe : $(OBJS)

gcc $^ -o $@

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

gcc -o $@ -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 $@ $< -L./ -lfunc

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

    gcc -shared -o $@ $^

$(DIR)main.o:main.c

    gcc -o $@ -c $^

$(DIR)func.o:func.c

    gcc $(CFLAGS) -c $^ -o $@

.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 $@ $(OBJS) $(addprefix -l,$(LIBS))

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

$(CC) -c $(CXXFLAGS) $< -o $@

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

$(CC) -c $(CXXFLAGS) $< -o $@

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

$(CC) -c $(CXXFLAGS) $< -o $@

.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可以用来调试CC++ 程序

在程序编译时用 -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   //在第四行设置断点   相当于WindowsF9  (break)

r     //运行               相当于WindowsF5 (run)

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

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

p  I //打印变量I          相当于WindowsWatch窗口 (print)

c        //运行到最后     (continue)

q //退出               相当于WindowsShift+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);

 

推荐阅读