본문 바로가기
모던 C

레벨1 친숙 - 들어가며

by 왕초보 독학 코딩 2024. 2. 3.

이 포스트는 '모던 C'를 요약한 내용입니다.

 

이 레벨은 C 프로그램을 잘 작성하고 사용하는 데 필요한 지식을 제공한다. 여기서 잘 작성한다는 말은 C 언어의 최신 규격을 이해하는 것이며, 초창기 C 언어의 다양한 방언에서 저지르기 쉬운 실수를 피하고, 이식 가능한 새로운 구문을 사용한다는 뜻이다. 

 

준비

C 언어는 제약이 없는 편이다. 다시 말해 프로그래머가 원한다면 자폭에 가까운 일도 할 수 있다. 먼저 몇 가지 제약 사항에 대해 알아보자.

C 언어에서 가장 위험한 구문은 타입 변환(cast)이다. 레벨 1에서는 이에 대한 설명을 생략하고, 우선 빠지기 쉬운 함정 몇 가지를 소개한다. 

이 레벨에서 받아들이기 힘든 내용 중 일부는 다음과 같이 일정한 순서에 따라 점진적으로 소개한다.

  • 정수 타입은 부호 없는(unsigned) 버전 위주로 소개한다.
  • 포인터는 단계를 나눠서 소개한다. 
  • 레벨 1에서는 포인터를 주로 배열 사용 관점에서 살펴본다.

레벨 1의 설명 과정에서 생소한 스타일은 다음과 같다.

 

1. 타입 수정자(type modifier)와 타입 한정자(type qualifier)는 왼쪽에 바인딩된다. 

식별자를 타입과 시각적으로 구분하기 위해 일반적으로 다음과 같이 작성한다. 

char* name;

여기서 char*는 타입이고 name은 식별자이다. 한정자를 적을 때는 왼쪽 바인딩을 적용하여 다음과 같이 표기한다.

char const* const path_name;

첫 번째 const 한정자는 왼쪽에 나온 char에 적용되며 *를 붙여서 포인터로 만들었다. 두 번째 const 한정자도 왼쪽으로 바인딩된다. 따라서 path_name은 문자열 타입(char)인데 상수 포인터(const*)임을 나타내고, 이름 수정한 수 없도록 const로 표기한 것이다.

 

2. 연달아 선언하지 않는다.

타입 선언이 연달아 나오면 바인딩 과정이 모호해진다. 

unsigned const* const a, b;

여기서 b의 타입은 unsinged const다. 다시 말해 첫 번째 const는 타입에 적용되고, 두 번째 const는 a 선언문에 적용된다. 

 

3. 포인터 매개변수를 배열로 표기한다. 

null이 될 수 없는 포인터는 항상 이렇게 표기한다.

/* 인수가 null이 될 수 없음을 강조한다. */
size_t strlen(char const string[static 1]);
int main(int argc, char* argv[argc+1]);

/* 위 함수를 다음과 같이 선언할 수도 있다. */
size_t strlen(const char *string);
int main(int argc, char **argc);

 

첫 번째 문장은 strlen이 null이 아닌 포인터를 받고 string 원소를 최소한 한 개 받는다는 것을 강조한 것이다. 두 번째 문장은 main이 char에 대한 포인터로 구성된 배열을 받는다는 것을 표현한 것이다. 이 배열은 배열의 끝을 나타내는 null 포인터로 구성되기 때문에 argc+1의 크기가 된다.

 

위의 코드를 그대로 작성해도 문제가 되지 않는다. 세 번째와 네 번째 선언문은 이미 컴파일러가 아는 사실과 동일한 선언을 추가했을 뿐이다.

 

4. 함수 포인터 매개변수는 함수처럼 표기한다.

배열과 마찬가지로 함수 포인터도 null이 될 수 없을 때마다 이렇게 표기한다.

/* handler란 인수가 null이 될 수 없음을 강조한 표현 */
int atexit(void handler(void);

/* 같은 내용을 다르게 선언한 문장 */
int atexit(void (*handler));

 

5. 변수 선언문은 그 변수를 처음 사용하는 지점과 최대한 가까이 둔다.

초보 C 프로그래머는 변수(특히 포인터 변수)의 초기화를 깜박하는 실수를 가장 많이 한다. 그래서 가능하면 변수 선언과 값 대입문을 하나로 묶는 것이 좋다. 즉, 초기화 선언을 하는 것이 좋다. 

이 방식은 for 루프를 작성할 때 특히 유용하다. 한 루프에서 사용하는 반복자 변수는 다른 루프에서 사용하는 반복자와는 별개인 오브젝트다. 따라서 이 변수를 for문 안에 선언해서 스코프가 해당 루프를 벗어나지 않도록 한다.

 

6. 코드 블록은 전위 표기법(preifx notation)을 따른다.

코드 블록을 쉽게 이해하려면 목적과 범위를 잘 드러나도록 다음과 같이 표기해야 한다.

  • { 는 항상 첫 문장이나 선언문과 같은 줄에 적는다.
  • 블록 안에 나오는 코드는 한 단계 들여 쓴다.
  • 마지막 }는 새 줄에 쓰고 해당 블록을 시작하는 문장과 들여 쓰기를 맞춘다.
  • } 뒤에 이어지는 블록 문장은 같은 줄에 쓴다.
int main(int argc, char* argv[argc+1]) {
    puts("Hello world!");
    
    if (argc > 1) {
        puts("Argc is bigger then 1");
    } else {
        puts("Argc is 1");
    }
    
    return EXIT_SUCCESS;
}

 

'모던 C' 카테고리의 다른 글

레벨 1 친숙 - 04 계산 표현하기  (0) 2024.02.23
레벨 1 친숙 - 03 결국은 제어  (0) 2024.02.03
레벨0 만남 - 02 프로그램의 핵심 구조  (0) 2024.02.03
레벨0 만남 - 01 들어가며  (0) 2024.02.02
들어가며  (0) 2024.02.02