标签 GCC 下的文章

视警告为错误

每当你Build Project代码的时候,如果看到的是满屏的Warning,那么提醒你小心了,不妨看看《高效程序员的45个习惯》中对Warning的态度和处理方式。该书中的第34个习惯讲的是“警告就是错误”! 当然这个“习惯”所阐述的内容并不是这本书首创,在很多经典的传授编程之道的书中也都提到过。

将警告作为错误来处理,说起来容易,可作起来可并不那么简单。这不仅仅只是一个态度的问题,有时候还需要有技术手段或技巧去帮助你完成。《高效程序员的45个习惯》一书的作者也在该习惯所对应的“平衡的艺术”中提到:“如果确实没有应对之策的话,那就不要再浪费更多时间了。但类似这种情况很少发生”。另外他建议应该经常使用编译器的directive(指示器),将一些实在无法避免但又确定不是潜在缺陷的警告进行提示,告知编译器这个地方的警告可以不去理会。

将警告视为错误首先需要的就是勇气,大胆的在你的Makefile中将-Werror赋予给Gcc吧。Gcc则为你提供了一定的技术手段来帮你处理面对无法避免的警告时“左右为难”的情况。

Gcc为我们提供的技术手段就是Pragmas,虽然Gcc手册中建议我们一般情况下不要显式的使用Pragmas,但必要时还是需要这个工具的帮助的。

#pragma directives这个指示器嵌入在源码中,用于在源码编译时给GCC编译器提供一些指示信息。Pragmas有多种分类,这里我们需要的是Diagnostic Pragmas。简单的说,嵌入在源码中的Diagnostic Pragmas给我们提供了如下一些能力:
-> 将某类编译警告按编译错误处理 (如:#pragma GCC diagnostic error "-Wformat")
-> 将某类编译错误按编译警告处理 (如:#pragma GCC diagnostic warning "-Wformat")
-> 忽略某类编译警告 (如:#pragma GCC diagnostic ignored "-Wformat")

Gcc对Diagnostic Pragmas的支持是随着版本进化而逐渐增强的,在gcc 3.4.6版本下Diagnostic Pragmas是不被accepted的,Gcc只accept五类Pragmas;到了gcc 4.4.4版本,Gcc已经可以支持12类Pragmas,这其中就包括Diagnostic Pragmas

通过实际的测试也可以证实以上说明。

e.g.
/* testpragma.c , gcc -Wall testpragma.c */
#include

#pragma GCC diagnostic error "-Wformat"
int main() {
    printf("%d\n", "Diagnostic Pragmas Test");
    return 0;
}

在Sparc Solaris 10上使用gcc 3.4.6编译结果如下:
gcc -Wall testpragma.c
testpragma.c:3: warning: ignoring #pragma GCC diagnostic
testpragma.c: In function `main':
testpragma.c:5: warning: int format, pointer arg (arg 2)
编译器提示忽略了diagnostic pragma指示。

而在Ubuntu 10.04 Gcc 4.4.3版本下编译结果如下:
gcc -Wall testpragma.c
testpragma.c: In function ‘main’:
testpragma.c:5: error: format ‘%d’ expects type ‘int’, but argument 2 has type ‘char *’
编译器正确执行了我们给出的指示^_^。

#pragma directive的作用范围也很好理解:
首先肯定是在同一编译单元范围内有效,在A编译单元中设置的#pragma directive,在B编译单元是无效的。比如我另外编写一个utils.c,其内容如下:
#include

void foo() {
    printf("%d\n", "In another compile unit");
}
我们将utils.c与testpragma.c一起编译,gcc -Wall testpragma.c utils.c,得到的结果是:
testpragma.c: In function ‘main’:
testpragma.c:5: error: format ‘%d’ expects type ‘int’, but argument 2 has type ‘char *’
utils.c: In function ‘foo’:
utils.c:4: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘char *’
在testpragma.c这个编译单元,#pragma directive生效,但是在utils.c这个编译单元并不生效,依旧被诊断为Warning。

其次,在同一个编译单元中,#pragma directive影响的范围是其所在行之后的代码,直到下一次修改针对同样warning option的#pragma被放置。还是举例说明,我们改造一下testpragma.c:
/* testpragma.c */
#include

#pragma GCC diagnostic error "-Wformat"
void foo() {
    printf("%d\n", "Diagnostic Pragmas Scope Test");
}

int main() {
    printf("%d\n", "Diagnostic Pragmas Test");
    return 0;
}
编译命令执行后,Gcc给出的输出结果是:
testpragma.c: In function ‘foo’:
testpragma.c:5: error: format ‘%d’ expects type ‘int’, but argument 2 has type ‘char *’
testpragma.c: In function ‘main’:
testpragma.c:9: error: format ‘%d’ expects type ‘int’, but argument 2 has type ‘char *’
#pragma directive从头置尾一直发挥作用,两个format Warning都被当作Error报告了。

现在我们对main中的问题放松要求,将源码变为:
/* testpragma.c */
#include

#pragma GCC diagnostic error "-Wformat"
void foo() {
    printf("%d\n", "Diagnostic Pragmas Scope Test");
}

#pragma GCC diagnostic ignored "-Wformat"
int main() {
    printf("%d\n", "Diagnostic Pragmas Test");
    return 0;
}
执行编译命令后,Gcc的提示变为:
testpragma.c: In function ‘foo’:
testpragma.c:5: error: format ‘%d’ expects type ‘int’, but argument 2 has type ‘char *’
显然对-Wformat按照Error处理的作用范围仅限于foo这个函数,因为在foo之后我们修改了对-Wformat的指示!

有了编译器的支持,我们将更有信心去养成“视警告为错误”的习惯了。实际工作中,#pragma directive应用应该不多,因为多数情况下的警告都是可以通过正常的代码完善消除掉的。

也谈Configure脚本问题的解决

开了一个下午的技术交流会,回到办公室时离下班时间已经不远,天气预报说今晚有暴雪,外面阴沉的天气似乎也证实了这一点。这时一个同事遇到了一个软件包编译的问题,一时无法解决,向我求助。

这是一个libmemcached的编译问题,我们用的是libmemcached 0.34版本,我的同事在PC Solaris上执行libmemcached的configure脚本时遇到如下错,Configure脚本提示:

checking for pthread-config… no
configure: error: could not find libpthread

但经过确认系统中明明在/usr/lib下有pthread相关库的存在:
Tony Bai-[~/libmemcached-0.34]526:>ll /usr/lib|grep pthread
lrwxrwxrwx   1 root     root          26 2009   9月 10 llib-lpthread.ln -> ../../lib/llib-lpthread.ln
lrwxrwxrwx   1 root     root          23 2009   9月 10 llib-lpthread -> ../../lib/llib-lpthread
lrwxrwxrwx   1 root     root          25 2009   9月 10 libpthread.so.1 -> ../../lib/libpthread.so.1*
lrwxrwxrwx   1 root     root          25 2009   9月 10 libpthread.so -> ../../lib/libpthread.so.1*

又确认了一下用户的环境变量设置,LD_LIBRARY_PATH也包含了这些库的目录。

经验告诉我,这个错误是假象,向上翻Configure的输出结果,的确发现些奇怪的Check结果,如下:

checking for ANSI C header files… no
checking for sys/types.h… no
checking for sys/stat.h… no
checking for stdlib.h… no
checking for string.h… no
checking for memory.h… no
checking for strings.h… no
checking for inttypes.h… no
checking for stdint.h… no
checking for unistd.h… no

第一感觉,这怎么可能呢?这些标准C库头文件居然都Check失败了!在网上用“checking for ANSI C header files… no”搜了一下,也没有找到很好的答案。

我对Configure了解也不多,但是还是让我发现了config.log这根救命稻草。config.log这个文件详细地记录了Configure的每一步校验的执行内容和结果,其中对于标准C头文件的Check是这样做的:

configure:4827: checking for ANSI C header files
configure:4857: gcc -c -g -O2 -m64  conftest.c >&5
conftest.c:1: sorry, unimplemented: 64-bit mode not compiled in
configure:4864: $? = 1
configure: failed program was:
| /* confdefs.h.  */
| #define PACKAGE_NAME "libmemcached"
| #define PACKAGE_TARNAME "libmemcached"
| #define PACKAGE_VERSION "0.34"
| #define PACKAGE_STRING "libmemcached 0.34"
| #define PACKAGE_BUGREPORT "http://tangent.org/552/libmemcached.html"
| /* end confdefs.h.  */
| #include
| #include
| #include
| #include
|
| int
| main ()
| {
|
|   ;
|   return 0;
| }
configure:4995: result: no

再往下看,检测sys/types.h等标准库头文件的错误都是:
conftest.c:1: sorry, unimplemented: 64-bit mode not compiled in
configure:5047: $? = 1

看来并非是系统没有包含标准头文件,而是Configure采用了64-bit编译的方法去测试头文件存在的时候出错。随意创建一个testm64.c的源文件,输入:

/* testm64.c */
int main() {
    ;
    return 0;
}
用gcc -g -m64 testm64.c执行编译,得到与之前相同的错误结果:
testm64.c:1: sorry, unimplemented: 64-bit mode not compiled in

查看Gcc版本,发现是3.4.6,突然恍然大悟,这不是之前发现在Solaris 10 for x86上Gcc 64位编译的一个问题吗,在Solaris 10 for x86上如果要进行64位编译,要使用/usr/sfw/bin下的gcc 3.4.3版本,不能用3.4.6版本。

除了更换Gcc之外,如果你想编译32位版本的话,还可以这样来做:修改Configure脚本,打开Configure,将-m64字样全部删除。这样Configure后编译libmemcached就一切顺利了。

以上关于Configure脚本问题的解决方法,有一定的通用性,因此记之。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats