27. The C Programming Language

27.1. C and C++: Siblings

C와 C++은 형제 관계이다.

27.1.1. C/C++ Compatibility

C/C++라는 언어는 없다. C/C++는 호환성 문제를 얘기할 때만 쓰는 단어로 하자.

27.1.2. C++ features missing from C

C에 없는 C++의 특성 :

  • 클래스와 멤버 함수
  • 파생 클래스와 가상 함수
  • 템플릿
  • 예외
  • 함수 오버로딩
  • new/delete
  • 참조자
  • constexpr
  • static_cast, reinterpret_cast, const_cast

27.1.3. The C standard library

C 표준 라이브러리를 참고하고 싶다면 : https://en.cppreference.com/w/c

27.2. Functions

C에는

  • 참조자가 없다.
  • 멤버 함수가 없다.
  • 함수 오버로딩이 없다.

27.2.1. No function name overloading

C에는 함수 오버로딩이 없다.

27.2.2. Function argument type checking

C89까지는 묵시적 함수 선언이 가능하므로, 이에 주의해야 한다. C99부터는 프로토타입을 쓰자.

27.2.3. Function definitions

함수 프로토타입을 꼭 쓰자.

27.2.4. Calling C from C++ and C++ from C

C 함수를 C++에서 쓸 때나 C++ 함수를 C에서 쓸 때는 extern “C”를 쓴다.

27.2.5. Pointers to functions

C에서 가상 함수와 다형성을 구현하려면 함수 포인터를 쓴다.

typedef void (*Pfct0)(struct Shape2*);
typedef void (*Pfct1int)(struct Shape2*,int);

struct Shape2 {
          Pfct0 draw;
          Pfct1int rotate;
          /* . . . */
};

void draw(struct Shape2* p)
{
          (p–>draw)(p);
}

void rotate(struct Shape2* p, int d)
{
          (p–>rotate)(p,d);
}

27.3. Minor Language Differences

27.3.1. struct tag namespace

C11부터는 typedef struct Struct Struct;라는 전방 선언을 통해 struct Struct 대신 Struct로 쓸 수 있다.

C에는 중첩된 구조체가 같은 스코프를 가진다. C++에서는 아니다.

27.3.2. Keywords

C에는 없는 C++ 키워드가 많이 존재한다.

27.3.3. Definitions

C99부터는 for문 내에서 변수를 정의와 선언을 동시에 할 수 있다.

27.3.4. C-style casts

C 스타일 캐스팅은 간단하지만, 위험하다. 가급적 쓰지 말라.

27.3.5. Conversion of void*

C에서는 void*가 임의의 포인터로/로부터 형변환될 수 있다.

27.3.6. enum

C에는 scoped enum이 없다.

27.3.7. Namespaces

C에는 네임스페이스가 없기 때문에 전통적으로 함수명에 전치사를 붙인다.

27.4. Free Store

C에서는 new/delete 대신 malloc/free를 쓴다. C++에서는 절대로 쓰지 말라.

27.5. C-Style Strings

C 스타일 문자열은 0으로 끝나는 char 배열이다. 매우 쓰기 번거로우므로 C++에서는 std::string을 쓰자.

27.5.1. C-style strings and const

C 스타일 문자열 함수는 const-세이프하지 않음에 유의하라.

27.5.2. Byte operations

memset, memmove, memcpy 등을 C++에서 쓰지 말라.

27.5.3. An example: strcpy()

C 표준 라이브러리의 strcpy()는 다음과 같다.

char* strcpy(char* p, const char* q)
{
          while (*p++ = *q++);
          return p;
}

27.5.4. A style issue

포인터 선언 스타일에 일관성을 유지하자.

27.6. Input/Output : stdio

C에는 std::cin, std::cout 대신 stdin, stdout을 쓴다.

27.6.1. Output

C에서는 printf()을 쓴다. 이는 타입 세이프하지 않다.

27.6.2. Input

gets()나 scanf(“%s”)는 절대 쓰지 말라. gets()는 그래서 삭제되었다. fgets를 쓰라.

27.6.3. Files

C에서는 std::fstream 대신 fopen(), fclose()를 사용한다.

27.7. Constants and Macros

C에서 const는 컴파일 타임 상수가 아니다. 그 대신에 매크로를 쓴다.

27.8. Macros

C에서는 매크로가 많이 쓰인다. C++에서는 가급적 쓰지 말라.

27.8.1. Function-like macros

C에서는 함수형 매크로가 많이 쓰인다. 대신에 inline 함수를 쓰라. 불가피한 경우에는 가변 인자 매크로를 쓰라.

27.8.2. Syntax macros

신택스 매크로를 쓰지 말라.

27.8.3. Conditional compilation

조건부 컴파일을 위해서는 #ifdef를 쓴다. C++로 컴파일할지 C로 컴파일할지는 #ifdef __cplusplus를 쓴다.

27.9. An example: intrusive containers

C++ 표준 컨테이너는 비침입적이다. 여기서는 침입적 컨테이너를 다룬다.

void init(struct List* lst);                  /* initialize lst to empty */
struct List* create();                         /* make a new empty list on free store */
void clear(struct List* lst);               /* free all elements of lst */
void destroy(struct List* lst);         /* free all elements of lst, then free lst */

void push_back(struct List* lst, struct Link* p);        /* add p at end of lst */
void push_front(struct List*, struct Link* p);            /* add p at front of lst */

/* insert q before p in lst: */
void insert(struct List* lst, struct Link* p, struct Link* q);
struct Link* erase(struct List* lst, struct Link* p);  /* remove p from lst */

/* return link n “hops” before or after p: */
struct Link* advance(struct Link* p, int n);
void init(struct List* lst)        /* initialize *lst to the empty list */
{
          assert(lst);
          lst–>first = lst–>last = 0;
}

struct List* create()                 /* make a new empty list */
{
          struct List* lst = (struct List*)malloc(sizeof(struct List));
          init(lst);
          return lst;
}

void clear(struct List* lst)           /* free all elements of lst */
{
          assert(lst);
          {
                    struct Link* curr = lst–>first;
                    while(curr) {
                              struct Link* next = curr–>suc;
                              free(curr);
                              curr = next;
                    }
                    lst–>first = lst–>last = 0;
          }
}

void destroy(struct List* lst)      /* free all elements of lst; then free lst */
{
          assert(lst);
          clear(lst);
          free(lst);
}

void push_back(struct List* lst, struct Link* p)    /* add p at end of lst */
{
          assert(lst);
          {
                    struct Link* last = lst–>last;
                    if (last) {
                              last–>suc = p;                              /* add p after last */
                              p–>pre = last;
                    }
                    else {
                              lst–>first = p;                               /* p is the first element */
                              p–>pre = 0;
                    }
                    lst–>last = p;                       /* p is the new last element */
                    p–>suc = 0;
          }
}

struct Link* erase(struct List* lst, struct Link* p)
/*
          remove p from lst;
         return a pointer to the link after p
*/
{
          assert(lst);
          if (p==0) return 0;                                        /* OK to erase(0) */

          if (p == lst–>first) {
                    if (p–>suc) {
                              lst–>first = p–>suc;                 /* the successor becomes first */
                              p–>suc–>pre = 0;
                              return p–>suc;
                    }
                    else {
                              lst–>first = lst–>last = 0;              /* the list becomes empty */
                              return 0;
                    }
          }
          else if (p == lst–>last) {
                    if (p–>pre) {
                              lst–>last = p–>pre;      /* the predecessor becomes last */
                              p–>pre–>suc = 0;
                    }
                    else {
                              lst–>first = lst–>last = 0;         /* the list becomes empty */
                              return 0;
                    }
          }
          else {
                    p–>suc–>pre = p–>pre;
                    p–>pre–>suc = p–>suc;
                    return p–>suc;
          }
}