NULL 指针在不同平台下的表现引发程序报错(C 语言)

  • 为什么有这篇
  • 正文
  • 1. 使用环境
  • 2.问题描述及展示
  • 3. 走了些弯路
  • 4. 柳岸花明(GDB 调试)
  • 5. 访问 NULL 指针错误背后的原理
  • 总结

 作者:高玉涵
 时间:2022.6.1 13:47
 博客:blog.csdn.net/cg_i

眼前的高山多少让我有点胆颤。—— 2021.5.17 微信朋友圈

为什么有这篇

 下面这段话引用自 2021 年 5 月 17 日,我首次接到任务时写的一篇日记《X86 项目移植的思考》开篇的部分内容:

 把一个运行在某个操作系统和硬件结构上的软件程序,在另一个操作系统和硬件结构上重新编译(包括一些必要的修改),以便在新的平台上运行,这一过程叫做应用程序移植。有些情况下,把应用程序从一个平台移植到另一个平台上非常简单直接,仅需要重新编译并进行一些验证测试即可。但是有些情况下,移植程序并不是这么容易。一些大的应用程序可能是在较老的操作系统上编写的,并且是用操作系统特有的编译器编译的,这些应用程序可能并不遵循现在的编程语言标准,因此移植起来就比较困难 …

 时隔一年,随着时间推移,我对此项工作的理解也逐步加深。边学、边干的过程中,项目正按时有条不紊的推进中,离预期目标也越来越近(期间感谢这里就一一不表了)。可能缘于“二八法则”越深入,真正的“硬骨头”才显现出来。回望以往写过的这段话,再结合这周的工作经历,当时担心的事终还是发生了,而且伤害来得极快。

 如标题所述,我下面给出的例子是真实存在于项目中的(示例程序只为说明问题做了简化),并将排查、分析过程分享出来。一来,是为了说明遇到此类问题时排查的难度。二来,是为了给各位程序员们传递日常书写程序时,遵循”标准“的重要性(这里指的是 ANSI C 标准)。特别是 C 语言这种极度灵活,这有助于提高它的效率,但也增加了出错的可能性。例如,C 对数组下标引用和指针访问并不进行有效性检查,这可以节省时间(相信我,你节省的那点时间,必将成倍反噬于你),但你在使用这些特性时就必须特别小心。如果你在使用 C 语言时能够遵守相关规定,就可以避免这些潜在的问题。

 最后,希望此文能有助于您,避开可能遇到的痛苦。

正文

1. 使用环境

  • CPU

 HPUNIX:安腾 9700 系列

 LINUX:X86 架构

  • 操作系统

 HPUNIX:HP-UX xxxxx2 B.11.31 U ia64 2932500072 unlimited-user license

 LINUX:Linux xxxxkf1 4.19.90-24.4.v2101.ky10.x86_64 #1 SMP Mon May 24 12:14:55 CST 2021 x86_64 x86_64 x86_64 GNU/Linux

  • 编译器程序及版本

 HPUNIX:cc: HP C/aC++ B3910B A.06.25 [Nov 30 2009]

 LINUX:

使用内建 specs。
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/7.3.0/lto-wrapper
目标:x86_64-linux-gnu
配置为:../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,fortran,lto --enable-plugin --enable-initfini-array --disable-libgcj --without-isl --without-cloog --enable-gnu-indirect-function --build=x86_64-linux-gnu --with-stage1-ldflags=' -Wl,-z,relro,-z,now' --with-boot-ldflags=' -Wl,-z,relro,-z,now' --with-tune=generic --with-arch_32=x86-64 --disable-multilib
线程模型:posix
gcc 版本 7.3.0 (GCC)

2.问题描述及展示

 先看示例代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>int BDPceshi( char *sDPACNO, char *sFRENUM, double *dFREAMT )
{memset(sDPACNO,0,sizeof(sDPACNO));printf("!!!!!!!!!\n");sprintf(sDPACNO,"%s",sFRENUM);printf("!!!!!!!!![%s]\n",sDPACNO);sprintf(sDPACNO,"%s",sFRENUM);if( 0 == strlen( sFRENUM ) ){printf("会core掉,应该打印不出来\n");}return 0;
}
int main()
{double dFREAMT;char sDPACNO[254];int iRe= BDPceshi(sDPACNO,NULL,&dFREAMT);if(iRe != 0){printf("打印成功\n");}
}
  • LINUX 编译并运行
gcc -o t t.c
#./t
!!!!!!!!!
段错误 (核心已转储)
  • HPUX 编译并运行
cc -o t t.c
./t
!!!!!!!!!
!!!!!!!!![]
会core掉,应该打印不出来

 如你所见代码在两个平台下,编译通过程序生成成功。但在运行时 LINUX 下会崩溃,HPUNIX 下正常(起码看起来像)。运行时错误的排查难点:编译错误可由编译器检查出来,而多数编译器对运行时错误却无能为力,查错和纠错只能由人工完成,考虑到存量代码已积累多年,此项工作繁重。让我们先分析一下代码,在 main 函数里唯一做的就是调用了一个名为 BDPceshi的函数,这个函数内部也不神秘,无非是初始化和格式化字符串并输出的操作,看似也没什么问题。让我们回过头再仔细观察, main 函数在调用 BDPceshi时,根据 int BDPceshi( char *sDPACNO, char *sFRENUM, double *dFREAMT )函数定义,它接收 3 个参数,前 2 个是指向字符型的指针,最后 1 个是双精度浮点型数值。调用时,在第 2 个参数所在位置,却给它传递了一个 NULL 值 BDPceshi(sDPACNO,NULL,&dFREAMT);这里发生了参数数据类型调用和定义时不一致的情景。即使是这样,当时我也并没觉得有什么了不起,在 C 语言里这种情况太稀松平常了,不然也不会有默认数据类型转换这种东西的存在。

 话虽如此,初步分析问题可能出在这个地方。那么NULL 指针在不同平台下的表现如何?带着凝问我开始了下面的排查。

3. 走了些弯路

  1. 获取编译器默认执行标准

 我首先想到两个平台的 C 编译器,默认执行的“标准”可能不同,生成的机器码自然也不同。首先,我得先弄清楚们各自区别。获取编译器默认执行标准代码如下:

#include <stdio.h>
#include <stdlib.h>int main()
{printf("%d\n", __STDC__);printf("%ld\n",__STDC_VERSION__);return 0;
}
  • LINUX
gcc -o std std.c
./std
1
201112
  • HPUX
cc -o std std.c
./std
1
199901

 根据输出结果获知 LINUX、HPUX 编译器,各自默认执行的标准是 C11 和 C99。

  1. 获取不同编译器生成的汇编码
  • HPUX
 .file   "t.c".psr msb.radix   C.pred.safe_across_calls p1-p5,p16-p63.allow 0, "ao", "x1", "x2", "da".type sprintf,@function.global   sprintf.type    memset,@function.global    memset.type BDPceshi,@function.global  BDPceshi.type   main,@function.global  main.type   _memset,@function.global   _memset.size    BDPceshi, 320
// Routine [id=0004] ( BDPceshi )// ===.secalias .abe$3.text, ".text".section .abe$3.text = "ax", "progbits".align   16.proc BDPceshi
..L0:
//      $start          ARid768 =               ;; // A..L2:
BDPceshi::
.prologue
//      $entry          ARid770, S:r32, S:r33, S:r34 =  // A [t.c: 6/1]
//file/line/col t.c/6/1
.save ar.pfs, r40alloc           r40 = ar.pfs, 3, 7, 3, 0   // M [t.c: 6/1]
// SPadd             sp = -512, sp              // M [t.c: 6/1]
.save rp, r41mov             r41 = rp                   // I [t.c: 6/1]add             r39 = 0, gp                // M
.bodyadd             r37 = 0, r34               // M [t.c: 6/1]add             r36 = 0, r33            ;; // I [t.c: 6/1]add             r35 = 0, r32               // M [t.c: 6/1]
//file/line/col t.c/9/2add             out2 = 512, r0             // M [t.c: 9/2]add             out1 = 0, r0               // I [t.c: 9/2]add             r9 = 16, sp                // M [t.c: 9/2]add             r11 = 16, sp               // M [t.c: 9/2]add             r14 = r0, gp            ;; // I [t.c: 9/2]lfetch.nt1      [r9]                       // M [t.c: 9/2]add             r9 = @pltoff(_memset), r0  // I [t.c: 9/2]add             out0 = 0, r11           ;; // I [t.c: 9/2]add             r10 = r9, gp            ;; // M [t.c: 9/2]ld8.acq         r9 = [r10]                 // M [t.c: 9/2]add             r10 = 8, r10            ;; // I [t.c: 9/2]ld8             gp = [r10]                 // M [t.c: 9/2]mov             b6 = r9                    // I [t.c: 9/2]br.call.dptk.many rp = b6               ;; // B [t.c: 9/2] [FN: _memset#]add             gp = 0, r39                // M [t.c: 9/2]
//file/line/col t.c/10/2add             r9 = @ltoffx(.abe$2.rodata), r0 // M [t.c: 10/2]add             r11 = 16, sp               // I [t.c: 10/2]add             out2 = 0, r36              // M [t.c: 10/2]add             r10 = @pltoff(sprintf), r0 ;; // I [t.c: 10/2]add             r9 = r9, gp                // I [t.c: 10/2]add             r10 = r10, gp              // M [t.c: 10/2]add             r14 = r0, gp               // M [t.c: 10/2]add             out0 = 0, r11           ;; // I [t.c: 10/2]ldxmov          r38 = [r9], .abe$2.rodata# // M [t.c: 10/2]ld8.acq         r9 = [r10]                 // M [t.c: 10/2]add             r10 = 8, r10            ;; // I [t.c: 10/2]ld8             gp = [r10]                 // M [t.c: 10/2]add             out1 = 0, r38              // M [t.c: 10/2]mov             b6 = r9                    // I [t.c: 10/2]nop.m           0                          // Mnop.m           0                          // Mbr.call.dptk.many rp = b6               ;; // B [t.c: 10/2] [FN: sprintf#]add             gp = 0, r39                // M [t.c: 10/2]
//file/line/col t.c/11/2add             r11 = 16, sp               // M [t.c: 11/2]add             out1 = 0, r38              // I [t.c: 11/2]add             out2 = 0, r36              // M [t.c: 11/2]add             r9 = @pltoff(sprintf), r0 ;; // I [t.c: 11/2]add             r10 = r9, gp               // I [t.c: 11/2]add             r14 = r0, gp               // M [t.c: 11/2]add             out0 = 0, r11           ;; // I [t.c: 11/2]nop.i           0                          // Ild8.acq         r9 = [r10]                 // M [t.c: 11/2]add             r10 = 8, r10            ;; // I [t.c: 11/2]mov             b6 = r9                    // I [t.c: 11/2]ld8             gp = [r10]                 // M [t.c: 11/2]nop.m           0                          // Mbr.call.dptk.many rp = b6               ;; // B [t.c: 11/2] [FN: sprintf#]add             gp = 0, r39                // M [t.c: 11/2]
//file/line/col t.c/12/2add             r8 = 0, r0                 // M [t.c: 12/2]mov             rp = r41                   // I [t.c: 12/2]
.restore spadd             sp = 512, sp               // M [t.c: 12/2]mov             ar.pfs = r40               // I [t.c: 12/2]br.ret.dptk.many rp                     ;; // B [t.c: 12/2]..L1:
//      $end                                    ;; // A.endp    BDPceshi// ===// ===.secalias .abe$4.IA_64.unwind, ".IA_64.unwind".section .abe$4.IA_64.unwind = "a", "unwind".align 4data4.ua @segrel(.abe$3.text)data4.ua @segrel(.abe$3.text+320)data4.ua @segrel(.abe$unwind_info_block00000)// ===.secalias .abe$5.IA_64.unwind_info, ".IA_64.unwind_info".section .abe$5.IA_64.unwind_info = "a", "progbits".align 4
// unwind_info_block: notype local tempdata4.ua @segrel(.llo$annot_info_block0000)
.abe$unwind_info_block00000:    data1   0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04data1 0x04, 0xe4, 0x02, 0xb0, 0xa9, 0xe6, 0x00, 0xb1data1 0x28, 0xe0, 0x01, 0x20, 0x61, 0x38, 0xc0, 0x02.size main, 80
// Routine [id=0007] ( main )// ===.secalias .abe$7.text, ".text".section .abe$7.text = "ax", "progbits".align   16.proc main
..L0:
//      $start          ARid768 =               ;; // A..L2:
main::
.prologue
//      $entry          ARid770 =                  // A [t.c: 16/1]
//file/line/col t.c/16/1
.save ar.pfs, r33alloc           r33 = ar.pfs, 0, 3, 3, 0   // M [t.c: 16/1]
.save rp, r34mov             r34 = rp                   // I [t.c: 16/1]
// SPadd             sp = -272, sp              // I [t.c: 16/1]
.body//file/line/col t.c/19/9add             out1 = 0, r0            ;; // M [t.c: 19/9]add             r10 = 32, sp               // M [t.c: 19/9]add             r9 = 16, sp             ;; // I [t.c: 19/9]add             out0 = 0, r10              // M [t.c: 19/9]add             out2 = 0, r9               // M [t.c: 19/9]br.call.dptk.many rp = BDPceshi#        ;; // B [t.c: 19/9]add             r32 = 0, r8                // M [t.c: 19/9]
//file/line/col t.c/20/1add             r8 = 0, r0                 // M [t.c: 20/1]mov             rp = r34                   // I [t.c: 20/1]
.restore spadd             sp = 272, sp               // M [t.c: 20/1]mov             ar.pfs = r33               // I [t.c: 20/1]br.ret.dptk.many rp                     ;; // B [t.c: 20/1]..L1:
//      $end                                    ;; // A.endp    main// ===// ===.secalias .abe$8.IA_64.unwind, ".IA_64.unwind".section .abe$8.IA_64.unwind = "a", "unwind".align 4data4.ua @segrel(.abe$7.text)data4.ua @segrel(.abe$7.text+80)data4.ua @segrel(.abe$unwind_info_block00001)// ===.secalias .abe$9.IA_64.unwind_info, ".IA_64.unwind_info".section .abe$9.IA_64.unwind_info = "a", "progbits".align 4
// unwind_info_block: notype local tempdata4.ua @segrel(.llo$annot_info_block0001)
.abe$unwind_info_block00001:    data1   0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04data1 0x03, 0xe4, 0x01, 0xb0, 0xa2, 0xe6, 0x00, 0xb1data1 0x21, 0xe0, 0x02, 0x11, 0x2c, 0xc0, 0x02, 0x00// ===.secalias .abe$2.rodata, ".rodata".section .abe$2.rodata = "a", "progbits".align 8
// .rodata: section local
// _noname: notype local temp.abe$_noname00002: stringz "%s"// ===.secalias .abe$6.HP.opt_annot, ".HP.opt_annot".section .abe$6.HP.opt_annot = "a", "annot".align 8
// .llo$annot_info_block0000: object local temp
// .llo$annot_info_block0001: object local temp
.llo$annot_info_block0000:  data1   0x00, 0xac, 0x01, 0xbe, 0xff, 0xfe, 0xff, 0xffdata1 0xff, 0xfc, 0xff, 0xff, 0xa0, 0xfc, 0x01, 0x02data1 0x84, 0x00, 0x02, 0x84, 0x03, 0x01, 0x84, 0x04data1 0x01, 0x03, 0x88, 0x02, 0x00, 0x1f, 0x00, 0x00
.llo$annot_info_block0001:  data1   0x00, 0xac, 0x01, 0xfe, 0xff, 0xfe, 0xff, 0xffdata1 0xff, 0xfc, 0xff, 0xff, 0xe8, 0xfe, 0x01, 0x02data1 0x84, 0x00, 0x02, 0x84, 0x03, 0x01, 0x84, 0x04data1 0x01, 0x03, 0x88, 0x02, 0x00, 0x1f, 0x00// ===.secalias .abe$10.debug_procs_info, ".debug_procs_info".section .abe$10.debug_procs_info = "", "progbits".align 1data1  0x00, 0x00, 0x00, 0x4b, 0x00, 0x02data4.ua @secrel(.abe$11.debug_procs_abbrev)data1    0x04, 0x03data4.ua @secrel(.abe$0.debug_line)data4.ua @secrel(.abe$1.debug_actual)stringz "t.c"data1    0x02, 0x01, 0x47, 0x00stringz   "/root"data1  0x00, 0x00, 0x00, 0x00stringz   "\tt.c"data1  0x00, 0x00, 0x00, 0x30, 0x00, 0x07, 0x01, 0x00data1 0x00, 0x00, 0x00data4.ua .abe$3.textdata4.ua .abe$3.text+320data1  0x00, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00data4.ua .abe$7.textdata4.ua .abe$7.text+80data1   0x00, 0x00// ===.secalias .abe$11.debug_procs_abbrev, ".debug_procs_abbrev".section .abe$11.debug_procs_abbrev = "", "progbits".align 1
// .debug_procs_abbrev: section localdata1  0x03, 0x11, 0x01, 0x10, 0x06, 0x90, 0x40, 0x06data1 0x03, 0x08, 0x13, 0x0b, 0x95, 0x40, 0x0b, 0x25data1 0x08, 0x1b, 0x08, 0x01, 0x13, 0x00, 0x00, 0x07data1 0x2e, 0x01, 0x94, 0x40, 0x0b, 0x96, 0x40, 0x06data1 0x11, 0x01, 0x12, 0x01, 0x00, 0x00, 0x09, 0x1edata1 0x01, 0x03, 0x08, 0x01, 0x13, 0x00, 0x00, 0x00// ===.secalias .abe$0.debug_line, ".debug_line".section .abe$0.debug_line = "", "progbits".align 1
// .debug_line: section localdata1  0x00, 0x00, 0x00, 0x4a, 0x00, 0x02, 0x00, 0x00data1 0x00, 0x10, 0x04, 0x01, 0x00, 0x05, 0x0a, 0x00data1 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01data1 0x00, 0x00, 0x00, 0x08, 0x03stringz "t.c"data1    0x00, 0x00, 0xdd, 0x02, 0x00, 0x05, 0x02data4.ua .abe$3.textdata1   0x04, 0x01, 0x05, 0x01, 0x03, 0x05, 0x01, 0x05data1 0x02, 0x3a, 0x6f, 0x83, 0x6f, 0x00, 0x05, 0x02data4.ua .abe$7.textdata1 0x05, 0x01, 0x0e, 0x05, 0x09, 0x21, 0x05, 0x01data1 0x38, 0x02, 0x04, 0x00, 0x01, 0x01// ===.secalias .abe$1.debug_actual, ".debug_actual".section .abe$1.debug_actual = "", "progbits".align 1
// .debug_actual: section localdata1    0x00, 0x00, 0x00, 0x69, 0x00, 0x02, 0x00, 0x00data1 0x00, 0x0e, 0x04, 0x01, 0x00, 0x05, 0x0a, 0x00data1 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01data1 0x00, 0x05, 0x02data4.ua .abe$3.textdata1   0x00, 0x01, 0x11, 0x0b, 0x00, 0x01, 0x11, 0x0fdata1 0x00, 0x01, 0x11, 0x1e, 0x00, 0x01, 0x11, 0x0fdata1 0x00, 0x01, 0x11, 0x14, 0x10, 0x6f, 0x00, 0x01data1 0x11, 0x73, 0x1a, 0x00, 0x01, 0x11, 0x5f, 0x1adata1 0x00, 0x01, 0x11, 0x19, 0x00, 0x01, 0x18, 0x14data1 0x00, 0x05, 0x02data4.ua .abe$7.textdata1   0x00, 0x01, 0x11, 0x0b, 0x00, 0x01, 0x11, 0x14data1 0x15, 0x00, 0x01, 0x11, 0x28, 0x00, 0x01, 0x11data1 0x14, 0x10, 0x00, 0x01, 0x11, 0x19, 0x00, 0x01data1 0x18, 0x14, 0x02, 0x04, 0x00, 0x01, 0x01// ===.secalias .abe$12.note, ".note".section .abe$12.note = "", "note".align 4data1  0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x5fdata1 0x00, 0x00, 0x00, 0x01stringz   "HP"data1 0x00stringz "A.06.25 [Nov 30 2009] [Build N/A *E001*]\necom options = -assembly on -ext on -lang c,c99 -ansi_for_scope on -inline_power 1 -link_type dynamic -fpeval float -fpevaldec _Decimal32 -tls_dyn on -target_os 11.31 --sys_include /usr/include -ucode hdriver=optlevel%1% -plusolistoption -Ol06all! -plusolistoption -Ol12direct! -plusolistoption -Ol13moderate! -plusooption -Oq01,al,ag,cn,sz,ic,vo,Mf,Po,es,rs,Rf,Pr,sp,in,cl,om,vc,pi,fa,pe,rr,pa,pv,nf,cp,lx,Pg,ug,lu,lb,uj,dn,sg,pt,kt,em,np,ar,rp,dl,fs,bp,wp,pc,mp,lr,cx,cr,pi,so,Rc,fa,ft,fe,ap,st,lc,Bl,sr,Qs,do,ib,pl,sd,ll,rl,dl,Lt,ol,fl,lm,ts,rd,Dp,If!\n1653977540"data1 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00data1 0x24, 0x00, 0x00, 0x00, 0x04stringz "HP"data1 0x00stringz "t.c\n/root\nANSI C 32 bits\n1653977528"
  • LINUX
 .file   "te.c".pred.safe_across_calls p1-p5,p16-p63.section   .rodata,    "a",  "progbits".align 8
.LC0:stringz    "!!!!!!!!!".align 8
.LC1:stringz    "!!!!!!!!![%s]\n".align 8
.LC2:stringz    "\273\341core\265\364\243\254\323\246\270\303\264\362\323\241\262\273\263\366\300\264".section    .text,  "ax", "progbits".align 16.global BDPceshi#.proc BDPceshi#
BDPceshi:.prologue 14, 35.save ar.pfs, r36alloc r36 = ar.pfs, 3, 4, 3, 0.vframe r37mov r37 = r12adds r12 = -528, r12.save rp, r35mov r35 = b0mov r38 = r1.body;;mov r14 = r37;;st4 [r14] = r32adds r14 = 4, r37;;st4 [r14] = r33adds r14 = 8, r37;;st4 [r14] = r34adds r14 = -512, r37;;mov r39 = r14mov r40 = r0addl r41 = 512, r0br.call.sptk.many b0 = memset#mov r1 = r38;;addl r39 = @ltoffx(.LC0), r1;;ld8.mov r39 = [r39], .LC0br.call.sptk.many b0 = puts#mov r1 = r38adds r14 = 4, r37adds r15 = -512, r37;;mov r39 = r15ld4 r40 = [r14]br.call.sptk.many b0 = strcpy#mov r1 = r38;;addl r39 = @ltoffx(.LC1), r1;;ld8.mov r39 = [r39], .LC1adds r14 = -512, r37;;mov r40 = r14br.call.sptk.many b0 = printf#mov r1 = r38adds r14 = 4, r37adds r15 = -512, r37;;mov r39 = r15ld4 r40 = [r14]br.call.sptk.many b0 = strcpy#mov r1 = r38adds r14 = 4, r37;;ld4 r14 = [r14];;addp4 r14 = 0,r14;;ld1 r14 = [r14];;sxt1 r14 = r14;;cmp4.ne p6, p7 = 0, r14(p6) br.cond.dptk .L2addl r39 = @ltoffx(.LC2), r1;;ld8.mov r39 = [r39], .LC2br.call.sptk.many b0 = puts#mov r1 = r38
.L2:mov r14 = r0;;mov r8 = r14mov ar.pfs = r36mov b0 = r35.restore spmov r12 = r37br.ret.sptk.many b0;;.endp BDPceshi#.section .rodata,    "a",  "progbits".align 8
.LC3:stringz    "\264\362\323\241\263\311\271\246".section    .text,  "ax", "progbits".align 16.global main#.proc main#
main:.prologue 14, 32.save ar.pfs, r33alloc r33 = ar.pfs, 0, 4, 3, 0.vframe r34mov r34 = r12adds r12 = -272, r12.save rp, r32mov r32 = b0mov r35 = r1.body;;adds r14 = -240, r34adds r15 = -248, r34;;mov r36 = r14mov r37 = r0mov r38 = r15br.call.sptk.many b0 = BDPceshi#mov r1 = r35mov r14 = r8adds r15 = -256, r34;;st4 [r15] = r14adds r15 = -256, r34;;ld4 r14 = [r15];;cmp4.eq p6, p7 = 0, r14(p6) br.cond.dptk .L9addl r36 = @ltoffx(.LC3), r1;;ld8.mov r36 = [r36], .LC3br.call.sptk.many b0 = puts#mov r1 = r35
.L9:;;mov ar.pfs = r33mov b0 = r32.restore spmov r12 = r34br.ret.sptk.many b0;;.endp main#.ident "GCC: (GNU) 4.2.3".global strcpy#.type    strcpy#,@function.global puts#.type    puts#,@function.global printf#.type    printf#,@function.global memset#.type  memset#,@function

 经过上述一通折腾,是不是感觉有点头晕眼花,血压直接往上窜。加之 HPUX 下的汇编,网上资料风毛菱角,就在我快走进死胡同的时候,忽然灵光显现,为何不用 GDB 跟踪一下试试。

4. 柳岸花明(GDB 调试)

  • HPUNX
gdb t
HP gdb 6.3 for HP Itanium (32 or 64 bit) and target HP-UX 11iv2 and 11iv3.
Copyright 1986 - 2011 Free Software Foundation, Inc.
Hewlett-Packard Wildebeest 6.3 (based on GDB) is covered by the
GNU General Public License. Type "show copying" to see the conditions to
change it and/or distribute copies. Type "show warranty" for warranty/support.
..
(gdb) b main
Breakpoint 1 at 0x4000c00:1: file t.c, line 27 from /root/t.
(gdb)
(gdb) run
Starting program: /root/t
Breakpoint 1, main () at t.c:27
27              int iRe= BDPceshi(sDPACNO,NULL,&dFREAMT);
(gdb) s
BDPceshi (sDPACNO=0x200000007ffff090 "", sFRENUM=0x0, dFREAMT=0x200000007ffff080) at t.c:7
7               memset(sDPACNO,0,sizeof(sDPACNO));

 当我看到 sFRENUM=0x0时,我已恍然大悟,记得曾经在那本书上提到过此种情形,只是时间久远,当下已然记不起了,不过可以笃定的是,它就是问题的关键。抱着印证心中的想法,我又执行了下面的命令:

(gdb) x /x 0x0
0x0:    Error accessing memory address 0x0: Invalid argument.
(gdb) x /x 0x200000007ffff090
0x200000007ffff090:     0x00000180
(gdb) x /x 0x200000007ffff080
0x200000007ffff080:     0x00000000
(gdb) s
8               printf("!!!!!!!!!\n");
(gdb) s
!!!!!!!!!
11              sprintf(sDPACNO,"%s",sFRENUM);
(gdb) s
12              printf("!!!!!!!!![%s]\n",sDPACNO);
(gdb) s
!!!!!!!!![]

 通过分别访问 3 个参数数据存储地址。再结合访问 0x0 位置系统给出的提示疑惑已解。下面又跟踪了几行,余下部分省略。

 为了对比,也给出 LINUX 下跟踪的结果,大家从中可以发现明显的区别。

  • LINUX
gdb t
GNU gdb (GDB) EulerOS 9.2-1.ky10
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-kylin-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:<http://www.gnu.org/software/gdb/documentation/>.For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from t...
(gdb) b main
Breakpoint 1 at 0x400701: file t.c, line 27.
(gdb) run
Starting program: /root/te/t Breakpoint 1, main () at t.c:27
27      int iRe= BDPceshi(sDPACNO,NULL,&dFREAMT);
(gdb) s
BDPceshi (sDPACNO=0x7fffffffe250 "", sFRENUM=0x0, dFREAMT=0x7fffffffe350) at t.c:7
7       memset(sDPACNO,0,sizeof(sDPACNO));
8       printf("!!!!!!!!!\n");
(gdb) s
!!!!!!!!!
11      sprintf(sDPACNO,"%s",sFRENUM);
(gdb) sProgram received signal SIGSEGV, Segmentation fault.
0x00007ffff7e990d0 in ?? () from /usr/lib64/libc.so.6

 程序执行到这里收到了一个 Segmentation fault(段错误)系统信号并中止了程序的运行。

5. 访问 NULL 指针错误背后的原理

 标准定义了 NULL 指针,它作为一个特殊指针变量,表示不指向任何东西。要使一个指针变量为 NULL,你可以给它赋一个 0 值。为了测试一个指针变量是否为 NULL,你可以将它与 0 值进行比较。之所以选择零这个值是因为一种源代码约定。就机器内部而言,NULL 指针的实际值可能与此不同。在这种情况下,编译器将负责零值和内部值之间的翻译转换。

 如果对一个 NULL 指针进行间接访问会发生什么情况呢?它的结果因编译器而异,在有些机器上,它会访问内存位置 0,编译器能够确保内存位置 0 没有存储任何变量,但机器并未妨碍你访问或修改这个位置(HPUNIX 下的 cc 采用的策略)。这种行为是非常不幸的,因为程序包含了一个错误,但机器却隐匿了它的症状,这样就使这个错误更加难以寻找(“起码看起来像”没问题的由来)。

 在其他机器上(LINUX 下的 gcc 采用的策略),对 NULL 指针进行间接访问将引发一个错误(内核发送 SIGSEGV 信号),并终止程序。宣布这个错误比隐藏这个错误要好得多,因为程序员能够更容易修正它。

 为了进一步验证,我还在 HPUNIX 下安装了 gcc 运行结果和在 LINUX 下如出一辙。

总结

 至此,因程序员在使用 C 语言时,未能遵守相关规定,写下了本可避免潜在的问题,导致此次程序移植难其背后根源终于找到了。但我一点也高兴不起来,因为要排除散落在程序各处这类问题,将是接下来的挑战。为了研究这个问题,我查了很多资料,最大的感悟是风格良好的程序会在指针解引用之前对它进行检查,这种看似麻烦的策略可以节省大量的调试时间,同样不能过度依赖某些编译器的特性(此次 HPUNIX 下的 cc),这会为以后程序的运行、移植埋下隐患。还是那句话:相信我,你节省的那点时间,必将成倍反噬于你。

NULL 指针在不同平台下的表现引发程序报错(C 语言)相关推荐

  1. Linux下解决发布Qt程序报错:it could not find or load the Qt platform plugin “xcb” in “”

    简述 用Qt5.8版本在ubuntu16.04版本下编写Qt应用程序,生成release版本并打包,到另一台无Qt环境的linux系统中运行. 网上通常是按以下几个步骤进行: 1.生成release程 ...

  2. linux64下编译32位程序,报错

    linux64下编译32位程序 gcc -o xxx -m32 xxx.c 遇到问题 /usr/include/gnu/stubs.h:7:27: 致命错误:gnu/stubs-32.h:没有那个文件 ...

  3. mysql安装教程博音网_RTSP视频平台EasyNVR使用mysql数据源启动报错unknow drivermysql优化...

    原标题:RTSP视频平台EasyNVR使用mysql数据源启动报错unknow driver"mysql"优化 我们上一篇讲了TSINGSEE青犀视频开发的视频平台默认都是使用的s ...

  4. php fpm 日志记录,如何解决nginx下php-fpm不记录php报错日志的问题

    如何解决nginx下php-fpm不记录php报错日志的问题 发布时间:2020-07-28 10:17:29 来源:亿速云 阅读:150 作者:Leah 本篇文章为大家展示了如何解决nginx下ph ...

  5. nginx php fpm 日志,nginx下php-fpm不记录php报错日志怎么办?

    解决nginx下php-fpm不记录php报错日志的办法:1.修改[php-fpm.conf]中配置,没有则增加:2.修改[php.ini]中配置,没有则增加:3.重启[php-fpm]即可. 解决n ...

  6. Linux下启动启动tomcat 服务器报错 The file is absent or does not have execute permission

    为什么80%的码农都做不了架构师?>>>    Linux下启动启动tomcat 服务器报错 The file is absent or does not have execute ...

  7. cannot set up a python sdk 3.8_anaconda+pycharm环境下创建新的虚拟环境报错Cannot set up a py...

    anaconda+pycharm环境下创建新的虚拟环境报错Cannot set up a py anaconda+pycharm环境下创建新的虚拟环境报错Cannot set up a python ...

  8. spring mvc项目在IDEA下使用jrebel热部署报错!!

    转载:https://segmentfault.com/q/1010000006068898 spring mvc项目在IDEA下使用jrebel热部署报错!! 25-Jul-2016 20:43:4 ...

  9. centos 7 mysql 中文,解决centOS7 下mysql插入中文字符报错相关问题

    解决centOS7 下mysql插入中文字符报错相关问题 在刚装完mysql,就建立了数据库abc,然后新建一个abc表,插入英文没有问题,但是插入中文就有问题,会报错: ERROR 1366 (HY ...

最新文章

  1. VMware虚拟机搭MAC系统
  2. Android CTS 测试总结【转】
  3. GPU Gems1 - 14 透视阴影贴图(Perspective Shadow Maps: Care and Feeding)
  4. Jdbc连接mysql的五种连接方式
  5. 【转】列“xxx”不属于表 Table asp.net
  6. mysql 绑定 cpu 节点_MySQL Cluster(MySQL集群)配置
  7. php栏目树,php生成无限栏目树
  8. 软件基本功:linux/windows的头文件互相包含,大哥你这是什么创新?
  9. 用【快剪辑】给视频打马赛克
  10. 高斯整数matlab,Matlab---自适应高斯核
  11. 跨平台 H264 H265/HEVC 编解码 硬件加速
  12. 生产排程系统_【聚焦】纸箱世界智能制造纸板纸箱厂的高级计划与排程系统应用...
  13. 在唯一密钥属性“fileExtension”设置为“.json”时,无法添加类型为“mimeMap”的重复集合项...
  14. Geek Challenge
  15. hzhost防asp攻击函数
  16. 缺少所需的CD/DVD驱动器设备驱动程序
  17. LeetCode题解(1564):把箱子放进仓库里I(Python)
  18. 首批部分游戏已完成审核 正在抓紧核发版号
  19. 25岁同济硕士生斩获CVPR 2022 最佳学生论文奖
  20. linux环境怎么更新离线rpm包,SUSE Linux 11系统rpm包离线安装GCC

热门文章

  1. 讯飞智能录音笔SR502:支持OCR识别功能的职场礼物
  2. 后台管理系统——权限管理
  3. Intel无线网卡蓝牙功能失效解决思路分享
  4. 谈谈自己对教育的理解(K12)
  5. Omdia 表示,智能手机出货量下降 12.9%
  6. PC端页面在手机端完整显示
  7. MySQL8高级优化,持续更新......
  8. 有效解决package ‘xxxx‘ is not in GOROOT
  9. 心脏滴血漏洞简单攻击
  10. Scrum立会报告+燃尽图(Beta阶段第五次)