天天看点

一份简单的在 Linux下编译及调试 C 代码的指南一份简单的在 Linux下编译及调试 C 代码的指南

一份简单的在 Linux下编译及调试 C 代码的指南一份简单的在 Linux下编译及调试 C 代码的指南

对于linux下的c程序员来说,几乎天天都会和linux打交道。但在很多人的眼中,linux是一个易用性极差、靠命令驱动的操作系统,根本无法与有着友好用户界面的windows相比。确实是这样的,即使大家的程序是运行在linux下,基于以下种种原因,我们的大部分工作还是在windows下完成的:

第一,除了编译调试代码之外,每个程序员还有很多工作要做,像文档编写、邮件发送及回复、ppt制作等,这些工作在windows下做要更方便快捷一些。 第二,公司及项目组的资源有限,一般不会为每个开发人员配备一台安装有linux的机器,而是大家共用一台或少许几台linux机器。在每台机器上建立多个用户,需要用来编译或调试程序的时候,大家用某个用户登录上去。

这样,问题就来了:自己平时是在windows下面办公的,而自己编写的程序的运行环境又是linux的,如何从windows切换到linux呢?是不是要到专门的linux机器上去编写代码呢?我们如何在linux下调试程序呢?本文将一一道来。

<a></a>

只要在windows下安装一个叫做securecrt的软件和一个叫做filezilla的软件,便可轻松实现windows到linux的切换。

在使用securecrt和filezilla之前,要确保有一台安装了linux的机器处于运行状态(一般说来,每个开发小组都会有专门用于测试程序的机器,可以在此机器上安装linux)。作者使用的linux机器的ip地址为xx.xx.xx.xx,用户名为zxin10,密码为yyyy。

打开securecrt软件,在界面上输入ip和用户名,如图1所示。

一份简单的在 Linux下编译及调试 C 代码的指南一份简单的在 Linux下编译及调试 C 代码的指南

图1 登录界面

然后,单击图1中的“connect”,在出现的界面上输入密码,如图2所示。

一份简单的在 Linux下编译及调试 C 代码的指南一份简单的在 Linux下编译及调试 C 代码的指南

图2 密码输入界面

密码输入正确之后,便登录到了linux系统下,如图3所示。

一份简单的在 Linux下编译及调试 C 代码的指南一份简单的在 Linux下编译及调试 C 代码的指南

图3 登录成功之后的界面

为了编译自己的程序,我们需要建立自己的文件存放目录,如图4所示。

一份简单的在 Linux下编译及调试 C 代码的指南一份简单的在 Linux下编译及调试 C 代码的指南

图4 新建个人目录

目录建立成功之后,我们便可以转到目录中去看一下,如图5所示。

一份简单的在 Linux下编译及调试 C 代码的指南一份简单的在 Linux下编译及调试 C 代码的指南

图5 转到新建目录

此时,“万事俱备,只欠东风”,我们接下来要做的工作是利用filezilla软件将自己在windows下编写的程序传上去。

示例程序如下:

<code>/**********************************************************************</code>

<code>* 版权所有 (c)2015, zhou zhaoxiong。</code>

<code>*</code>

<code>* 文件名称:hello.c</code>

<code>* 文件标识:无</code>

<code>* 内容摘要:演示windows下编写的程序如何在linux下执行</code>

<code>* 其它说明:无</code>

<code>* 当前版本:v1.0</code>

<code>* 作 者:zhou zhaoxiong</code>

<code>* 完成日期:201501028</code>

<code>**********************************************************************/</code>

<code>#include &lt;stdio.h&gt;</code>

<code></code>

<code>* 功能描述:主函数</code>

<code>* 输入参数:无</code>

<code>* 输出参数:无</code>

<code>* 返 回 值:0-执行完毕</code>

<code>* 修改日期 版本号 修改人 修改内容</code>

<code>* -------------------------------------------------------------------</code>

<code>* 201501028 v1.0 zhou zhaoxiong 创建</code>

<code>***********************************************************************/</code>

<code>int main()</code>

<code>{</code>

<code>printf("hello, world!\n");</code>

<code>return 0;</code>

<code>}</code>

将该“hello.c”文件存放在d盘的“test”文件夹下,并启动filezilla,如图6所示。

一份简单的在 Linux下编译及调试 C 代码的指南一份简单的在 Linux下编译及调试 C 代码的指南

图6 启动filezilla之后的界面

在“主机(h)”中输入ip地址,在“用户名(u)”中输入“zxin10”用户名,在“密码(w)”中输入正确的密码,“端口(p)”可不填写而使用默认值,则可登录到linux机器上去。登上去后,转到“zhouzx”目录下,并将“hello.c”文件传上去,如图7所示。

一份简单的在 Linux下编译及调试 C 代码的指南一份简单的在 Linux下编译及调试 C 代码的指南

图7 上传文件之后的界面

此时,“hello.c”文件已经传到了“zhouzx”目录下,现在可以对该文件进行编译了。

使用“gcc -g -o hello hello.c”命令对文件进行编译,如图8所示。

一份简单的在 Linux下编译及调试 C 代码的指南一份简单的在 Linux下编译及调试 C 代码的指南

图8 编译之后的结果

可以看到,编译成功之后,有“hello”文件生成。紧接着,运行“hello”命令,便可看到程序的输出结果,如图9所示。

一份简单的在 Linux下编译及调试 C 代码的指南一份简单的在 Linux下编译及调试 C 代码的指南

图9 程序的输出结果

以上便是将windows下的程序放到linux下去编译和运行的全过程。这里只是示例了简单的程序,实际软件开发项目中的程序要复杂很多,但基本操作流程都是类似的。当然,直接在linux下编写程序也是可以的,如可以利用vi编辑器来写程序。但由于易用性的原因,我认为,在windows下编写程序要更方便一点。大家要根据自己的习惯及项目组的要求来选择合理的代码编写的方式。

在实际的软件开发项目中,程序出现问题是在所难免的。遥想本人参加工作之后首次遇到程序的情景,至今还历历在目。之前的经验告诉我,我们越是惊慌失措,问题就越是解决不了。我们要先让自己平静下来,然后再寻找解决程序问题的办法。 

这里以一个实际的程序为例,以用gdb分析core文件为例介绍了linux下程序调试的方法,同时演示了常见gdb命令的操作方法。

在linux下执行“ulimit –a”命令查看程序运行出错时是否会产生core文件,命令执行的结果中有“core file size = 0”表示不会产生core文件,此时要使用“ulimit -c 1000000”命令设置core文件的大小。

示例程序

<code>* 文件名称:gdbdebug.c</code>

<code>* 内容摘要:gdb命令演示程序</code>

<code>* 完成日期:20151008</code>

<code>#include &lt;stdlib.h&gt;</code>

<code>#include &lt;string.h&gt;</code>

<code>// 数据类型重定义</code>

<code>typedef unsigned char uint8;</code>

<code>typedef signed int int32;</code>

<code>typedef unsigned int uint32;</code>

<code>// 函数声明</code>

<code>void sleep(uint32 icountms);</code>

<code>void printinfo(void);</code>

<code>int32 main();</code>

<code>* 返 回 值:无</code>

<code>* 修改日期 版本号 修改人 修改内容</code>

<code>* 20151008 v1.0 zhou zhaoxiong 创建</code>

<code>int32 main()</code>

<code>printinfo(); // 在屏幕上输出消息</code>

<code>* 功能描述: 在屏幕上输出消息</code>

<code>* 输入参数: 无</code>

<code>* 输出参数: 无</code>

<code>* 返 回 值: 无</code>

<code>* 其它说明: 无</code>

<code>* 修改日期 版本号 修改人 修改内容</code>

<code>* ----------------------------------------------------------------------</code>

<code>* 20151008 v1.0 zhou zhaoxiong 创建</code>

<code>************************************************************************/</code>

<code>void printinfo(void)</code>

<code>uint32 iloopflag = 0;</code>

<code>uint32 isum = 0;</code>

<code>uint32 ilen = 0;</code>

<code>uint8 *pctrstr = null;</code>

<code>ilen = strlen(pctrstr);</code>

<code>for (iloopflag = 0; iloopflag &lt; ilen; iloopflag ++) // 打印消息ilen次</code>

<code>printf("printinfo: hello, world!\n");</code>

<code>isum = isum + iloopflag;</code>

<code>sleep(10 * 1000); // 每10s打印一次</code>

<code>return;</code>

<code>* 功能描述: 程序休眠</code>

<code>* 输入参数: icountms-休眠时间(单位:ms)</code>

<code>* 修改日期 版本号 修改人 修改内容</code>

<code>* ------------------------------------------------------------------</code>

<code>* 20151008 v1.0 zhou zhaoxiong 创建</code>

<code>********************************************************************/</code>

<code>void sleep(uint32 icountms)</code>

<code>struct timeval t_timeout = {0};</code>

<code>if (icountms &lt; 1000)</code>

<code>t_timeout.tv_sec = 0;</code>

<code>t_timeout.tv_usec = icountms * 1000;</code>

<code>else</code>

<code>t_timeout.tv_sec = icountms / 1000;</code>

<code>t_timeout.tv_usec = (icountms % 1000) * 1000;</code>

<code>select(0, null, null, null, &amp;t_timeout); // 调用select函数阻塞程序</code>

在linux上用“gcc -g -o gdbdebug gdbdebug.c”命令对程序进行编译之后,运行“gdbdebug”命令,发现在当前目录下出现了core文件。利用gdb命令对core文件进行分析的过程如下所示:

<code>~/zhouzhaoxiong/zzx/gdbdebug&gt; gdb gdbdebug core -- 启动gdb对core文件的分析</code>

<code>gnu gdb (gdb) suse (7.3-0.6.1)</code>

<code>copyright (c) 2011 free software foundation, inc.</code>

<code>license gplv3+: gnu gpl version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt;</code>

<code>this is free software: you are free to change and redistribute it.</code>

<code>there is no warranty, to the extent permitted by law. type "show copying"</code>

<code>and "show warranty" for details.</code>

<code>this gdb was configured as "x86_64-suse-linux".</code>

<code>for bug reporting instructions, please see:</code>

<code>&lt;http://www.gnu.org/software/gdb/bugs/&gt;...</code>

<code>reading symbols from /home/zhou/zhouzhaoxiong/zzx/gdbdebug/gdbdebug...done.</code>

<code>core was generated by `gdbdebug'.</code>

<code>program terminated with signal 11, segmentation fault.</code>

<code>#0 0x00007f4a736f9812 in __strlen_sse2 () from /lib64/libc.so.6</code>

<code>(gdb) where -- 查看程序出问题的地方</code>

<code>#1 0x000000000040061a in printinfo () at gdbdebug.c:64 -- 可以看到,在gdbdebug.c文件的第64行出的问题</code>

<code>#2 0x00000000004005e5 in main () at gdbdebug.c:41</code>

<code>(gdb) b 41 -- 在gdbdebug.c文件第41行设立断点</code>

<code>breakpoint 1 at 0x4005e0: file gdbdebug.c, line 41.</code>

<code>(gdb) b 64 -- 在gdbdebug.c文件第64行设立断点</code>

<code>breakpoint 2 at 0x400611: file gdbdebug.c, line 64.</code>

<code>(gdb) info b -- 显示断点信息</code>

<code>num type disp enb address what</code>

<code>1 breakpoint keep y 0x00000000004005e0 in main at gdbdebug.c:41</code>

<code>2 breakpoint keep y 0x0000000000400611 in printinfo at gdbdebug.c:64</code>

<code>(gdb) r -- 运行gdbdebug</code>

<code>starting program: /home/zhou/zhouzhaoxiong/zzx/gdbdebug/gdbdebug</code>

<code>breakpoint 1, main () at gdbdebug.c:41</code>

<code>41 printinfo(); // 在屏幕上输出消息</code>

<code>(gdb) n -- 执行下一步</code>

<code>breakpoint 2, printinfo () at gdbdebug.c:64</code>

<code>64 ilen = strlen(pctrstr);</code>

<code>(gdb) p ilen -- 打印(输出)ilen的值</code>

<code>$1 = 0</code>

<code>(gdb) p iloopflag -- 打印(输出)iloopflag的值</code>

<code>$2 = 0</code>

<code>(gdb) c -- 继续执行</code>

<code>continuing.</code>

<code>program received signal sigsegv, segmentation fault. -- 程序core掉了</code>

<code>0x00007ffff7ae9812 in __strlen_sse2 () from /lib64/libc.so.6</code>

<code>(gdb) q -- 退出gdb</code>

<code>a debugging session is active.</code>

<code>inferior 1 [process 26640] will be killed.</code>

<code>quit anyway? (y or n) y</code>

<code>~/zhouzhaoxiong/zzx/gdbdebug&gt;</code>

从以上分析可知,执行gdbdebug.c文件的第64行时程序core掉了。此时仔细分析程序,发现pctrstr指针为空。当对一个不存在的指针取长度时,由于找不到地址,程序便崩溃了。修改的办法也非常的简单,只需要让pctrstr指针指向具体的地址即可。

修改之后的代码如下:

<code>* 20151008 v1.0 zhou zhaoxiong 创建</code>

<code>* 20151008 v1.0 zhou zhaoxiong 创建</code>

<code>uint8 *pctrstr = "hello, world!"; // 修改了这行代码</code>

编译并运行之后,程序正常,说明问题已被我们解决掉。下面是常见的gdb命令的操作示例:

<code>~/zhouzhaoxiong/zzx/gdbdebug&gt; gdb gdbdebug -- 启动gdb调试</code>

<code>(gdb) b 64 -- 在gdbdebug.c文件第64行设立断点</code>

<code>breakpoint 1 at 0x400611: file gdbdebug.c, line 64.</code>

<code>(gdb) b 72 -- 在gdbdebug.c文件第72行设立断点</code>

<code>breakpoint 2 at 0x400637: file gdbdebug.c, line 72.</code>

<code>(gdb) info b -- 显示断点信息</code>

<code>1 breakpoint keep y 0x0000000000400611 in printinfo at gdbdebug.c:64</code>

<code>2 breakpoint keep y 0x0000000000400637 in printinfo at gdbdebug.c:72</code>

<code>(gdb) r -- 运行gdbdebug</code>

<code>breakpoint 1, printinfo () at gdbdebug.c:64</code>

<code>(gdb) p ilen -- 打印(输出)ilen的值</code>

<code>(gdb) n -- 执行下一步</code>

<code>66 for (iloopflag = 0; iloopflag &lt; ilen; iloopflag ++) // 打印消息ilen次</code>

<code>68 printf("printinfo: hello, world!\n");</code>

<code>$3 = 13</code>

<code>printinfo: hello, world! -- 程序的输出结果</code>

<code>70 isum = isum + iloopflag;</code>

<code>(gdb) p isum -- 打印(输出)isum的值</code>

<code>$4 = 0</code>

<code>(gdb) n -- 执行下一步</code>

<code>breakpoint 2, printinfo () at gdbdebug.c:72</code>

<code>72 sleep(10 * 1000); // 每10s打印一次</code>

<code>(gdb) n</code>

<code>(gdb) p iloopflag</code>

<code>$5 = 0</code>

<code>$6 = 1</code>

<code>printinfo: hello, world!</code>

<code>(gdb) p isum</code>

<code>$7 = 0</code>

<code>$8 = 1</code>

<code>(gdb) finish -- 一直运行到函数返回</code>

<code>run till exit from #0 printinfo () at gdbdebug.c:72</code>

<code>(gdb) c -- 继续执行</code>

<code>(gdb) bt -- 打印当前的函数调用栈的所有信息</code>

<code>#0 printinfo () at gdbdebug.c:72</code>

<code>#1 0x00000000004005e5 in main () at gdbdebug.c:41</code>

<code>(gdb) q -- 退出gdb</code>

<code>inferior 1 [process 26685] will be killed.</code>

作为linux下调试c/c++程序的工具,大家一定要熟练掌握gdb的用法。

linux具有免费、可靠、安全、稳定、多平台等特点,因此深受全球各大it厂商的追捧。linux操作系统的两大主要应用领域是服务器领域和嵌入式linux系统。不管你从事的开发工作是否与linux有关,掌握linux下的软件开发方法总是有好处的。

本文来自云栖社区合作伙伴“linux中国”,原文发表于2013-04-02.

继续阅读