CC++ 编译器和调试器以及静态库动态库使用.docx

上传人:b****4 文档编号:12336252 上传时间:2023-04-18 格式:DOCX 页数:25 大小:29.65KB
下载 相关 举报
CC++ 编译器和调试器以及静态库动态库使用.docx_第1页
第1页 / 共25页
CC++ 编译器和调试器以及静态库动态库使用.docx_第2页
第2页 / 共25页
CC++ 编译器和调试器以及静态库动态库使用.docx_第3页
第3页 / 共25页
CC++ 编译器和调试器以及静态库动态库使用.docx_第4页
第4页 / 共25页
CC++ 编译器和调试器以及静态库动态库使用.docx_第5页
第5页 / 共25页
点击查看更多>>
下载资源
资源描述

CC++ 编译器和调试器以及静态库动态库使用.docx

《CC++ 编译器和调试器以及静态库动态库使用.docx》由会员分享,可在线阅读,更多相关《CC++ 编译器和调试器以及静态库动态库使用.docx(25页珍藏版)》请在冰豆网上搜索。

CC++ 编译器和调试器以及静态库动态库使用.docx

CC++编译器和调试器以及静态库动态库使用

C/C++编译器和调试器以及静态库、动态库使用汇总

阅读提示:

本文是C/C++编译器和调试器以及静态库、动态库使用汇总

大多数unix系统下面的调试器的使用方法如下:

gdb介绍

GNU的调试器称为gdb,该程序是一个交互式工具,工作在字符模式。

在XWindow系统中,有一个gdb的

前端图形工具,称为xxgdb。

gdb是功能强大的调试程序,可完成如下的调试任务:

*设置断点;

*监视程序变量的值;

*程序的单步执行;

*修改变量的值。

在可以使用gdb调试程序之前,必须使用-g选项编译源文件。

可在makefile中如下定义CFLAGS变量:

CFLAGS=-g

运行gdb调试程序时通常使用如下的命令:

gdbprogname

在gdb提示符处键入help,将列出命令的分类,主要的分类有:

*aliases:

命令别名

*breakpoints:

断点定义;

*data:

数据查看;

*files:

指定并查看文件;

*internals:

维护命令;

*running:

程序执行;

*stack:

调用栈查看;

*statu:

状态查看;

*tracepoints:

跟踪程序执行。

键入help后跟命令的分类名,可获得该类命令的详细清单。

gdb的常用命令

命令解释

breakNUM在指定的行上设置断点。

bt显示所有的调用栈帧。

该命令可用来显示函数的调用顺序。

clear删除设置在特定源文件、特定行上的断点。

其用法为:

clearFILENAME:

NUM。

continue继续执行正在调试的程序。

该命令用在程序由于处理信号或断点而

导致停止运行时。

displayEXPR每次程序停止后显示表达式的值。

表达式由程序定义的变量组成。

fileFILE装载指定的可执行文件进行调试。

helpNAME显示指定命令的帮助信息。

infobreak显示当前断点清单,包括到达断点处的次数等。

infofiles显示被调试文件的详细信息。

infofunc显示所有的函数名称。

infolocal显示当函数中的局部变量信息。

infoprog显示被调试程序的执行状态。

infovar显示所有的全局和静态变量名称。

kill终止正被调试的程序。

list显示源代码段。

make在不退出gdb的情况下运行make工具。

next在不单步执行进入其他函数的情况下,向前执行一行源代码。

printEXPR显示表达式EXPR的值。

gdb使用范例

-----------------

清单一个有错误的C源程序bugging.c

-----------------

#include

#include

staticcharbuff[256];

staticchar*string;

intmain()

{

printf("Pleaseinputastring:

");

gets(string);

printf("\nYourstringis:

%s\n",string);

}

-----------------

上面这个程序非常简单,其目的是接受用户的输入,然后将用户的输入打印出来。

该程序使用了一个未经过初

始化的字符串地址string,因此,编译并运行之后,将出现SegmentFault错误:

$gcc-otest-gtest.c

$./test

Pleaseinputastring:

asfd

Segmentationfault(coredumped)

为了查找该程序中出现的问题,我们利用gdb,并按如下的步骤进行:

1.运行gdbbugging命令,装入bugging可执行文件;

2.执行装入的bugging命令;

3.使用where命令查看程序出错的地方;

4.利用list命令查看调用gets函数附近的代码;

5.唯一能够导致gets函数出错的因素就是变量string。

用print命令查看string的值;

6.在gdb中,我们可以直接修改变量的值,只要将string取一个合法的指针值就可以了,为此,我们在第

11行处设置断点;

7.程序重新运行到第11行处停止,这时,我们可以用setvariable命令修改string的取值;

8.然后继续运行,将看到正确的程序运行结果。

运行gcc/egcs

GCC是GNU的C和C++编译器。

实际上,GCC能够编译三种语言:

C、C++和ObjectC(C语言的一种面向对象扩展)。

利用gcc命令可同时编译并连接C和C++源程序。

如果你有两个或少数几个C源文件,也可以方便地利用GCC编译、连接并生成可执行文件。

例如,假设你有

两个源文件main.c和factorial.c两个源文件,现在要编译生成一个计算阶乘的程序。

清单factorial.c

-----------------------

#include

#include

intfactorial(intn)

{

if(n<=1)

return1;

else

returnfactorial(n-1)*n;

}

-----------------------

-----------------------

清单main.c

-----------------------

#include

#include

intfactorial(intn);

intmain(intargc,char**argv)

{

intn;

if(argc<2){

printf("Usage:

%sn\n",argv[0]);

return-1;

}

else{

n=atoi(argv[1]);

printf("Factorialof%dis%d.\n",n,factorial(n));

}

return0;

}

-----------------------

利用如下的命令可编译生成可执行文件,并执行程序:

$gcc-ofactorialmain.cfactorial.c

$./factorial5

Factorialof5is120.

GCC可同时用来编译C程序和C++程序。

一般来说,C编译器通过源文件的后缀名来判断是C程序还是C+

+程序。

在Linux中,C源文件的后缀名为.c,而C++源文件的后缀名为.C或.cpp。

但是,gcc命令只能编译C++源文件,而不能自动和C++程序使用的库连接。

因此,通常使用g++命令来完

完成C++程序的编译和连接,该程序会自动调用gcc实现编译。

假设我们有一个如下的C++源文件(hello.C):

#include

voidmain(void)

{

cout<<"Hello,world!

"<

}

则可以如下调用g++命令编译、连接并生成可执行文件:

$g++-ohellohello.C

$./hello

Hello,world!

gcc/egcs的主要选项

选项解释

-ansi只支持ANSI标准的C语法。

这一选项将禁止GNUC的某些特色,

例如asm或typeof关键词。

-c只编译并生成目标文件。

-DMACRO以字符串“1”定义MACRO宏。

-DMACRO=DEFN以字符串“DEFN”定义MACRO宏。

-E只运行C预编译器。

-g生成调试信息。

GNU调试器可利用该信息。

-IDIRECTORY指定额外的头文件搜索路径DIRECTORY。

-LDIRECTORY指定额外的函数库搜索路径DIRECTORY。

-lLIBRARY连接时搜索指定的函数库LIBRARY。

-m486针对486进行代码优化。

-oFILE生成指定的输出文件。

用在生成可执行文件时。

-O0不进行优化处理。

-O或-O1优化生成代码。

-O2进一步优化。

-O3比-O2更进一步优化,包括inline函数。

-shared生成共享目标文件。

通常用在建立共享库时。

-static禁止使用共享连接。

-UMACRO取消对MACRO宏的定义。

-w不生成任何警告信息。

-Wall生成所有警告信息。

SCOUNIX下面dbaxtra的调试技术

 

在scounix下编程大多离不开C语言,即使是数据库应用也有很多是与c搭配使用的,例如informixesql/c就可以在c语言中嵌入sql语句。

很多人认为在unix下写程序是件很痛苦的事情,其中一个很重要原因是不知道在unix下怎样调试程序。

其实在scounix源码调试器是dbxtra或dbXtra,linux下是gdb。

它们类似turboc的调试器,可以跟踪源码变量。

在unix下调试程序有如下传统方法

一、在要调试语句之前,输出要调试的变量,利用printf()函数。

二、写日志文件,把结果输出到文件中避免屏幕混乱,利用fprintf()函数。

三、利用sco内置调试器dbxtra或dbXtra。

dbxtra适用字符界面,在scounix的图形界面用dbXtra。

(编按:

请注意大小写)

以下是dbxtra基本命令:

ccont在断点后继续执行

ddelete删除所设断点

hhelp帮助

eedit编辑源程序

nnext源程序区的内容向下翻一屏。

pprint显示变量

qquit退出dbxtra

rrun运行程序,直到遇上设置的断点

rrrerun再次运行

sstep单步运行

ststop设置断点

jstatus显示当前断点

twhere显示当前状态,列出所有设置的变量值

didisplay开显示窗,用于查看变量

udundisplay删除显示窗的条目

fforward源程序区的内容向上翻一屏。

Bbackward源程序区的内容向下翻一屏。

Stopistopinst设置断点

traceitraceinst跟踪子程序

dbxtra[options][objectfile]

dbxtra在启动时有个参数-Idir值得一提.我们在编写一个较大程序的时候,通常源程序和编译生成的可执行文件都放在不同的目录中,这样便于管理。

默认dbxtra将在可执行文件所在的目录下找匹配c的源程序。

当我们启动时,指定-I参数,dbxtra就会到我们指定的目录下找匹配的c程序。

例如:

dbxtra-I"\work\c"program1

源程序在用cc编译时要带上-g参数,这样是加上符号表等调试信息。

只有这样编译过的文件,dbxtra才可以调试。

调试信息使源代码和机器码关联。

下面这个C程序输出结果和我们的预想结果不一样,说明某些地方有错误。

我们用调试器来调试它:

程序一:

t.c

main()

{inti=10,*p1;

floatj=1.5,*p2;

p1=&

p2=&

p2=p1;

printf("%d,%d\n",*p1,*p2);

}

首先带上-g参数编译cc-g-ott.c

启动调试器dbxtrat

屏幕显示:

1.main()

2.{inti=10,*p1;

3.floatj=1.5,*p2;

4.p1=&

5.p2=&

6.p2=p1;

7.printf("%d,%d\n",*p1,*p2);

8.}

C[browse]File:

t.cFunc.-

Readubgsymbolicinformation

Type'help'forhelp

(dbxtra)

(dbxtra)

设置断点:

(dbxtra)stopat5

运行:

(dbxtra)run

程序自动在第5行停下。

这时我们可以看变量的值。

(dbxtra)print*p1

单步执行。

(dbxtra)step

程序将执行第5行源码,指针将移到第6行。

(dbxtra)print*p2

(dbxtra)step

程序执行了第6行源码后,将指针移到第7行。

(dbxtra)print*p1,*p2

我们发现在执行了第6行源码后,*p1,*p2的值就不对了,所以问题就出在第6行上。

仔细检查后发现指针p1指向整型,指针p2指向实型。

它们之间的赋值要进行强制类型转换。

这种错误在C程序中是很常见的。

有时我们在调试一些程序时,要在整个程序运行中时刻监视莫些变量的值,例如程序一中我们要时刻了解*p1,*p2的值,除了在每一行程序执行完后,打print*p1,*p2外,还可以开一个显示窗口。

(dbxtra)display*p1,*p2

用undisplay删掉不想要的变量。

有些程序运行时要带参数,mycat/etc/passwd在调试时候

(dbxtra)run'/etc/passwd'

再运行时,无需再写一遍参数。

(dbxtra)rerun

在涉及到curses库编程或屏幕有大量的人机界面时,为了调试方便,我们可以把程序输出结果重定向到个虚屏。

(dbxtra)run>/dev/tty03

当然要先把tty03disable掉。

(disabletty03)

创建和使用静态库

详细的使用情况,请大家man手册,这里只介绍一下。

静态库相对的比较简单。

创建一个静态库是相当简单的。

通常使用ar程序把一些目标文件(.o)组合在一起,成为一个单独的库,然后运行ranlib,以给库加入一些索引信息。

创建和使用共享库

特殊的编译和连接选项

-D_REENTRANT使得预处理器符号_REENTRANT被定义,这个符号激活一些宏特性。

-fPIC选项产生位置独立的代码。

由于库是在运行的时候被调入,因此这个选项是必需的,因为在编译的时候,装入内存的地址还不知道。

如果不使用这个选项,库文件可能不会正确运行。

-shared选项告诉编译器产生共享库代码。

-Wl,-soname-Wl告诉编译器将后面的参数传递到连接器。

而-soname指定了共享库的soname。

#可以把库文件拷贝到/etc/ld.so.conf中列举出的任何目录中,并以

root身份运行ldconfig;或者#运行exportLD_LIBRARY_PATH='pwd',它把当前路径加到库搜索路径中去。

使用高级共享库特性

1.ldd工具

ldd用来显示执行文件需要哪些共享库,共享库装载管理器在哪里找到了需要的共享库.

2.soname

共享库的一个非常重要的,也是非常难的概念是soname——简写共享目标名(shortforsharedobjectname)。

这是一个为共享库(.so)文件而内嵌在控制数据中的名字。

如前面提到的,每一个程序都有一个需要使用的库的清单。

这个清单的内容是一系列库的soname,如同ldd显示的那样,共享库装载器必须找到这个清单。

soname的关键功能是它提供了兼容性的标准。

当要升级系统中的一个库时,并且新库的soname和老的库的soname一样,用旧库连接生成的程序,使用新的库依然能正常运行。

这个特性使得在Linux下,升级使用共享库的程序和定位错误变得十分容易。

在Linux中,应用程序通过使用soname,来指定所希望库的版本。

库作者也可以通过保留或者改变soname来声明,哪些版本是相互兼容的,这使得程序员摆脱了共享库版本冲突问题的困扰。

查看/usr/local/lib目录,分析MiniGUI的共享库文件之间的关系

3.共享库装载器

当程序被调用的时候,Linux共享库装载器(也被称为动态连接器)也自动被调用。

它的作用是保证程序所需要的所有适当版本的库都被调入内存。

共享库装载器名字是ld.so或者是ld-linux.so,这取决于Linuxlibc的版本,它必须使用一点外部交互,才能完成自己的工作。

然而它接受在环境变量和配置文件中的配置信息。

文件/etc/ld.so.conf定义了标准系统库的路径。

共享库装载器把它作为搜索路径。

为了改变这个设置,必须以root身份运行ldconfig工具。

这将更新/etc/ls.so.cache文件,这个文件其实是装载器内部使用的文件之一。

可以使用许多环境变量控制共享库装载器的操作(表1-4+)。

表1-4+共享库装载器环境变量

变量含义

LD_AOUT_LIBRARY_PATH除了不使用a.out二进制格式外,与LD_LIBRARY_PATH相同。

LD_AOUT_PRELOAD除了不使用a.out二进制格式外,与LD_PRELOAD相同。

LD_KEEPDIR只适用于a.out库;忽略由它们指定的目录。

LD_LIBRARY_PATH将其他目录加入库搜索路径。

它的内容应该是由冒号

分隔的目录列表,与可执行文件的PATH变量具有相同的格式。

如果调用设置用户ID或者进程ID的程序,该变量被忽略。

LD_NOWARN只适用于a.out库;当改变版本号是,发出警告信息。

LD_PRELOAD首先装入用户定义的库,使得它们有机会覆盖或者重新定义标准库。

使用空格分开多个入口。

对于设置用户ID或者进程ID的程序,

只有被标记过的库才被首先装入。

在/etc/ld.so.perload中指定

了全局版本号,该文件不遵守这个限制。

4.使用dlopen

另外一个强大的库函数是dlopen()。

该函数将打开一个新库,并把它装入内存。

该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。

比如ApacheWeb服务器利用这个函数在运行过程中加载模块,这为它提供了额外的能力。

一个配置文件控制了加载模块的过程。

这种机制使得在系统中添加或者删除一个模块时,都不需要重新编译了。

可以在自己的程序中使用dlopen()。

dlopen()在dlfcn.h中定义,并在dl库中实现。

它需要两个参数:

一个文件名和一个标志。

文件名可以是我们学习过的库中的soname。

标志指明是否立刻计算库的依赖性。

如果设置为RTLD_NOW的话,则立刻计算;如果设置的是RTLD_LAZY,则在需要的时候才计算。

另外,可以指定RTLD_GLOBAL,它使得那些在以后才加载的库可以获得其中的符号。

当库被装入后,可以把dlopen()返回的句柄作为给dlsym()的第一个参数,以获得符号在库中的地址。

使用这个地址,就可以获得库中特定函数的指针,并且调用装载库中的相应函数。

LINUX动态链接库的使用

一、编写合格的动态链接库头文件

C语言的头文件,可供一个或多个程序引用,里面一般定义程序所需的常量,自定义类型及函数原型说明等.其中的函数原型说明,则供编译器检查语法,用于排除引用参数时类型不一致的错误.只有编写合格的动态链接库头文件,程序员才能正确使用动态链接库内的函数.

动态链接库头文件要采用C语言标准格式,其中的动态函数原型定义,不必象上文介绍的那样用(*动态函数名)的描述形式.请看下面的例子每行开始的数字为所在行行号,为笔者添加,供注解使用)

1/*adatetime.h:

纵横软件制作中心雨亦奇(zhsoft@)编写,2002-03-06.*/

2

3#ifndef__DATETIME_H

4

5#define__DATETIME_H

6

7/*日期结构*/

8typedefstruct

9{

10intyear;

11intmon;

12intday;

13}DATETYPE;

14

15/*时间结构*/

16typedefstruct

17{

18charhour;

19charmin;

20charsec;

21}TIMETYPE;

22

23intgetdate(DATETYPE*d);/*取当前日期*/

24intgettime(TIMETYPE*t);/*取当前时间*/

25

26#endif

27

注:

与上文的datetime.h文件比较,从该头文件第23,24行可以看到,动态函数getdate,gettime的原型定义改变了,不再使用(*getdate),(*gettime)的格式了(这种格式使用较为罗嗦).

二、正确编译与命名动态链接库

为了让GCC编译器生成动态链接库,编译时须加选项-shared.(这点须牢记)

LINUX系统中,为了让动态链接库能被系统中其它程序共享,其名字应符合“lib*.so*”这种格式.如果某个动态链接库不符合此格式,则LINUX的动态链接库自动装入程序(ld.so)将搜索不到此链接库,其它程序也无法共享之.

格式中,第一个*通常表示为简写的库名,第二个*通常表示为该库的版本号.如:

在我的系统中,基本C动态链接库的名字为libc.so.6,线程pthread动态链接库的名字为libpthread.so.0等等.本文例子所生成的动态链接库的名字为libmy.so,虽没有版本号,但也符合所要求的格式.

生成该动态链接库的维护文件makefile-lib内容如下:

1#makefile:

纵横软件制作中心雨亦奇编写,2002-03-07.

2

3all:

libmy.so

4

5SRC=getdate.cgettime.c

6

7TGT=$(SRC:

.c=.o)

8

9$(SRC):

adatetime.h

10@touch$@

11

12%.o:

%.c

13cc-c$?

14

15#动态链接库(libmy.so)生成

16libmy.so:

$(TGT)

17cc-s-shared-o$@$(TGT)

18

运行命令:

$m

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 成人教育 > 专升本

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1