目录
最近从windows到linux 迁移代码,遇到了一个sprintf参数空指针段错误。
很疑惑,按说项目这么久不该出现段错误。
1.代码
原代码(用到msxml)
sprintf(pItemData.szNodeText,"%s",(const char*)nodeptr->Gettext());
移植后的代码(用到libxml2)
xmlCharTemp = xmlNodeGetContent(xmlChildNode); sprintf(pItemData.szNodeText,"%s", (const char*)xmlCharTemp); xmlFree(xmlCharTemp);
用什么xml解析库并不关键,关键是sprintf的参数都有可能是空指针。
于是乎实验了一下,发现vc++6.0 和 g++编译器存在差异性。
Windows-vc++6.0-sprintf截图
Linux-g++sprintf-截图
2.G++为什么会出现这种情况?
输出汇编代码
g++ test.cpp -S -o test.S
.file "test.cpp" .section .rodata .LC0: .string "%s" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $32, %rsp movq $0, -8(%rbp) leaq -32(%rbp), %rax movl $0, %edx movl $.LC0, %esi movq %rax, %rdi movl $0, %eax call sprintf leaq -32(%rbp), %rax movq %rax, %rdi call puts movq -8(%rbp), %rdx leaq -32(%rbp), %rax movq %rdx, %rsi movq %rax, %rdi call strcpy leaq -32(%rbp), %rax movq %rax, %rdi call puts movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-28)" .section .note.GNU-stack,"",@progbits
会发现两次调用sprintf,第一次调用的call sprintf,第二次调用的call
strcpy。
3.printf也会存在类似的段错误
Windows-vc++6.0-printf截图
Linux-g++-printf-截图
g++汇编代码
.file "test2.cpp" .section .rodata .LC0: .string "%s" .LC1: .string "%s \n" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $32, %rsp movl %edi, -20(%rbp) movq %rsi, -32(%rbp) movq $0, -8(%rbp) movq -8(%rbp), %rax movq %rax, %rsi movl $.LC0, %edi movl $0, %eax call printf movq -8(%rbp), %rax movq %rax, %rsi movl $.LC1, %edi movl $0, %eax call printf movq -8(%rbp), %rax movq %rax, %rdi call puts movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-28)" .section .note.GNU-stack,"",@progbits
会发现前两次调用的call printf,而第三次调用call puts。
4.总结
尽量不要期待编译器去做空指针的判断优化,因为大多数情况下你不知道编译器是怎么实现的,况且编译器间存在差距,自己保证参数的不为空指针。
最后代码无奈改成了这个样子。
sprintf(pItemData.szNodeText,"%s", xmlCharTemp!=NULL?(const char*)xmlCharTemp:"(null)");