Skip to content

Surprises in C code

1. For Loop 와 While Loop#

For Loop#

for(int i=0;i<10;++i){

}

While Loop#

int i=0;
while(i<10){
    ++i;
}
- For 문과 While 문은 동일한 표현이 가능함 - 다음과 같이 For Loop에 다중 Statement도 표기 가능함
for(int i=0,j=10 ;i<j; i++, j--){
    ...
}

2. i++ and ++i#

  • i++ : 변수에 i 값 저장 후 증가
  • ++i : 증가된 값이 저장됨

성능 차이??#

  • C언어가 만들어진 초창기 시절에는 컴파일러가 좋지 않아서 Prefix-Expression 선호했었음
// Prefix Expression
for(int i=0;i<10;++i){
    ...
}

prefix 테스트 코드#

#define MAX 1000000000
int arr[MAX];
int main(){
    for(int i=0;i<MAX;++i){
        arr[i] = i;
    }  
}

어셈블리 코드 (gcc -O0 -S loop1.c)#

  • Prefix / Postfix 동일한 어셈블리 결과물을 출력함
    main:
    .LFB0:
        .cfi_startproc
        endbr64
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    $0, -4(%rbp)
        jmp .L2
    .L3:
        movl    -4(%rbp), %eax
        cltq
        leaq    0(,%rax,4), %rcx
        leaq    arr(%rip), %rdx
        movl    -4(%rbp), %eax
        movl    %eax, (%rcx,%rdx)
        addl    $1, -4(%rbp)
    .L2:
        cmpl    $999999999, -4(%rbp)
        jle .L3
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
    

For Loop 부분#

movl    $0, -4(%rbp)
    jmp .L2
.L3:
    ...
    ...
    addl    $1, -4(%rbp)
.L2:
    cmpl    $999999999, -4(%rbp)
    jle .L3

포인터에서의 Prefix / Postfix#

int arr[5] = {0,0,0,0,0}, arr2[5] = {0,0,0,0,0};
int *ptr = arr, *ptr2 = arr2;

for(int i=0;i<5;++i){
    *(ptr++) = i;
    *(++ptr2) = i
}
image

3. Multiple assignment#

a = b = c = 7; 
d = 7; e = 7; f = 7;
a = b = c = 7; 
// a = (b = (c = 7))
- assignment는 right to left로 판별 image

  • 변수가 아닌 레지스터를 설정하는 경우 Multiple Assignment가 적합하지 않을 수 있음

  • 변수의 데이터 타입이 다를 경우 문제 발생

    int b;
    double a, c;
    a = b = c = 5.2; // 5 5 5.2
DEBUG

    int16_t a, c;
    int8_t b;
    a = b = c = 129; // -127 -127 129

Multiple Assignment 테스트#

테스트 코드#

    int a, b, c,
        d, e, f;
    a = b = c = 7;
    printf("%d", c);
    d = 8, e = 8, f = 8;
    printf("%d", f);

어셈블리 파일 (gcc -S ...)#

main:
.LFB0:
    .cfi_startproc
    endbr64
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $32, %rsp
    movl    $7, -24(%rbp)
    movl    -24(%rbp), %eax
    movl    %eax, -20(%rbp)
    movl    -20(%rbp), %eax
    movl    %eax, -16(%rbp)
    movl    -24(%rbp), %eax
    movl    %eax, %esi
    leaq    .LC0(%rip), %rdi
    movl    $0, %eax
    call    printf@PLT
    movl    $8, -12(%rbp)
    movl    $8, -8(%rbp)
    movl    $8, -4(%rbp)
    movl    -4(%rbp), %eax
    movl    %eax, %esi
    leaq    .LC0(%rip), %rdi
    movl    $0, %eax
    call    printf@PLT
    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc

    ...
    ...

Multiple Assignment#

    a = b = c = 7;
    printf("%d", c);
    movl    $7, -24(%rbp)   // c = 7
    movl    -24(%rbp), %eax
    movl    %eax, -20(%rbp) // b = c
    movl    -20(%rbp), %eax
    movl    %eax, -16(%rbp) // a = b
    movl    -24(%rbp), %eax 
    movl    %eax, %esi
    leaq    .LC0(%rip), %rdi
    movl    $0, %eax
    call    printf@PLT      // print c

Simple Assignment#

    d = 8, e = 8, f = 8;
    printf("%d", f);
    movl    $8, -12(%rbp) // d = 8
    movl    $8, -8(%rbp)  // e = 8
    movl    $8, -4(%rbp)  // f = 8
    movl    -4(%rbp), %eax
    movl    %eax, %esi
    leaq    .LC0(%rip), %rdi
    movl    $0, %eax
    call    printf@PLT // print f

4. Reference#

Surprises in C code