c - memcpy 导致奇怪的分段错误
问题描述
尽管我的 C 程序看起来很完美,但我遇到了分段错误!下面是它的工作原理:
假设我们有一个包含 6 个 int 和 1 个 int* 的 struct Heap。(大小为 32 字节)
struct Heap
{
int* a;
int b;
int c;
int d;
int e;
bool f; // Trust me it's int
bool g; // Trust me it's int
};
在main()我调用:
examine(heap, enable_cloning);
检查()所做的是:
...
if(!enable_cloning)
{
Heap* my_clone = new_Heap_from_clone(heap); // A clone is being made ...
...
现在让我们跳到棘手的部分(在new_Heap_from_clone()内部)
Heap* new_Heap_from_clone(Heap const* the_original_or_clone_heap)
{
Heap* heap = malloc(sizeof(Heap));
if(heap == NULL)
return NULL;
memcpy(heap, the_original_or_clone_heap, sizeof(Heap)); //Happy Segmentation Fault !!!
...
运行 GDB 我得到:
(gdb) step
174 memcpy(heap, the_original_or_clone_heap, sizeof(the_original_or_clone_heap));
(gdb) step
Program received signal SIGSEGV, Segmentation fault.
0x0000555555554dae in new_Heap_from_clone (the_original_or_clone_heap=0xb) at ./Heap.c:174
174 memcpy(heap, the_original_or_clone_heap, sizeof(the_original_or_clone_heap));
(gdb) sizeof *heap
Undefined command: "sizeof". Try "help".
(gdb) print sizeof *heap
$5 = 32
(gdb) print sizeof *the_original_or_clone_heap
$6 = 32
注意: 我包括了memcpy()获得的大小,以便我从您那里获得更多宝贵的信息! 让我们看看 valgrind 对此有何评论:
==6244== Use of uninitialised value of size 8
==6244== at 0x108DAE: new_Heap_from_clone (Heap.c:174)
==6244== by 0x1095ED: heapSort (Heap.c:481)
==6244== by 0x10915F: sort (Heap.c:330)
==6244== by 0x10A897: ds_bench (main.c:90)
==6244== by 0x10A9B4: main (main.c:106)
==6244==
==6244== Invalid read of size 8
==6244== at 0x108DAE: new_Heap_from_clone (Heap.c:174)
==6244== by 0x1095ED: heapSort (Heap.c:481)
==6244== by 0x10915F: sort (Heap.c:330)
==6244== by 0x10A897: ds_bench (main.c:90)
==6244== by 0x10A9B4: main (main.c:106)
==6244== Address 0xb is not stack'd, malloc'd or (recently) free'd
==6244==
==6244==
==6244== Process terminating with default action of signal 11 (SIGSEGV)
==6244== Access not within mapped region at address 0xB
==6244== at 0x108DAE: new_Heap_from_clone (Heap.c:174)
==6244== by 0x1095ED: heapSort (Heap.c:481)
==6244== by 0x10915F: sort (Heap.c:330)
==6244== by 0x10A897: ds_bench (main.c:90)
==6244== by 0x10A9B4: main (main.c:106)
==6244== If you believe this happened as a result of a stack
==6244== overflow in your program's main thread (unlikely but
==6244== possible), you can try to increase the size of the
==6244== main thread stack using the --main-stacksize= flag.
==6244== The main thread stack size used in this run was 12001280.
==6244==
==6244== HEAP SUMMARY:
==6244== in use at exit: 1,648 bytes in 31 blocks
==6244== total heap usage: 32 allocs, 1 frees, 2,672 bytes allocated
==6244==
==6244== LEAK SUMMARY:
==6244== definitely lost: 0 bytes in 0 blocks
==6244== indirectly lost: 0 bytes in 0 blocks
==6244== possibly lost: 0 bytes in 0 blocks
==6244== still reachable: 1,648 bytes in 31 blocks
==6244== suppressed: 0 bytes in 0 blocks
==6244== Reachable blocks (those to which a pointer was found) are not shown.
==6244== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==6244==
==6244== For counts of detected and suppressed errors, rerun with: -v
==6244== Use --track-origins=yes to see where uninitialised values come from
==6244== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
请记住,我以前没有使用 valgrind 的经验,但它说的很明显:
==6244== 访问不在地址 0xB 的映射区域内
但我不认为存在这样的问题......是否有可能(以某种方式)运行堆栈空间不足?但即使在这种情况下,运行 valgrind :
valgrind --leak-check=yes
--main-stacksize=12000000
./hxn 超过其默认值(8388608)并得到与您看到的相同的答案。
那么地球上的重点是什么?
解决方案
您将未初始化的指针传递给new_Heap_from_clone()
. gdb 和 valgrind 都试图告诉你:
- gdb 告诉你指针 is
0xb
,这是 gdb 中未初始化指针的经典值;和 - valgrind 在说 . 时再明显不过了
Use of uninitialised value of size 8
。
这通过了检查,因为0xb
它不为空;0x0
是。所以,当你打电话时,new_Heap_from_clone(heap)
你一定没有用你应该有的值初始化那个特定heap
的值。null
使用 gdb 的回溯能力(up
在调用堆栈中向上,down
向下)来找出出错的确切位置。
推荐阅读
- java - com.parse.ParseObject 不能强制转换为
- javascript - 用“role=button”模拟一个 div 元素的点击——JavaScript
- java - Android Google 登录失败 com.google.android.gms.common.api.ApiException: 12500
- javascript - 在tomcat的war文件中编辑js文件
- java - 在复制构造函数中使用直接字段访问而不是 getter 会导致空指针异常
- php - 在 PHP 中打印 mysql 表
- jenkins - 安装 Github 集成插件后 Jenkins 崩溃
- sharepoint-online - 如何在 Sharepoint Online 中设置“社区站点”导航
- arduino - 使用 Arduino IDE 将 Arduino 代码拆分为多个文件
- css - 这种情况下如何处理absolute、overflow和zindex