为什么分段错误是由类变量顺序引起的

Why segmentation fault is caused by class variables order?

本文关键字:顺序 类变量 分段 错误 为什么      更新时间:2023-10-16

>我创建了以下程序:

class CLexer
{
public:
  CLexer( ) {
    iCursorPos = 0;
  }
  void putCharacter(char character)
  {
    if(character != ' ' && character != 'n') {
      m_strToken[iCursorPos] = character;
      iCursorPos++;
    }
    else {
      m_strToken[iCursorPos] = '';
      iCursorPos = 0;
    }
  }
private:
  char m_strToken[1024];
  int iCursorPos = 0;
};
int main(int argc, char * argv[]) {
  CLexer lex;
  lex.putCharacter('m');
  return 0;
}

编译器产生的汇编器输出:

    .file   "main.cpp"
    .section    .text._ZN6CLexerC2Ev,"axG",@progbits,_ZN6CLexerC5Ev,comdat
    .align 2
    .weak   _ZN6CLexerC2Ev
    .type   _ZN6CLexerC2Ev, @function
_ZN6CLexerC2Ev:
.LFB1:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movq    %rdi, -8(%rbp)
    movq    -8(%rbp), %rax
    movl    $0, 1024(%rax)
    movq    -8(%rbp), %rax
    movl    $0, 1024(%rax)
    nop
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE1:
    .size   _ZN6CLexerC2Ev, .-_ZN6CLexerC2Ev
    .weak   _ZN6CLexerC1Ev
    .set    _ZN6CLexerC1Ev,_ZN6CLexerC2Ev
    .section    .text._ZN6CLexer12putCharacterEc,"axG",@progbits,_ZN6CLexer12putCharacterEc,comdat
    .align 2
    .weak   _ZN6CLexer12putCharacterEc
    .type   _ZN6CLexer12putCharacterEc, @function
_ZN6CLexer12putCharacterEc:
.LFB3:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movq    %rdi, -8(%rbp)
    movl    %esi, %eax
    movb    %al, -12(%rbp)
    cmpb    $32, -12(%rbp)
    je  .L3
    cmpb    $10, -12(%rbp)
    je  .L3
    movq    -8(%rbp), %rax
    movl    1024(%rax), %eax
    movq    -8(%rbp), %rdx
    cltq
    movzbl  -12(%rbp), %ecx
    movb    %cl, (%rdx,%rax)
    movq    -8(%rbp), %rax
    movl    1024(%rax), %eax
    leal    1(%rax), %edx
    movq    -8(%rbp), %rax
    movl    %edx, 1024(%rax)
    jmp .L4
.L3:
    movq    -8(%rbp), %rax
    movl    1024(%rax), %eax
    movq    -8(%rbp), %rdx
    cltq
    movb    $0, (%rdx,%rax)
    movq    -8(%rbp), %rax
    movl    $0, 1024(%rax)
.L4:
    nop
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE3:
    .size   _ZN6CLexer12putCharacterEc, .-_ZN6CLexer12putCharacterEc
    .text
    .globl  main
    .type   main, @function
main:
.LFB4:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $1056, %rsp
    movl    %edi, -1044(%rbp)
    movq    %rsi, -1056(%rbp)
    leaq    -1040(%rbp), %rax
    movq    %rax, %rdi
    call    _ZN6CLexerC1Ev
    leaq    -1040(%rbp), %rax
    movl    $109, %esi
    movq    %rax, %rdi
    call    _ZN6CLexer12putCharacterEc
    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE4:
    .size   main, .-main
    .ident  "GCC: (GNU) 6.1.1 20160501"
    .section    .note.GNU-stack,"",@progbits

执行后,第一次调用以"m"字符作为参数的 putCharacter 方法会抛出段错误。附加的 gdb 给出以下输出:

Program received signal SIGSEGV, Segmentation fault.
0x00000000004018e5 in CLexer::putCharacter (this=0x7fffffffe370, 
    character=109 'm') at src/main.cpp:60
60        m_strToken[iCursorPos] = character;

我已经设法通过在类声明中将 iCursorPos 变量移动到m_strToken以上来修复此错误,但我认为这不是解决此问题的正确方法。

我在最新和更新的ArchLinux x86_64版本上使用g++ (GCC) 6.1.1 20160501

if(character != ' ' && character != 'n') {
  m_strToken[iCursorPos] = character;
  iCursorPos++;
}

你不在这里检查iCursorPos < 1024。因此,您越过缓冲区的末尾写入iCursorPos本身。

下一个访问m_strToken[iCursorPos] = character;可能会写入缓冲区的末尾,并且您得到一个段错误(幸运的是)。

您的"修复"仍然不正确,因为无论如何您都会损坏对象内存的其他部分。