C언어를 사용해 CS:APP의 LAB을 진행하면서 저지르기 쉬운 실수들을 정리해둔 게시물입니다.
아래의 내용은 모두 저자의 강의에서 소개된 예시들을 옮긴 것입니다.

int foo(unsigned int u) {
    return (u > -1) ? 1 : 0;
}

uunsigned 이므로 -1 역시 unsignedimplicit casting 된다.
-1의 binary expression은 111...1이므로 자동으로 UNSIGNED_MAX가 되어 원래의 의도와 다르게 작동하게 된다.

int main() {
    int* a = malloc(100*sizeof(int));
    for (int i=0; i<100 ; i++) {
        a[i] = i / a[i];
    }
    free(a);
    return 0;
}

a를 초기화하지 않았기 때문에 main()의 행동이 정의되지 않은 상태다.
main()이 실행될 때마다 행동이 달라지고, 때로는 zero division error가 발생한다.

int main() {
    char w[strlen("C programming")];
    strcpy(w, "C programming");
    printf("%s\n", w);
    return 0;
}

strlen()은 문자열의 끝에 오는 null은 문자열의 길이로 계산하지 않는다.
따라서, strcpy()가 진행되며 null에 의해 준비된 buffer를 1 byte 초과해서 적게 된다. 즉, buffer overflow.

struc ht_node {
    int key;
    int data;
};
typedef struct ht_node* node;

node makeNode(int k, int e) {
    node curr = malloc(sizeof(node));
    curr->key = k;
    curr->data = e;
    return curr;
}

여기서 nodestruct가 아닌 pointer이다. 그러므로, 32-bit System인지 64-bit System인지에 따라 malloc()이 4 byte 또는 8 byte를 return하게되고, 전자의 경우 해당 공간에 struct의 전체 내용을 담지 못할 수도 있다.
또, 메모리를 할당해줬으면 free()를 통해 메모리를 해제해줘야 한다.

char *strcdup(int n, char c) {
    char dup[n+1];
    int i;
    for (i=0; i<n; i++)
        dup[i] = c;
    dup[i] = '\0';
    char *A = dup;
    return A;
}

위에서 dup 배열을 local variable로 선언했다. 이 경우 해당 함수의 stack frame 내에 원소들이 저장된다.
문제는, 이 함수가 종료되고 나면, stack pointer는 다시 더 높은 메모리 주소를 가리키게 되며 이후 프로그램이 더 작동하며 원래의 dup 배열의 내용이 overwrite 될 수 있다는 점이다.
그럼에도 A는 계속 원래의 dup의 시작주소를 가리키고 있을 것이다. 프로그래머는 dup의 내용물이 변경된 줄 모르고 사용하게 될 위험이 있다.

#define IS_GREATER(a, b) a > b
inline int isGreater(int a, int b) {
    return a > b ? 1 : 0;
}

int m1 = IS_GREATER(1, 0) + 1;
int m2 = isGreater(1, 0) + 1;

원래는 IS_GREATERisGreater()이 같은 작동을 하도록 의도되었을 것이다.
하지만 IS_GREATER는 매크로이다. 괄호로 감싸져 있지 않기 때문에, m1의 경우 1 > 0 + 1로 대체되고, 원래의 의도와 달리 0을 return하게 된다.

#define NEXT_BYTE(a) ((char*) (a+1))
//여기서 NEXT_BYTE(a)의 의도는 a의 시작주소 바로 다음 주소를 return 하는 것이다.
main() {
    int a1 = 54; //&a1 = 0x100 이라 가정한다.
    long long a2 = 42; //&a2 = 0x200 이라 가정한다.
    void* b1 = NEXT_BYTE(&a1); //0x104
    void* b2 = NEXT_BYTE(&a2); //0x108
}

pointer arithmetic에서 발생할 수 있는 문제다. pointer arithmetic은 해당 type의 size에 영향을 받는다. 그러므로, 주석에 적혀있는 의도와 달리 작동하게 되는 것이다. inta1을 가리키는 포인터인 &a1에 1을 더한 &a1 + 1의 결과는 (a1의 주소값) + 0x4와 같다.
원래의 의도대로라면 ((char*)a + 1)로 작성해줘야 한다.

Comments