【发布时间】:2020-04-27 03:03:47
【问题描述】:
我有以下结构:
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;
...
结果是:
分段错误(核心转储)
使用 valgrind:
==26436== 使用大小为 8 的未初始化值
...
==26436==
==26436== 大小为 4 的无效写入
...
==26436== 地址 0x4c3a78000000000 未堆栈、malloc 或(最近)释放
我试图在一个较小的程序中重现这一点,但没有成功。
将结构更改为 uint64_t 而不是 uint32_t 可以解决问题。
在ann_init 内部打印指针ann->x 我得到0x55601051f080 和外部0x1051f08000000000。
为什么会这样?
编辑: 在其中一个包含的文件中找到了罪魁祸首:
#pragma pack(1)
仍然不确定为什么会导致问题。
如果我正在做指针运算来访问结构字段,这是有道理的,但我是按名称访问结构字段,为什么它会计算错误的值?
为什么init函数里面没问题,但是外面访问失败了?
【问题讨论】:
-
请显示重现问题所需的所有代码。请发布minimal reproducible example。很可能,问题出在其他地方。
-
在启用调试信息的情况下构建(在构建时添加
-g选项),Valgrind 应该能够为您提供在代码中检测到问题的文件名和行号。还可以使用调试器来捕捉运行中的崩溃,并在代码中定位它发生的位置,并检查该点所有相关变量的值。 -
我刚刚运行了你的代码,就像你拥有它一样,它运行没有问题。因此,我怀疑您未显示的某些内容可能导致了段错误,或者某处可能潜伏着未定义的行为。我同意 KamilCuk 的建议:编辑您的帖子以包含相关代码区域。
-
这闻起来像是两个程序部分的结构声明不同......要么您更改了结构定义,但没有重新编译一个源文件,而是另一个;或者您使用两个不同的结构定义,即在不同的头文件中。首先删除所有目标文件,然后重新编译
-
“仍然不确定为什么这会导致问题。” 很可能(但在没有看到您的代码的情况下不确定)一个文件在结构定义(或从头文件中包含)之前具有“#pragma”,而一个文件没有。因此,两个文件都认为他们正在查看相同的结构,但一个需要填充,一个不需要。因此,当您取消对指针的引用时,它们位于错误的位置,一切都变得松散了。实际上,您可以在 printf 示例中看到这一点:一个地址的底部与另一个地址的顶部匹配;这不太可能是偶然的。