2014-12-23 3 views
0

나는 런타임시 segfault해야한다는 다음과 같은 생각을 썼다. 그러나 그렇지 않습니다. 정상적으로 실행되며 이유를 이해할 수 없습니다.게재 위치는 어떻게 새로 작동합니까?

#include <cstdlib> 
#include <cstdio> 
#include <new> 

struct MyStruct 
{ 
    double *a; 

    MyStruct() 
     : a(NULL) 
     { printf("Default constructor\n"); } 
    MyStruct(double *b) 
     : a(b) 
     {} 
    MyStruct(const MyStruct& other) 
    { 
     printf("Copy-constructor\n"); 
     if (a != NULL && *a != 3.14) 
      a = other.a; 
    } 
}; 

int main() 
{ 
    double num = 3.14; 
    MyStruct obj(&num); 

    void *ptr = ::operator new(sizeof(MyStruct)); 
    new (ptr) MyStruct(obj); 

    delete (MyStruct*) ptr; // Calls ~MyStruct 
} 

출력은 다음과 같습니다

Copy-constructor 

내가 void *ptr = ::operator new(sizeof(MyStruct));을 쓸 때 나는이 전용 메모리를 할당 알고, 기본 생성자를 호출하지 않아야합니다. 그리고 그것은 다음과 같이 보이지 않습니다.

내가 new (ptr) MyStruct(obj);이라고 쓸 때, 내가 생각한 것처럼 작동한다면 segfault로 예상 할 수 있습니다. 나는 이것이 ((MyStruct*) ptr)->MyStruct(obj)과 같다고 생각한다. a이 초기화되지 않았기 때문에 if (a != NULL && *a != 3.14) 줄은 *a != 3.14에 도달하고 segfault에 도달해야합니다.

내 문제는 a이 초기화되지 않은 것 같습니다 ("기본 생성자"출력이 없었기 때문에). 그러나 이전에는 여전히 segfault가 아닙니다. 내가 뭘 놓치고 있니?

.file "placement_new.cpp" 
    .section .rodata.cst8,"aM",@progbits,8 
    .align 8 
.LCPI0_0: 
    .quad 4614253070214989087  # double 3.1400000000000001 
    .text 
    .globl main 
    .align 16, 0x90 
    .type main,@function 
main:         # @main 
    .cfi_startproc 
# BB#0: 
    push rbp 
.Ltmp2: 
    .cfi_def_cfa_offset 16 
.Ltmp3: 
    .cfi_offset rbp, -16 
    mov rbp, rsp 
.Ltmp4: 
    .cfi_def_cfa_register rbp 
    sub rsp, 48 
    lea rdi, qword ptr [rbp - 24] 
    lea rsi, qword ptr [rbp - 16] 
    movsd xmm0, qword ptr [.LCPI0_0] 
    mov dword ptr [rbp - 4], 0 
    movsd qword ptr [rbp - 16], xmm0 
    call _ZN8MyStructC2EPd 
    movabs rdi, 8 
    call _Znwm 
    mov qword ptr [rbp - 32], rax 
    mov rax, qword ptr [rbp - 32] 
    cmp rax, 0 
    mov qword ptr [rbp - 40], rax # 8-byte Spill 
    je .LBB0_2 
# BB#1: 
    lea rsi, qword ptr [rbp - 24] 
    mov rax, qword ptr [rbp - 40] # 8-byte Reload 
    mov rdi, rax 
    call _ZN8MyStructC2ERKS_ 
.LBB0_2: 
    mov rax, qword ptr [rbp - 32] 
    cmp rax, 0 
    mov qword ptr [rbp - 48], rax # 8-byte Spill 
    je .LBB0_4 
# BB#3: 
    mov rax, qword ptr [rbp - 48] # 8-byte Reload 
    mov rdi, rax 
    call _ZdlPv 
.LBB0_4: 
    mov eax, dword ptr [rbp - 4] 
    add rsp, 48 
    pop rbp 
    ret 
.Ltmp5: 
    .size main, .Ltmp5-main 
    .cfi_endproc 

    .section .text._ZN8MyStructC2EPd,"axG",@progbits,_ZN8MyStructC2EPd,comdat 
    .weak _ZN8MyStructC2EPd 
    .align 16, 0x90 
    .type _ZN8MyStructC2EPd,@function 
_ZN8MyStructC2EPd:      # @_ZN8MyStructC2EPd 
    .cfi_startproc 
# BB#0: 
    push rbp 
.Ltmp8: 
    .cfi_def_cfa_offset 16 
.Ltmp9: 
    .cfi_offset rbp, -16 
    mov rbp, rsp 
.Ltmp10: 
    .cfi_def_cfa_register rbp 
    mov qword ptr [rbp - 8], rdi 
    mov qword ptr [rbp - 16], rsi 
    mov rsi, qword ptr [rbp - 8] 
    mov rdi, qword ptr [rbp - 16] 
    mov qword ptr [rsi], rdi 
    pop rbp 
    ret 
.Ltmp11: 
    .size _ZN8MyStructC2EPd, .Ltmp11-_ZN8MyStructC2EPd 
    .cfi_endproc 

    .section .rodata.cst8,"aM",@progbits,8 
    .align 8 
.LCPI2_0: 
    .quad 4614253070214989087  # double 3.1400000000000001 
    .section .text._ZN8MyStructC2ERKS_,"axG",@progbits,_ZN8MyStructC2ERKS_,comdat 
    .weak _ZN8MyStructC2ERKS_ 
    .align 16, 0x90 
    .type _ZN8MyStructC2ERKS_,@function 
_ZN8MyStructC2ERKS_:     # @_ZN8MyStructC2ERKS_ 
    .cfi_startproc 
# BB#0: 
    push rbp 
.Ltmp14: 
    .cfi_def_cfa_offset 16 
.Ltmp15: 
    .cfi_offset rbp, -16 
    mov rbp, rsp 
.Ltmp16: 
    .cfi_def_cfa_register rbp 
    sub rsp, 32 
    lea rax, qword ptr [.L.str] 
    mov qword ptr [rbp - 8], rdi 
    mov qword ptr [rbp - 16], rsi 
    mov rsi, qword ptr [rbp - 8] 
    mov rdi, rax 
    mov al, 0 
    mov qword ptr [rbp - 24], rsi # 8-byte Spill 
    call printf 
    mov rsi, qword ptr [rbp - 24] # 8-byte Reload 
    cmp qword ptr [rsi], 0 
    mov dword ptr [rbp - 28], eax # 4-byte Spill 
    je .LBB2_3 
# BB#1: 
    movsd xmm0, qword ptr [.LCPI2_0] 
    mov rax, qword ptr [rbp - 24] # 8-byte Reload 
    mov rcx, qword ptr [rax] 
    movsd xmm1, qword ptr [rcx] 
    ucomisd xmm1, xmm0 
    jne .LBB2_2 
    jp .LBB2_2 
    jmp .LBB2_3 
.LBB2_2: 
    mov rax, qword ptr [rbp - 16] 
    mov rax, qword ptr [rax] 
    mov rcx, qword ptr [rbp - 24] # 8-byte Reload 
    mov qword ptr [rcx], rax 
.LBB2_3: 
    add rsp, 32 
    pop rbp 
    ret 
.Ltmp17: 
    .size _ZN8MyStructC2ERKS_, .Ltmp17-_ZN8MyStructC2ERKS_ 
    .cfi_endproc 

    .type .L.str,@object   # @.str 
    .section .rodata.str1.1,"aMS",@progbits,1 
.L.str: 
    .asciz "Copy-constructor\n" 
    .size .L.str, 18 


    .ident "Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4)" 
    .section ".note.GNU-stack","",@progbits 
+1

릴리스 버전을 컴파일하고 다시 시도하십시오. – Matt

+4

'내가 무엇을 놓치고 있니? 'C++에서 열악한 일을 할 때 프로그램이 중단된다는 것을 의미하지 않는다는 단순한 사실을 놓치고 있습니다. – PaulMcKenzie

+2

이 코드에는 정의되지 않은 동작이 있습니다. 즉, 'delete'가 없으며 'new'라는 배치로 생성 된 객체입니다. 그것을 파괴하고 메모리를 따로 분리해야합니다 :'mptr-> ~ MyStruct(); operator delete (ptr);'(여기서'mptr'은'new'에서 반환 된 MyStruct *입니다). –

답변

2

정의되지 않은 동작입니다 초기화되지 않은 변수를 읽기 : 여기


는 생성 된 어셈블리 코드 (나는 그것을 읽는 방법을 모르는)입니다. 컴파일러가 할당 전에 포인터의 값을 NULL로 설정하면 복사 생성자의 if 문에 short-circuit이 발생하므로 *a이 절대로 실행되지 않습니다.

+0

이것은 런타임에 발생하는 것으로 보입니다. 그러나이 동작이 컴파일되었는지 여부를 확인하고 싶습니다. 아니면 그냥 우연히 어셈블리 코드를 도울 수있는 기회입니까? – Sheljohn

+0

@ Sh3ljohn 어셈블리 코드에서 볼 수 없습니다. C 런타임 (특히 malloc의 구현)에서 숨겨 지거나 더욱 깊어 질 수 있습니다. 예를 들어, Windows에서 할당 된 메모리가 새로운 메모리 페이지에 있으면 그 뒤에있는 실제 RAM은 완전히 다른 과정. –

+0

@SebastianRedl 감사합니다. 이것이 체계적인지 아닌지를 아는 것이 좋을 것입니다. – Sheljohn

1

게재 위치 new에 대한 호출은 정상입니다. 잘못된 것이 무엇이겠습니까? 객체를 배치하기에 충분한 메모리를 확보합니다. 물론 매개 변수로 obj을 사용하여 복사 생성자를 사용해야합니다. 그러나이 출력이 나타나는지 여부는 공중에 올라와 있지 않습니다. printf()은 메모리를 버퍼링하고이 구성 후 delete (MyStruct*)ptr;을 호출하여 정의되지 않은 동작을 발생시킵니다. 즉, 비 - 배치 new으로 얻지 못한 포인터에서 코드가 쉽게 충돌 할 수 있습니다 라이브러리가 버퍼를 플러시하기 전에 (복사 생성자가 내 시스템에서 사용되었음을 출력한다). 제대로 개체를 파괴하는

은이 같은 것을 사용할 필요가 :

MyStruct* mptr = new(ptr) MyStruct(obj); 
mptr->~MyStrucT(); 
operator delete(ptr); 

는 사실, 또한 복사 건설 기간 동안 a의 회원에 대한 정의되지 않은 동작이 : 회원이 암시 적으로 복사되지 않습니다. 즉, 복사 생성자에서 초기화되지 않은 메모리에 액세스합니다. 복사 생성자는 원하는 모든 작업을 수행 할 수 있습니다.

+0

이것은 (아무것도 확인하지 않았지만) 아무것도 변경하지 않아야합니다. 파괴를 위해 할 일이 없습니다. Cf. 내 편집,'delete' 문 앞에 파기를 추가했습니다. – Sheljohn

+0

@ Sh3ljohn : 물론 'a'는 복사 생성자에 의해 초기화되지 않습니다. 왜 그것이 호출 될 것이라고 생각합니까? 복사 생성자는 멤버를 초기화하지만 초기화는 수행하지 않습니다. MyStruct (MyStruct const & other) : MyStruct() {...}'를 쓰고 싶습니까? –

+0

아니,이 때문에 나는 프로그램이 추락 할 것이라고 기대하고있다. 나는 "정의되지 않은 행동"의 범위를 완전히 이해하고 있는지 확신 할 수 없다. 내 코드가 의미가 없다는 말은 마술 적 단어가 아닙니다. 코드가 컴파일되고 실행되며, 작성된 내용이 실행됩니다. '* a! = 3.14 '이면 초기화되지 않은 포인터를 역 참조하려고 시도해야합니다. 이것은 segfault해야합니다. – Sheljohn