当前位置: 代码迷 >> Ruby/Rails >> 一个大意引发的思考!(strerror)
  详细解决方案

一个大意引发的思考!(strerror)

热度:327   发布时间:2016-04-29 02:09:59.0
一个疏忽引发的思考!(strerror)

  前几天写代码因为自己的疏忽导致一遍又一遍的Segmentation fault (core dumped)。该问题是因为strerror(errno)返回的指针指向非法的内存导致程序打印错误时崩溃。

  尝试数次无果,为了进度。简单粗暴的换成了perror(str)。今天忙里偷闲,定位到了问题做个记录和分享。

  所有的坑,都是自己挖的。开始正题。

  

  1 #include<stdio.h>  2 #include<errno.h>  3 //#include<string.h>  4 int main()  5 {  6     char *perr = NULL;  7     errno = 14;  8     perr = strerror(errno);  9     puts(perr); 10     return 0; 11 } 

  先看代码,上述我屏蔽了#include<string.h>。如我所料,编译通过(其实有warn已经告诉你有问题了),运行崩溃了。warn如下:

  

  然后加上#include<string.h>程序完美编译,完美成功执行。为什么呢?经过与人(CSDN某某某)讨论和浏览stackoverflow上的帖子,以及我最终使用gcc -E预编译  gcc -Wall的验证。

  

  我得到以下结论。

  1.strerror()函数声明在string.h头文件里(我以前以为是在errno.h里的,还是基本功不行啊)。

  2.gcc编译时如果发现未定义的函数,它会认为该函数是定义在其他源文件中的,所以编译是通过的。

  但是因为编译器看不见函数原型,所以它认为函数返回值为int。在链接阶段,如果找到该函数则通过,找不到则报错。
    这里我做了个实验,如果函数原型在.o文件里链接后没有问题,连接器会修正返回值类型。但是如果链接的是.so文件,则返回值就是int。我也想不明白。

  

  揭晓答案吧!我的程序为什么会报错呢?这是一系列疏忽大意加基本功不扎实的恶果。

  因为我没有包string.h。所以编译器看不见函数原型,默认函数返回值为int。并给出了wara:assignment makes pointer from integer without a cast(用一个int型给指针赋值而没有转换--英文不好)。显而易见,在64位系统中char*是64位的,int是32位。所以相当于我用32位的整型值当作地址赋值给了指针。所以指针指向非法内存。接着Segmentation fault (core dumped)就登场了。

  系统中类型于strerror()这种返回指针的函数都可能有类似问题,所以头文件该包的还是要包上。该注意的warn还是不容忽视啊。

  文章到此已经结束,给个附录。64位系统 gcc编译器 各个数据类型长度表。

  

  
  以上阐述由非权威人士撰写。如有大牛深谙其理,欢迎跟帖深入说明。

2楼封剑隐居
这个问题还真没注意过。不过楼主还是没讲明白,为什么把strerror返回的int赋给char*,就会段错误啊。
1楼封剑隐居
哦刚才百度了下,好像明白了。本来strerror是要返回64位的char*的,但是由于没包含string.h,编译器提供了返回int的隐式声明(c89规定),64位的char*被截断到32位了。
Re: 不爱洗脸
@封剑隐居,是这样的。可能我还是没说清楚。见谅