SIGSEGV,(貌似)由printf引起
首先,对任何交叉发布道歉。 希望我不是在这里重复一个问题,但我无法在其他地方找到这个问题(通过Google和Stack Overflow)。
这是错误的要点。 如果我在代码中的任何地方调用printf
, sprintf
或fprintf
来显示浮点数,我会收到SIGSEGV (EXC_BAD_ACCESS)
错误。 让我举个例子。
以下引发错误:
float f = 0.5f; printf("%f\n",f);
此代码不:
float f = 0.5f; printf("%d\n",f);
我意识到那里有隐含的转换,但我并不关心这一点。 我只是无法理解为什么打印浮点数而不是打印整数会产生错误。
注意:部分代码使用malloc
来创建一些非常大的多维数组。 但是,这些打印语句不会以任何方式引用这些数组。 这是我如何声明这些数组的示例。
#define X_LEN 20 #define XDOT_LEN 20 #define THETA_LEN 20 #define THETADOT_LEN 20 #define NUM_STATES (X_LEN+1) * (XDOT_LEN+1) * (THETA_LEN+1) * (THETADOT_LEN+1) #define NUM_ACTS 100 float *states = (float *)malloc(NUM_STATES * sizeof(float)); // as opposed to float states[NUM_STATES] (more memory effecient) float **q = (float**)malloc(NUM_STATES * sizeof(float*)); for(int i=0; i < NUM_STATES; i++) { float *a = (float*)malloc(NUM_ACTS * sizeof(float)); for(int j=0; j < NUM_ACTS; j++) { a[j] = 0.0f; } q[i] = a; }
然后上面的printf
语句出现在代码中。
我加入malloc
的原因是因为根据我的理解, SIGSEGV
与形成不良的malloc
调用有关。 因此,如果数组初始化是造成问题的原因,我想知道:
- 为什么?
- 如何更改
malloc
代码来解决这个问题?
我已经包含了OS X生成的崩溃日志,以防万一可以帮助任何人。
流程:极点[5453] 路径:{删除} 标识符:极点 版本:??? (???) 代码类型:X86-64(原生) 父母程序:bash [5441] 日期/时间:2009-12-08 11:38:38.358 -0600 操作系统版本:Mac OS X 10.6.2(10C540) 报告版本:6 自上次报告后的时间间隔:130074秒 自上次报告以来崩溃:68 自上次报告以来每个应用程序崩溃:63 匿名UUID:CA20CF15-8C46-4C85-A793-6C69F9F40140 例外类型:EXC_BAD_ACCESS(SIGSEGV) exception代码:KERN_INVALID_ADDRESS位于0x0000000100074f3b Crashed Thread:0 Dispatch queue:com.apple.main-thread 线程0崩溃:调度队列:com.apple.main-thread 0 libSystem.B.dylib 0x00007fff828d489e __Balloc_D2A + 164 1 libSystem.B.dylib 0x00007fff828d49b8 __d2b_D2A + 45 2 libSystem.B.dylib 0x00007fff828e8c74 __dtoa + 320 3 libSystem.B.dylib 0x00007fff828aa960 __vfprintf + 4980 4 libSystem.B.dylib 0x00007fff828ec7db vfprintf_l + 111 5 libSystem.B.dylib 0x00007fff828ec75e fprintf + 196 6极0x00000001000028b5 Balance :: sarsa()+ 187 7极0x0000000100002e54主+ 49 8极0x00000001000010a8开始+ 52 线程0与X86线程状态(64位)崩溃: rax:0x0000000000000001 rbx:0x000000010042cca0 rcx:0x000000010042cca8 rdx:0x0000000100074f3b rdi:0x000000000000000e rsi:0x00007fff5fbfecbc rbp:0x00007fff5fbfeba0 rsp:0x00007fff5fbfeb90 r8:0x00007fff5fbff0b0 r9:0x0000000000000000 r10:0x00000000ffffffff r11:0x000000010083a40b r12:0x0000000000000001 r13:0x00007fff5fbfecb8 r14:0x00007fff5fbfecbc r15:0x000000010000363e rip:0x00007fff828d489e rfl:0x0000000000010202 cr2:0x0000000100074f3b 二进制图像: 0x100000000 - 0x100003fff +极??? (???){删除} 0x7fff5fc00000 - 0x7fff5fc3bdef dyld 132.1(???)/ usr / lib / dyld 0x7fff81697000 - 0x7fff8169bff7 libmathCommon.A.dylib ??? (???)/usr/lib/system/libmathCommon.A.dylib 0x7fff8289c000 - 0x7fff82a5aff7 libSystem.B.dylib ??? (???)/ usr / lib / libSystem.B.dylib 0x7fff83c4c000 - 0x7fff83cc9fef libstdc ++。6.dylib ??? (???)/usr/lib/libstdc++.6.dylib 0x7fffffe00000 - 0x7fffffe01fff libSystem.B.dylib ??? (???)/ usr / lib / libSystem.B.dylib 型号:MacBookPro4,1,BootROM MBP41.00C1.B03,2处理器,Intel Core 2 Duo,2.4 GHz,2 GB,SMC 1.27f2 显卡:NVIDIA GeForce 8600M GT,GeForce 8600M GT,PCIe,256 MB 内存模块:global_name AirPort:spairport_wireless_card_type_airport_extreme(0x14E4,0x8C),Broadcom BCM43xx 1.0(5.10.91.19) 蓝牙:版本2.2.4f3,2服务,1个设备,1个传入串行端口 网络服务:AirPort,AirPort,en1 串行ATA设备:Hitachi HTS542520K9SA00,186.31 GB 并行ATA设备:MATSHITADVD-R UJ-867 USB设备:内置iSight,0x05ac(Apple Inc.),0x8502,0xfd400000 USB设备:Apple内部键盘/触控板,0x05ac(Apple Inc.),0x0230,0x5d200000 USB设备:IR接收器,0x05ac(Apple Inc.),0x8242,0x5d100000 USB设备:BRCM2046集线器,0x0a5c(Broadcom Corp.),0x4500,0x1a100000 USB设备:蓝牙USB主机控制器,0x05ac(Apple Inc.),0x820f,0x1a110000
谢谢。
您的代码中的其他地方有一个与printf
语句无关的错误。 你正在某个地方踩踏内存,但是直到printf
试图用__BAlloc_D2A
分配一些内存__BAlloc_D2A
,因为它用来跟踪空闲内存块的堆数据结构已经损坏,因此崩溃了。
为了尝试检测你在内存上踩踏的地方,有很多工具可用。 如果您使用的是Linux,我建议使用valgrind ,它基本上在虚拟机中运行您的代码并告诉您何时执行任何非法操作,如读/写内存超出范围,读取未初始化的变量等等。但是,它不可用在Mac OS X中(尚未)。
一种选择是使用libgmalloc :
% cat gmalloctest.c #include #include main() { unsigned *buffer = (unsigned *)malloc(sizeof(unsigned) * 100); unsigned i; for (i = 0; i < 200; i++) { buffer[i] = i; } for (i = 0; i < 200; i++) { printf ("%d ", buffer[i]); } } % cc -g -o gmalloctest gmalloctest.c % gdb gmalloctest Reading symbols for shared libraries .. done (gdb) set env DYLD_INSERT_LIBRARIES /usr/lib/libgmalloc.dylib (gdb) r Starting program: gmalloctest Reading symbols for shared libraries .. done GuardMalloc: Allocations will be placed on 16 byte boundaries. GuardMalloc: - Some buffer overruns may not be noticed. GuardMalloc: - Applications using vector instructions (eg, SSE or Altivec) should work. GuardMalloc: GuardMalloc version 19 Program received signal EXC_BAD_ACCESS, Could not access memory. Reason: KERN_PROTECTION_FAILURE at address: 0xb000d000 0x00001f65 in main () at gmalloctest.c:10 10 buffer[i] = i; (gdb) print i $1 = 100 (gdb) where #0 0x00001f65 in main () at gmalloctest.c:10 (gdb)
另请参阅启用Malloc调试function 。
当您访问未映射到任何内容的虚拟地址或以不允许的方式访问地址时(例如,尝试写入只读区域),SIGSEGV会发生。 如您所说,分段错误可能与堆损坏有关。 这是因为在内部,大多数malloc
实现将簿记信息与堆上的已分配数据交错。 如果该簿记信息被破坏,则malloc
的行为未定义。 您可能在程序的后期很久才会看到任何错误。
在这种情况下, printf
可能在内部分配一些内存,这会触发故障。 解决此问题的最佳方法可能是使用valgrind运行程序,它会在发生任何堆损坏时立即通知您。
您可能有指针算术错误或缓冲区溢出,这会产生打破printf
的副作用。
尝试注释掉大部分代码( printf
除外)并查看它是否崩溃。 如果没有,那么一点一点地发表评论部分直到你得到崩溃。 你会知道问题出在哪里。
此外,如果您使用的是linux或任何unix变体,请查看使用valgrind 。
编辑:
我在错误报告中看到了这一点:
0 libSystem.B.dylib 0x00007fff828d489e __Balloc_D2A + 164
这就是实际崩溃的地方,这似乎是一个低级别的分配例程。 我猜你有一个缓冲区溢出,这会破坏空闲列表,使某些未来的分配中断(例如在这个printf中)。
第一个可能性是,你只是普通的malloc()为你的数组提供了足够的内存,而printf()试图使malloc()多一点,并且失败了。 我认为这不太可能。
第二个可能性是你的printf()并不像你所展示的那么简单,而是一些相当复杂的多级指针表达式,而且这个表达式在某处变得很疯狂。
printf的一些实现在处理“%f”时执行malloc。 如果它正在执行此操作,那么如果您在某个时刻溢出内存(即写入超过分配结束),则printf可以尝试进行分配并发现堆已损坏并抛出错误…
只是一个想法。
编辑:它可能值得看看你的状态数组是如何被填充的…其他2看起来很好,但你可以在任何地方写到结尾…
printf("%f", parm)
期望参数为double。 你的f
是一个浮点数,它被隐式转换为double。
也许隐含的转换搞砸了???
尝试显式转换
float f = 0.5f; printf("%f\n",(double)f);
甚至
float f = 0.5f; double ff = f; printf("%f\n",ff);
只是一个猜测,但你做过#include
? 如果没有范围内的malloc()
原型,编译器会假定malloc()
返回int
,这显然不是真的。
如果我是对的(即使不是这样),它暴露了不在C中转换malloc()
的返回值的原因。注意,如果你正在编写C和C ++的代码,你将需要,但对于纯C,不要不要转换malloc()
的返回值,让编译器为你做正确的事。
所以,而不是:
T *data = (T *) malloc(sz * sizeof(T));
改为:
#include ... T *data = malloc(sz * sizeof *data);
这里, T
是任何类型。 优点是:
- 如果忘记
#include
,编译器会抱怨, - 如果更改
data
类型,则malloc()
调用不需要更改,并且 - 我认为它更容易阅读,更不容易出错。
通过转换malloc()
的返回值,您不会让编译器有机会警告您缺少stdlib.h
包含。