64位平台上,函数返回指针时遇到的段错误问题

Posted pengdonglin137

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了64位平台上,函数返回指针时遇到的段错误问题相关的知识,希望对你有一定的参考价值。

 

平台: x86_64

GCC:  7.3

类型 长度
int 4字节
int * 8字节

 

有如下两个文件:

b.c:

#include <stdio.h>

static int var = 0x5;

int *func(void)
{
        printf("var: %d, &var: %p
", var, &var);
        return &var;
}

a.c:

#include <stdio.h>

int main(int argc, const char *argv[])
{
        int *ret = NULL;

        ret = func();
        printf("*ret: %d
", *ret);

        return 0;
}

然后编译运行:

gcc a.c b.c

./a.out 
var: 5, &var: 0x55ac51c53010
Segmentation fault (core dumped)

可以看到,在访问返回的地址时发生了段错误,第一感觉不应该啊,b.c里定义的是static变量,并不是局部变量啊。那么我们把返回的指针具体数值打印出来,看跟b.c中打印的是否一致,修改a.c如下:

#include <stdio.h>

int main(int argc, const char *argv[])
{
        int *ret = NULL;

        ret = func();
        printf("ret: %p
", ret);

        return 0;
}

运行如下:

var: 5, &var: 0x56282a30b010
ret: 0x2a30b010

可以看到,二者果然不同,ret的值仅仅是&var的低4字节的内容。

经过Google,发现,原来是没有在a.c中对func()进行声明,如果没有对func声明,GCC会默认认为func返回的是int类型,而x86_64上,指针占用8个字节,int类型仅仅占用4个字节,所以在赋值时只会把&val的低4字节赋值给ret。

可以参考:

https://stackoverflow.com/questions/23144151/64-bit-function-returns-32-bit-pointer

https://stackoverflow.com/questions/14589314/segmentation-fault-while-accessing-the-return-address-from-a-c-function-in-64-bi

 

修改如下,在a.c中增加对func的声明,告诉编译器func的返回值是一个指针类型,需要占用8个字节长度

#include <stdio.h>

extern int *func(void);

int main(int argc, const char *argv[])
{
        int *ret = NULL;

        ret = func();
        printf("ret: %p, *ret: %d
", ret, *ret);

        return 0;
}

输出:

var: 5, &var: 0x561cb45bd010
ret: 0x561cb45bd010, *ret: 5

 

与此类似的还有一种:

.
├── a.c
├── b.c
├── common.h

common.h:

#ifndef __ABC_H__
#define __ABC_H__

struct ABC {
        int a;
        int b;
        int c;
};

#endif

b.c:

#include "common.h"

static struct ABC abc = {
        1, 2, 3
};

struct ABC *func(void)
{
        return &abc;
}

a.c:

#include <stdio.h>

extern struct ABC *func(void);

int main(int argc, const char *argv[])
{
        int *ret = NULL;

        struct ABC *abc = func();
        printf("abc: %p
", abc);

        return 0;
}

编译运行:

gcc a.c b.c

./a.out      
abc: 0x55ed86710010

没有问题,但是如果在a.c中去访问结构体成员的话,编译就会失败:

gcc a.c b.c      
a.c: In function ‘main’:
a.c:12:36: error: dereferencing pointer to incomplete type ‘struct ABC’
  printf("a: %d, b: %d, c:%d
", abc->a, abc->b, abc->c);

原因是,在x86_64上,不管是什么类型的指针,GCC都会会分配8个字节,这个不会有问题,但是如果要访问指针指向的结构体成员的话,就需要告诉GCC这个成员具体是什么样子。解决办法同时是需要声明一下结构体类型,这里包含对应的头文件即可。

#include <stdio.h>
#include "common.h"

extern struct ABC *func(void);

int main(int argc, const char *argv[])
{
        int *ret = NULL;

        struct ABC *abc = func();
        printf("abc: %p
", abc);

        printf("a: %d, b: %d, c:%d
", abc->a, abc->b, abc->c);

        return 0;
}

运行:

./a.out 
abc: 0x55f63c054010
a: 1, b: 2, c:3

 

 

以上是关于64位平台上,函数返回指针时遇到的段错误问题的主要内容,如果未能解决你的问题,请参考以下文章

未指定 lambda 函数的返回类型时的段错误

PHP x64 仍然返回错误的文件大小

GCC 生成的程序集 - C 函数调用时的段错误

读取 xml 时出现地址越界错误

C博客作业--指针

在运行非常旧的C代码时遇到分段错误