分段错误,大小为 4 的无效写入
Segmentation fault, Invalid write of size 4
我有以下结构:
typedef struct ann_t {
uint32_t xs;
uint32_t hs;
uint32_t ys;
float *x;
float *h;
float *y;
float **wxh;
float **why;
} ann_t;
初始化方式如下:
ann_t* ann_init(uint32_t xs, uint32_t hs, uint32_t ys) {
ann_t *ann = malloc(sizeof(ann_t));
ann->xs = xs;
ann->hs = hs;
ann->ys = ys;
ann->x = calloc(xs, sizeof(float));
ann->h = calloc(hs, sizeof(float));
ann->y = calloc(ys, sizeof(float));
ann->wxh = calloc(xs, sizeof(float*));
ann->why = calloc(hs+1, sizeof(float*));
int i, j;
for(i = 0; i < xs; i++) {
ann->wxh[i] = malloc(hs * sizeof(float));
}
for(i = 0; i < hs+1; i++) {
ann->why[i] = malloc(ys * sizeof(float));
}
// printf("%p\n", ann->x);
return ann;
}
在另一个程序中包含此代码:
...
ann_t *ann = ann_init(25, 10, 4);
// printf("%p\n", ann->x);
ann->x[0] = 1.0;
...
结果是:
Segmentation fault (core dumped)
使用 valgrind:
==26436== Use of uninitialised value of size 8
...
==26436==
==26436== Invalid write of size 4
...
==26436== Address 0x4c3a78000000000 is not stack'd, malloc'd or (recently) free'd
我试图在一个较小的程序中重现它,但没有成功。
将结构更改为 uint64_t
而不是 uint32_t
可以解决问题。
在 ann_init
内部打印指针 ann->x
我得到 0x55601051f080 和外部 0x1051f08000000000.
为什么会这样?
编辑:
在其中一个包含的文件中找到了罪魁祸首:
#pragma pack(1)
仍然不确定为什么会导致这个问题。
如果我正在做指针运算来访问结构字段,这是有道理的,但我是按名称访问结构字段,那么为什么它会计算出错误的值?
为什么在init函数里面没问题,在外面却访问失败?
根据对问题的评论得出的答案...
假设您在 header:
中定义了一个更简单的结构
// header.h
typedef struct {
char foo;
int *ptr;
} fish_t;
和两个源文件:
// src1.c
#include "header.h"
int dummy_int = 5;
fish_t my_fish;
my_fish.foo = 'a';
my_fish.ptr = &dummy_int;
use_fish_fn( &my_fish );
// src2.c
#pragma pack(1)
#include "header.h"
void use_fish_fn( fish_t *f )
{
int bar = *f->ptr;
}
第一个文件(没有打包)可能 fish_t
的内存布局如下:
0: | foo | pad | pad | pad | // one byte char, three bytes padding
4: | ptr | // four byte pointer
但是第二个文件(带包装)看起来像:
0: | foo | ptr ... | // one byte char, 3/4 of pointer
4: |...ptr | // last part of pointer
因此,当第二个文件尝试读取(并随后取消引用)指针时,它实际上正在读取填充中发生的任何内容,这肯定会出错。
我有以下结构:
typedef struct ann_t {
uint32_t xs;
uint32_t hs;
uint32_t ys;
float *x;
float *h;
float *y;
float **wxh;
float **why;
} ann_t;
初始化方式如下:
ann_t* ann_init(uint32_t xs, uint32_t hs, uint32_t ys) {
ann_t *ann = malloc(sizeof(ann_t));
ann->xs = xs;
ann->hs = hs;
ann->ys = ys;
ann->x = calloc(xs, sizeof(float));
ann->h = calloc(hs, sizeof(float));
ann->y = calloc(ys, sizeof(float));
ann->wxh = calloc(xs, sizeof(float*));
ann->why = calloc(hs+1, sizeof(float*));
int i, j;
for(i = 0; i < xs; i++) {
ann->wxh[i] = malloc(hs * sizeof(float));
}
for(i = 0; i < hs+1; i++) {
ann->why[i] = malloc(ys * sizeof(float));
}
// printf("%p\n", ann->x);
return ann;
}
在另一个程序中包含此代码:
...
ann_t *ann = ann_init(25, 10, 4);
// printf("%p\n", ann->x);
ann->x[0] = 1.0;
...
结果是:
Segmentation fault (core dumped)
使用 valgrind:
==26436== Use of uninitialised value of size 8
...
==26436==
==26436== Invalid write of size 4
...
==26436== Address 0x4c3a78000000000 is not stack'd, malloc'd or (recently) free'd
我试图在一个较小的程序中重现它,但没有成功。
将结构更改为 uint64_t
而不是 uint32_t
可以解决问题。
在 ann_init
内部打印指针 ann->x
我得到 0x55601051f080 和外部 0x1051f08000000000.
为什么会这样?
编辑: 在其中一个包含的文件中找到了罪魁祸首:
#pragma pack(1)
仍然不确定为什么会导致这个问题。
如果我正在做指针运算来访问结构字段,这是有道理的,但我是按名称访问结构字段,那么为什么它会计算出错误的值?
为什么在init函数里面没问题,在外面却访问失败?
根据对问题的评论得出的答案...
假设您在 header:
中定义了一个更简单的结构// header.h
typedef struct {
char foo;
int *ptr;
} fish_t;
和两个源文件:
// src1.c
#include "header.h"
int dummy_int = 5;
fish_t my_fish;
my_fish.foo = 'a';
my_fish.ptr = &dummy_int;
use_fish_fn( &my_fish );
// src2.c
#pragma pack(1)
#include "header.h"
void use_fish_fn( fish_t *f )
{
int bar = *f->ptr;
}
第一个文件(没有打包)可能 fish_t
的内存布局如下:
0: | foo | pad | pad | pad | // one byte char, three bytes padding
4: | ptr | // four byte pointer
但是第二个文件(带包装)看起来像:
0: | foo | ptr ... | // one byte char, 3/4 of pointer
4: |...ptr | // last part of pointer
因此,当第二个文件尝试读取(并随后取消引用)指针时,它实际上正在读取填充中发生的任何内容,这肯定会出错。