88
선선선선선선 (DCL) 선선선선선선선 선선선

선언과초기화 (DCL)

Embed Size (px)

DESCRIPTION

선언과초기화 (DCL). 사이버보안학과 김현성. 제안. DCL00-C 변하지 않는 객체는 const 로 보장해둬라 DCL01-C 내부 스코프에서 변수 이름을 재사용하지 마라 DCL02-C 시각적으로 구별되는 식별자를 사용하라 DCL03-C 상수 수식의 값을 테스트할 때 정적 어썰션을 사용하라 DCL04-C 한 번에 여러 변수를 선언하지 마라 DCL05-C 코드의 가독성을 높이기 위해 타입 정의를 사용하라 DCL06-C 프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는 심볼릭 상수를 사용하라 - PowerPoint PPT Presentation

Citation preview

Page 1: 선언과초기화 (DCL)

선언과초기화(DCL)

사이버보안학과 김현성

Page 2: 선언과초기화 (DCL)

2/88

제안 DCL00-C 변하지 않는 객체는 const 로 보장해둬라 DCL01-C 내부 스코프에서 변수 이름을 재사용하지 마라 DCL02-C 시각적으로 구별되는 식별자를 사용하라 DCL03-C 상수 수식의 값을 테스트할 때 정적 어썰션을 사용하라 DCL04-C 한 번에 여러 변수를 선언하지 마라 DCL05-C 코드의 가독성을 높이기 위해 타입 정의를 사용하라 DCL06-C 프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는

심볼릭 상수를 사용하라 DCL07-C 함수 선언 시 적절한 타입 정보를 포함시켜라 DCL08-C 상수 정의에서는 상수 간의 관계가 적절하게 나타나도록

정의하라 DCL09-C errno 에러 코드를 반환하는 함수의 반환 타입을 errno_t

로 정의하라

Page 3: 선언과초기화 (DCL)

3/88

제안 DCL10-C 가변 인자를 가진 함수에서는 함수 작성자와 함수 사용자

간의 약속이 지켜져야 한다 DCL11-C 가변 인자 함수와 연관된 타입 문제를 파악하고 있어야 한다 DCL12-C 불투명한 타입을 사용해 추상 데이터 타입을 구현하라 DCL13-C 함수에 의해 바뀌지 않을 값에 대한 포인터를 함수의

매개변수로 사용할 때는 const 로 정의하라 DCL14-C 여러 컴파일 단위를 거치는 전역 변수 초기화의 순서에

대해서는 어떤 가정도 하지 마라 DCL15-C 현재 범위를 넘어서까지 사용되지 않을 객체는 static 으로

선언하라

Page 4: 선언과초기화 (DCL)

규칙

DCL30-C 객체를 선언할 때 적절한 지속공간을 지정하라 DCL31-C 식별자를 사용하기 전에 먼저 선언하라 DCL32-C 서로에게 보이는 식별자가 유일한지를 보장하라 DCL33-C 함수 인자에서 restrict 로 지정된 소스 포인터와

목적 포인터가 동일한 객체를 참조하지 않게 하라 DCL34-C 캐시될 수 없는 데이터에는 volatile 을 사용하라 DCL35-C 함수 정의와 맞지 않는 타입으로 함수를 변환하지

마라 DCL36-C 링크 분류에서 충돌되는 식별자를 선언하지 마라

4/88

Page 5: 선언과초기화 (DCL)

5/88

제안 DCL00-C 변 하 지 않 는 객 체 는 const 로

보장해둬라 DCL01-C 내부 스코프에서 변수 이름을 재사용하지 마라 DCL02-C 시각적으로 구별되는 식별자를 사용하라 DCL03-C 상수 수식의 값을 테스트할 때 정적 어썰션을 사용하라 DCL04-C 한 번에 여러 변수를 선언하지 마라 DCL05-C 코드의 가독성을 높이기 위해 타입 정의를 사용하라 DCL06-C 프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는 심볼릭

상수를 사용하라 DCL07-C 함수 선언 시 적절한 타입 정보를 포함시켜라 DCL08-C 상수 정의에서는 상수 간의 관계가 적절하게 나타나도록 정의하라 DCL09-C errno 에러 코드를 반환하는 함수의 반환 타입을 errno_t 로

정의하라

Page 6: 선언과초기화 (DCL)

DCL00-C 부적절한 코드 예

float pi = 3.14159f;float degrees;float radians;/* ... */radians = degrees * pi / 180;

6/88

=> pi 가 수정되지 못하도록 보호되어야함

=> pi=3.14159f;

변하지 않는 객체는 const 로 보장해둬라

Page 7: 선언과초기화 (DCL)

DCL00-C 해결방법

const float pi = 3.14159f;float degrees;float radians;/* ... */radians = degrees * pi / 180;

7/88

변하지 않는 객체는 const 로 보장해둬라

Page 8: 선언과초기화 (DCL)

8/88

제안 DCL00-C 변하지 않는 객체는 const 로 보장해둬라

DCL01-C 내 부 스 코 프 에 서 변 수 이 름 을 재사용하지 마라

DCL02-C 시각적으로 구별되는 식별자를 사용하라 DCL03-C 상수 수식의 값을 테스트할 때 정적 어썰션을 사용하라 DCL04-C 한 번에 여러 변수를 선언하지 마라 DCL05-C 코드의 가독성을 높이기 위해 타입 정의를 사용하라 DCL06-C 프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는 심볼릭

상수를 사용하라 DCL07-C 함수 선언 시 적절한 타입 정보를 포함시켜라 DCL08-C 상수 정의에서는 상수 간의 관계가 적절하게 나타나도록 정의하라 DCL09-C errno 에러 코드를 반환하는 함수의 반환 타입을 errno_t 로

정의하라

Page 9: 선언과초기화 (DCL)

개념적 변수 스코프 룰

전역변수가 사용될 수 있는 범위 내에서 어떤 변수든 중복해서 전역 변수 이름을 사용하면 안된다

어떤 블록 안에서 이미 사용되고 있는 변수와 동일한 이름으로 다른 블록에서 선언하면 안된다

9/88

내부 스코프에서 변수 이름을 재사용하지 마라

Page 10: 선언과초기화 (DCL)

DCL01-C 부적절한 코드 예char msg[100];

void report_error(const char *error_msg){char msg[80];/* ... */strncpy(msg, error_msg, sizeof(msg));return;

}int main(void){

char error_msg[80];/* ... */report_error(error_msg);/* ... */

}

10/88

내부 스코프에서 변수 이름을 재사용하지 마라

char msg[80];

char msg[100];

strncpy(msg, error_msg, sizeof(msg));

=> 의도하지 않은 값이 복사됨 , 버퍼오버플로우

Page 11: 선언과초기화 (DCL)

DCL01-C 해결방법char system_msg[100];

void report_error(const char *error_msg){ char default_msg[80]; /* ... */ if (error_msg) strncpy(system_msg, error_msg, sizeof(system_msg)); else strncpy(system_msg, default_msg, sizeof(system_msg)); return;}int main(void){

char error_msg[80];/* ... */report_error(error_msg);/* ... */

}

11/88

내부 스코프에서 변수 이름을 재사용하지 마라

Page 12: 선언과초기화 (DCL)

12/88

제안 DCL00-C 변하지 않는 객체는 const 로 보장해둬라 DCL01-C 내부 스코프에서 변수 이름을 재사용하지 마라

DCL02-C 시 각 적 으 로 구 별 되 는 식 별 자 를 사용하라

DCL03-C 상수 수식의 값을 테스트할 때 정적 어썰션을 사용하라 DCL04-C 한 번에 여러 변수를 선언하지 마라 DCL05-C 코드의 가독성을 높이기 위해 타입 정의를 사용하라 DCL06-C 프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는 심볼릭

상수를 사용하라 DCL07-C 함수 선언 시 적절한 타입 정보를 포함시켜라 DCL08-C 상수 정의에서는 상수 간의 관계가 적절하게 나타나도록 정의하라 DCL09-C errno 에러 코드를 반환하는 함수의 반환 타입을 errno_t 로

정의하라

Page 13: 선언과초기화 (DCL)

비슷해 보이는 식별자

1( 하나 ) 과 l( 소문자 엘 ) 0( 영 ) 과 O( 대문자 O) 2( 둘 ) 와 Z( 대문자 Z) 5( 다섯 ) 와 S( 대문자 S) 8( 여덟 ) 과 B( 대문자 B)

13/88

시각적으로 구별되는 식별자를 사용하라

Page 14: 선언과초기화 (DCL)

14/88

제안 DCL00-C 변하지 않는 객체는 const 로 보장해둬라 DCL01-C 내부 스코프에서 변수 이름을 재사용하지 마라 DCL02-C 시각적으로 구별되는 식별자를 사용하라

DCL03-C 상수 수식의 값을 테스트할 때 정적 어썰션을 사용하라

DCL04-C 한 번에 여러 변수를 선언하지 마라 DCL05-C 코드의 가독성을 높이기 위해 타입 정의를 사용하라 DCL06-C 프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는 심볼릭

상수를 사용하라 DCL07-C 함수 선언 시 적절한 타입 정보를 포함시켜라 DCL08-C 상수 정의에서는 상수 간의 관계가 적절하게 나타나도록 정의하라 DCL09-C errno 에러 코드를 반환하는 함수의 반환 타입을 errno_t 로

정의하라

Page 15: 선언과초기화 (DCL)

어썰션 (assert)

assert() 는 소프트웨어 결점을 찾아 제거하는데 사용되는 진단도구– If(index<0||index>=size) {

printf(“ 배열 인덱스 범위 오류” ); }– assert(index>=0&&index<size, “ 배열 인덱스 범위 오

류” );

assert() 는 인자값이 0 이면 메시지 출력– assert((index>=0&&index<size), ” 배열 인덱스 범위 오류” );

15/88

상수 수식의 값을 테스트할 때 정적 어썰션을 사용하라

Page 16: 선언과초기화 (DCL)

DCL03-C 부적절한 코드 예

struct timer{unit8_t MODE;unit32_t DATA;unit32_t COUNT;

};int func(void) {

assert(offsetof(timer, DATA)==4);}

16/88

상수 수식의 값을 테스트할 때 정적 어썰션을 사용하라

=> func() 가 호출되어야만 assert 실행됨

<= offsetof(n,a)n 구조체의 a 멤버의 바이트 반환

Page 17: 선언과초기화 (DCL)

DCL03-C 해결방법

struct timer{unit8_t MODE;unit32_t DATA;unit32_t COUNT;

};#if(assert(offsetof(timer, DATA) !=4))

#error “DATA must be at offset 4”#endif

17/88

상수 수식의 값을 테스트할 때 정적 어썰션을 사용하라

=> error 지시자 사용시 컴파일타임에 어썰션 평가

Page 18: 선언과초기화 (DCL)

18/88

제안 DCL00-C 변하지 않는 객체는 const 로 보장해둬라 DCL01-C 내부 스코프에서 변수 이름을 재사용하지 마라 DCL02-C 시각적으로 구별되는 식별자를 사용하라 DCL03-C 상수 수식의 값을 테스트할 때 정적 어썰션을 사용하라

DCL04-C 한 번에 여러 변수를 선언하지 마라 DCL05-C 코드의 가독성을 높이기 위해 타입 정의를 사용하라 DCL06-C 프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는

심볼릭 상수를 사용하라 DCL07-C 함수 선언 시 적절한 타입 정보를 포함시켜라 DCL08-C 상수 정의에서는 상수 간의 관계가 적절하게 나타나도록

정의하라 DCL09-C errno 에러 코드를 반환하는 함수의 반환 타입을 errno_t 로

정의하라

Page 19: 선언과초기화 (DCL)

DCL04-C 부적절한 코드 예

char *src = 0, c = 0;

19/88

한 번에 여러 변수를 선언하지 마라

=> char *src, char c=> char *src, char *c

Page 20: 선언과초기화 (DCL)

DCL04-C 해결방법

char *src;char *c;

20/88

=> 개별 행으로 변수를 선언

한 번에 여러 변수를 선언하지 마라

Page 21: 선언과초기화 (DCL)

21/88

제안 DCL00-C 변하지 않는 객체는 const 로 보장해둬라 DCL01-C 내부 스코프에서 변수 이름을 재사용하지 마라 DCL02-C 시각적으로 구별되는 식별자를 사용하라 DCL03-C 상수 수식의 값을 테스트할 때 정적 어썰션을 사용하라 DCL04-C 한 번에 여러 변수를 선언하지 마라

DCL05-C 코드의 가독성을 높이기 위해 타입 정의를 사용하라

DCL06-C 프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는 심볼릭 상수를 사용하라

DCL07-C 함수 선언 시 적절한 타입 정보를 포함시켜라 DCL08-C 상수 정의에서는 상수 간의 관계가 적절하게 나타나도록 정의하라 DCL09-C errno 에러 코드를 반환하는 함수의 반환 타입을 errno_t 로

정의하라

Page 22: 선언과초기화 (DCL)

DCL05-C 부적절한 코드 예

void (*signal(int, void(*) (int))) (int);

22/88

=> signal() 함수 선언은 이해하기 힘듦

코드의 가독성을 높이기 위해 타입 정의를 사용하라

Page 23: 선언과초기화 (DCL)

DCL05-C 해결방법

typedef void (*SighandlerType) (int signum);extern SighandlerType signal(

int signum,SighandlerType handler

);

23/88

코드의 가독성을 높이기 위해 타입 정의를 사용하라

<= extern 다른 소스코드 .c에서변수나 함수를 현재파일에서 접근하거나 호출할 때 사용

Page 24: 선언과초기화 (DCL)

24/88

제안 DCL00-C 변하지 않는 객체는 const 로 보장해둬라 DCL01-C 내부 스코프에서 변수 이름을 재사용하지 마라 DCL02-C 시각적으로 구별되는 식별자를 사용하라 DCL03-C 상수 수식의 값을 테스트할 때 정적 어썰션을 사용하라 DCL04-C 한 번에 여러 변수를 선언하지 마라 DCL05-C 코드의 가독성을 높이기 위해 타입 정의를 사용하라

DCL06-C 프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는 심볼릭 상수를 사용하라

DCL07-C 함수 선언 시 적절한 타입 정보를 포함시켜라 DCL08-C 상수 정의에서는 상수 간의 관계가 적절하게 나타나도록

정의하라 DCL09-C errno 에러 코드를 반환하는 함수의 반환 타입을 errno_t 로

정의하라

Page 25: 선언과초기화 (DCL)

상수

정수형 상수– 10– 0x1C

부동소수점 상수– 1.0– 6.022e+23

문자상수– ‘a’– ‘\x10’

25/88

프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는 심볼릭 상수를 사용하라

문자 리터럴– “hello, world”– “\n”

<= 심볼릭 상수를 통해 적절한 이름을 붙여 코드의 명확성을 제시하는게 좋음<= const or 열거형 상수 or 객체형 매크로

Page 26: 선언과초기화 (DCL)

상수 지정

const – const unsigned int buffer_size = 256;– 컴파일 타임에 정수형 상수가 필요할 때 사용 불가

열거형 상수– enum { max = 15 }; int a[max];

객체형 매크로– #define buffer_size 256

26/88

프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는 심볼릭 상수를 사용하라

Page 27: 선언과초기화 (DCL)

DCL06-C 부적절한 코드 예

/* ... */if(age >= 18){

/* 작업 진행 */}else {

/* 다른 작업 진행 */}/* ... */

27/88

프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는 심볼릭 상수를 사용하라

=> 정수 리터럴 18 의 의미가 모호함

18

Page 28: 선언과초기화 (DCL)

DCL06-C 해결방법

enum { ADULT_AGE=18 };/* ... */if(age >= ADULT_AGE){

/* 작업 진행 */}else {

/* 다른 작업 진행 */}/* ... */

28/88

프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는 심볼릭 상수를 사용하라

Page 29: 선언과초기화 (DCL)

29/88

제안 DCL00-C 변하지 않는 객체는 const 로 보장해둬라 DCL01-C 내부 스코프에서 변수 이름을 재사용하지 마라 DCL02-C 시각적으로 구별되는 식별자를 사용하라 DCL03-C 상수 수식의 값을 테스트할 때 정적 어썰션을 사용하라 DCL04-C 한 번에 여러 변수를 선언하지 마라 DCL05-C 코드의 가독성을 높이기 위해 타입 정의를 사용하라 DCL06-C 프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는 심볼릭

상수를 사용하라

DCL07-C 함수 선언 시 적절한 타입 정보를 포함시켜라

DCL08-C 상수 정의에서는 상수 간의 관계가 적절하게 나타나도록 정의하라 DCL09-C errno 에러 코드를 반환하는 함수의 반환 타입을 errno_t 로

정의하라

Page 30: 선언과초기화 (DCL)

DCL07-C 부적절한 코드 예

int max(a, b)int a, b;{

return a>b ? a : b;}

30/88

함수 선언 시 적절한 타입 정보를 포함시켜라

=> 매개변수와 변수 선언을 별개로 가짐

Page 31: 선언과초기화 (DCL)

DCL07-C 해결방법

int max(int a, int b){

return a>b ? a : b;}

31/88

함수 선언 시 적절한 타입 정보를 포함시켜라

Page 32: 선언과초기화 (DCL)

32/88

제안 DCL00-C 변하지 않는 객체는 const 로 보장해둬라 DCL01-C 내부 스코프에서 변수 이름을 재사용하지 마라 DCL02-C 시각적으로 구별되는 식별자를 사용하라 DCL03-C 상수 수식의 값을 테스트할 때 정적 어썰션을 사용하라 DCL04-C 한 번에 여러 변수를 선언하지 마라 DCL05-C 코드의 가독성을 높이기 위해 타입 정의를 사용하라 DCL06-C 프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는 심볼릭

상수를 사용하라 DCL07-C 함수 선언 시 적절한 타입 정보를 포함시켜라

DCL08-C 상수 정의에서는 상수 간의 관계가 적절하게 나타나도록 정의하라

DCL09-C errno 에러 코드를 반환하는 함수의 반환 타입을 errno_t 로 정의하라

Page 33: 선언과초기화 (DCL)

DCL08-C 부적절한 코드 예

코 드 에 서 OUT_STR_LEN 이 항 상 IN_STR_LEN 보다 두개 커야 하는 관계

enum { IN_STR_LEN=18, OUT_STR_LEN=20 };

33/88

상수 정의에서는 상수 간의 관계가 적절하게 나타나도록 정의하라

Page 34: 선언과초기화 (DCL)

DCL08-C 해결방법

enum { IN_STR_LEN=18, OUT_STR_LEN=IN_STR_LEN+2 };

34/88

상수 정의에서는 상수 간의 관계가 적절하게 나타나도록 정의하라

Page 35: 선언과초기화 (DCL)

35/88

제안 DCL00-C 변하지 않는 객체는 const 로 보장해둬라 DCL01-C 내부 스코프에서 변수 이름을 재사용하지 마라 DCL02-C 시각적으로 구별되는 식별자를 사용하라 DCL03-C 상수 수식의 값을 테스트할 때 정적 어썰션을 사용하라 DCL04-C 한 번에 여러 변수를 선언하지 마라 DCL05-C 코드의 가독성을 높이기 위해 타입 정의를 사용하라 DCL06-C 프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는 심볼릭

상수를 사용하라 DCL07-C 함수 선언 시 적절한 타입 정보를 포함시켜라 DCL08-C 상수 정의에서는 상수 간의 관계가 적절하게 나타나도록 정의하라

DCL09-C errno 에러 코드를 반환하는 함수의 반환 타입을 errno_t 로 정의하라

Page 36: 선언과초기화 (DCL)

에러코드 함수 반환 타입

errno 를 반 환 하 는 많 은 함수가 반환 타입을 int 로 선언하거나 에러 상태 값을 반환

errno_t 타 입 은 errno에서 찾을 수 있는 값들을 갖고 있는 객체의 타입으로 사용되어야 함

36/88

errno 에러 코드를 반환하는 함수의 반환 타입을 errno_t 로 정의하라

errno: 0 message: Successerrno: 1 message: Operation not permittederrno: 2 message: No such file or directoryerrno: 3 message: No such processerrno: 4 message: Interrupted system callerrno: 5 message: Input/output errorerrno: 6 message: No such device or address…errno: 125 message: Operation canceled

Page 37: 선언과초기화 (DCL)

DCL09-C 부적절한 코드 예enum { NO_FILE_POS_VALUES = 3 };int operator(FILE *file,int *width,int *height,int *data_offset){

int file_w;int file_h;int file_o;fpos_t offset;

if(file==NULL) {return EINVAL;}errno=0;if(fgetpos(file, &offset)!=0) {return errno;}if(fscanf(file, “%i %i %i”, &file_w, &file_h, &file_o)

!= NO_FILE_POS_VALUES){ return EIO;}

37/88

errno 에러 코드를 반환하는 함수의 반환 타입을 errno_t 로 정의하라

=> 반환형 int not errno_t 형

int

EINVAL

errno

EIO

Page 38: 선언과초기화 (DCL)

DCL09-C 해결방법#include <errno.h>enum { NO_FILE_POS_VALUES = 3 };errno_t operator(FILE *file,int *width,int *height,int *data_offset){

int file_w; int file_h; int file_o; fpos_t offset;

if(file==NULL) {return EINVAL;}errno=0;if(fgetpos(file, &offset)!=0) {return errno;}if(fscanf(file, “%i %i %i”, &file_w, &file_h, &file_o)

!= NO_FILE_POS_VALUES){ return EIO;}

38/88

errno 에러 코드를 반환하는 함수의 반환 타입을 errno_t 로 정의하라

Page 39: 선언과초기화 (DCL)

39/88

제안 DCL10-C 가변 인자를 가진 함수에서는

함수 작성자와 함수 사용자 간의 약속이 지켜져야 한다

DCL11-C 가변 인자 함수와 연관된 타입 문제를 파악하고 있어야 한다 DCL12-C 불투명한 타입을 사용해 추상 데이터 타입을 구현하라 DCL13-C 함수에 의해 바뀌지 않을 값에 대한 포인터를 함수의

매개변수로 사용할 때는 const 로 정의하라 DCL14-C 여러 컴파일 단위를 거치는 전역 변수 초기화의 순서에

대해서는 어떤 가정도 하지 마라 DCL15-C 현재 범위를 넘어서까지 사용되지 않을 객체는 static 으로

선언하라

Page 40: 선언과초기화 (DCL)

가변 인자 함수 va_list

원래 C 함수들은 고정된 개수의 인수들을 취함 . '가변인자 함수 ' 는 함수가 호출 될 때 마다 인수의 개수를 변경 가능

가변인자 함수를 사용하기 위해서는 'stdarg.h' 헤더 파일이 필요

va_list 형으로 선언– va_start 를 사용해서 va_list 형의 포인터 변수를

인수로써 초기화– va_arg 를 호출함으로써 가변인수들을 엑세스 – va_end 를 호출해서 포인터 변수인 인수를 끝냈음을

알림40/88

가변 인자를 가진 함수에서는 함수 작성자와 함수 사용자 간의 약속이 지켜져야 한다

Page 41: 선언과초기화 (DCL)

DCL10-C 부적절한 코드 예enum { va_eol = -1 };

unsigned int average(int first, …){unsigned int count = 0;unsigned int sum = 0;int i = first;va_list args;va_start(args, first);while(i != va_eol){sum += i;count++;i = va_arg(args, int);}va_end(args);return(count ? (sum / count) : 0);

}

41/88

가변 인자를 가진 함수에서는 함수 작성자와 함수 사용자 간의 약속이 지켜져야 한다

=> 호출average(1, 3, 6, 4, 1);

=> 종료값 -1 을만날 수 없어 스택에서 값을 계속 이용

Page 42: 선언과초기화 (DCL)

DCL10-C 해결방법

42/88

가변 인자를 가진 함수에서는 함수 작성자와 함수 사용자 간의 약속이 지켜져야 한다

enum { va_eol = -1 };

unsigned int average(int first, …){unsigned int count = 0;unsigned int sum = 0;int i = first;va_list args;va_start(args, first);while(i != va_eol){sum += I;count++;i = va_arg(args, int);}va_end(args);return(count ? (sum / count) : 0);

}

=> 호출시average(1, 3, 6, 4, 1, -1);

Page 43: 선언과초기화 (DCL)

43/88

제안 DCL10-C 가변 인자를 가진 함수에서는 함수 작성자와 함수 사용자

간의 약속이 지켜져야 한다

DCL11-C 가변 인자 함수와 연관된 타입 문제를 파악하고 있어야 한다

DCL12-C 불투명한 타입을 사용해 추상 데이터 타입을 구현하라 DCL13-C 함수에 의해 바뀌지 않을 값에 대한 포인터를 함수의

매개변수로 사용할 때는 const 로 정의하라 DCL14-C 여러 컴파일 단위를 거치는 전역 변수 초기화의 순서에

대해서는 어떤 가정도 하지 마라 DCL15-C 현재 범위를 넘어서까지 사용되지 않을 객체는 static 으로

선언하라

Page 44: 선언과초기화 (DCL)

가변 인자 함수

가변 인자 함수의 인자 – 정수형 인자의 경우 int 보다 작은 타입의

인자는 int 로 승계되고 , 그렇지 않은 경우는 unsigned int(4) 로 승계됨

– float 타입의 인자는 double(8) 로 승계됨

44/88

가변 인자 함수와 연관된 타입 문제를 파악하고 있어야 한다

Page 45: 선언과초기화 (DCL)

DCL11-C 부적절한 코드 예

const char *error_msg=“Error occurred”;/* ... */printf(“%s:%d”, 15, error_msg);

45/88

가변 인자 함수와 연관된 타입 문제를 파악하고 있어야 한다

=> 인자의 순서불일치 오류

=> printf() 함수는 가변인자 함수로 구현15: Error occurred

Page 46: 선언과초기화 (DCL)

DCL11-C 해결방법

const char *error_msg=“Error occurred”;/* ... */printf(“%d:%s”, 15, error_msg);

46/88

가변 인자 함수와 연관된 타입 문제를 파악하고 있어야 한다

Page 47: 선언과초기화 (DCL)

47/88

제안 DCL10-C 가변 인자를 가진 함수에서는 함수 작성자와 함수 사용자

간의 약속이 지켜져야 한다 DCL11-C 가변 인자 함수와 연관된 타입 문제를 파악하고 있어야 한다

DCL12-C 불투명한 타입을 사용해 추상 데이터 타입을 구현하라

DCL13-C 함수에 의해 바뀌지 않을 값에 대한 포인터를 함수의 매개변수로 사용할 때는 const 로 정의하라

DCL14-C 여러 컴파일 단위를 거치는 전역 변수 초기화의 순서에 대해서는 어떤 가정도 하지 마라

DCL15-C 현재 범위를 넘어서까지 사용되지 않을 객체는 static 으로 선언하라

Page 48: 선언과초기화 (DCL)

DCL12-C 부적절한 코드 예

struct string_mx{size_t size;size_t maxsize;unsigned char strtype;char *cstr;

}typedef struct string_mx * string_m;/* 함수선언 */extern errno_t strcpy_m(string_m s1, const string_m s2);extern errno_t strcat_m(string_m s1, const string_m s2);/* 기타 */

48/88

불투명한 타입을 사용해 추상 데이터 타입을 구현하라

=> string_mx 는CERT 라이브러리string_m.h 에 정의( 사용자에게 멤버노출 )정보은닉 , 데이터캡슐화제공불가

Page 49: 선언과초기화 (DCL)

DCL12-C 해결방법

string_mx 의 데이터 타입이 외부에서 보이지 않도록 private 으로 재구현

=> 두 개 의 헤 더 파 일 생성 ( 하 나 는 외부노출 , 하나는 관리용 내부 파일 )

외부노출 파일 (string_mx 구조체는 내부파일에 제시 )struct string_mx;typedef struct string_mx *string_m;

49/88

불투명한 타입을 사용해 추상 데이터 타입을 구현하라

Page 50: 선언과초기화 (DCL)

50/88

제안 DCL10-C 가변 인자를 가진 함수에서는 함수 작성자와 함수 사용자

간의 약속이 지켜져야 한다 DCL11-C 가변 인자 함수와 연관된 타입 문제를 파악하고 있어야 한다 DCL12-C 불투명한 타입을 사용해 추상 데이터 타입을 구현하라

DCL13-C 함수에 의해 바뀌지 않을 값에 대한 포인터를 함수의 매개변수로 사용할 때는 const 로 정의하라

DCL14-C 여러 컴파일 단위를 거치는 전역 변수 초기화의 순서에 대해서는 어떤 가정도 하지 마라

DCL15-C 현재 범위를 넘어서까지 사용되지 않을 객체는 static 으로 선언하라

Page 51: 선언과초기화 (DCL)

DCL13-C 부적절한 코드 예

void foo(int *x){if (x != NULL) {

*x = 3; }/* ... */

}

51/88

함수에 의해 바뀌지 않을 값에 대한 포인터를 함수의 매개변수로 사용할 때는 const 로 정의하라

void foo(const int *x){if (x != NULL) {

*x = 3; }/* ... */

}

<= Error!!!

Page 52: 선언과초기화 (DCL)

DCL13-C 해결방법

void foo(const int *x){if (x != NULL) {

printf(“Value is %d\n”, *x); }/* ... */

}

52/88

함수에 의해 바뀌지 않을 값에 대한 포인터를 함수의 매개변수로 사용할 때는 const 로 정의하라

=> 값에 대한 변경 없을때만 const 사용

Page 53: 선언과초기화 (DCL)

53/88

제안 DCL10-C 가변 인자를 가진 함수에서는 함수 작성자와 함수 사용자

간의 약속이 지켜져야 한다 DCL11-C 가변 인자 함수와 연관된 타입 문제를 파악하고 있어야 한다 DCL12-C 불투명한 타입을 사용해 추상 데이터 타입을 구현하라 DCL13-C 함수에 의해 바뀌지 않을 값에 대한 포인터를 함수의

매개변수로 사용할 때는 const 로 정의하라

DCL14-C 여러 컴파일 단위를 거치는 전역 변수 초기화의 순서에 대해서는 어떤 가정도 하지 마라

DCL15-C 현재 범위를 넘어서까지 사용되지 않을 객체는 static 으로 선언하라

Page 54: 선언과초기화 (DCL)

DCL14-C 부적절한 코드 예

/* init.c 소스파일 */int init(void) {

static int c = 0;return c++;

}

/* file_a.c 소스파일 */int a = init();

54/88

여러 컴파일 단위를 거치는 전역 변수 초기화의 순서에 대해서는 어떤 가정도 하지 마라

=> a=0, b=1 or a=1, b=0=> a=0, b=0

/* file_b.c 소스파일 */int b = init();

Page 55: 선언과초기화 (DCL)

DCL14-C 해결방법

/* init.c 소스파일 */int init(void) {

static int c = 0;return c++;

}

/* ... */int a = init();int b = init();

55/88

여러 컴파일 단위를 거치는 전역 변수 초기화의 순서에 대해서는 어떤 가정도 하지 마라

/* init.h 소스파일 */extern int a;extern int b;

Page 56: 선언과초기화 (DCL)

56/88

제안 DCL10-C 가변 인자를 가진 함수에서는 함수 작성자와 함수 사용자

간의 약속이 지켜져야 한다 DCL11-C 가변 인자 함수와 연관된 타입 문제를 파악하고 있어야 한다 DCL12-C 불투명한 타입을 사용해 추상 데이터 타입을 구현하라 DCL13-C 함수에 의해 바뀌지 않을 값에 대한 포인터를 함수의

매개변수로 사용할 때는 const 로 정의하라 DCL14-C 여러 컴파일 단위를 거치는 전역 변수 초기화의 순서에

대해서는 어떤 가정도 하지 마라

DCL15-C 현재 범위를 넘어서까지 사용되지 않을 객체는 static 으로 선언하라

Page 57: 선언과초기화 (DCL)

DCL15-C 부적절한 코드 예

enum { MAX = 100 }int helper(int i){

/* i 로 특정한 연산을 수행 */}

int main(void){size_t I;int out[MAX];for (i=0; i<MAX; i++)

out[i] = helper(i);/* ... */

}57/88

현재 범위를 넘어서까지 사용되지 않을 객체는 static 으로 선언하라

=> helper() 는 main() 함 수 외 부 에 서 사용가능

helper(int i)

helper(i)

Page 58: 선언과초기화 (DCL)

DCL15-C 해결방법

enum { MAX = 100 }static int helper(int i){

/* i 로 특정한 연산을 수행 */}

int main(void){size_t i;int out[MAX];for (i=0; i<MAX; i++)

out[i] = helper(i);/* ... */

}58/88

현재 범위를 넘어서까지 사용되지 않을 객체는 static 으로 선언하라

Page 59: 선언과초기화 (DCL)

59/88

제안 DCL00-C 변하지 않는 객체는 const 로 보장해둬라 DCL01-C 내부 스코프에서 변수 이름을 재사용하지 마라 DCL02-C 시각적으로 구별되는 식별자를 사용하라 DCL03-C 상수 수식의 값을 테스트할 때 정적 어썰션을 사용하라 DCL04-C 한 번에 여러 변수를 선언하지 마라 DCL05-C 코드의 가독성을 높이기 위해 타입 정의를 사용하라 DCL06-C 프로그램 로직상의 고정적인 값을 나타낼 때는 의미 있는

심볼릭 상수를 사용하라 DCL07-C 함수 선언 시 적절한 타입 정보를 포함시켜라 DCL08-C 상수 정의에서는 상수 간의 관계가 적절하게 나타나도록

정의하라 DCL09-C errno 에러 코드를 반환하는 함수의 반환 타입을 errno_t

로 정의하라

Page 60: 선언과초기화 (DCL)

60/88

제안 DCL10-C 가변 인자를 가진 함수에서는 함수 작성자와 함수 사용자

간의 약속이 지켜져야 한다 DCL11-C 가변 인자 함수와 연관된 타입 문제를 파악하고 있어야 한다 DCL12-C 불투명한 타입을 사용해 추상 데이터 타입을 구현하라 DCL13-C 함수에 의해 바뀌지 않을 값에 대한 포인터를 함수의

매개변수로 사용할 때는 const 로 정의하라 DCL14-C 여러 컴파일 단위를 거치는 전역 변수 초기화의 순서에

대해서는 어떤 가정도 하지 마라 DCL15-C 현재 범위를 넘어서까지 사용되지 않을 객체는 static 으로

선언하라

Page 61: 선언과초기화 (DCL)

규칙

DCL30-C 객체를 선언할 때 적절한 지속공간을 지정하라 DCL31-C 식별자를 사용하기 전에 먼저 선언하라 DCL32-C 서로에게 보이는 식별자가 유일한지를 보장하라 DCL33-C 함수 인자에서 restrict 로 지정된 소스 포인터와

목적 포인터가 동일한 객체를 참조하지 않게 하라 DCL34-C 캐시될 수 없는 데이터에는 volatile 을 사용하라 DCL35-C 함수 정의와 맞지 않는 타입으로 함수를 변환하지

마라 DCL36-C 링크 분류에서 충돌되는 식별자를 선언하지 마라

61/88

Page 62: 선언과초기화 (DCL)

규칙

DCL30-C 객체를 선언할 때 적절한 지속공간을 지정하라

DCL31-C 식별자를 사용하기 전에 먼저 선언하라 DCL32-C 서로에게 보이는 식별자가 유일한지를 보장하라 DCL33-C 함수 인자에서 restrict 로 지정된 소스 포인터와

목적 포인터가 동일한 객체를 참조하지 않게 하라 DCL34-C 캐시될 수 없는 데이터에는 volatile 을 사용하라 DCL35-C 함수 정의와 맞지 않는 타입으로 함수를 변환하지

마라 DCL36-C 링크 분류에서 충돌되는 식별자를 선언하지 마라

62/88

Page 63: 선언과초기화 (DCL)

DCL30-C 부적절한 코드 예const char *p;void dont_do_this(void){

const char str[] = “This will change”;p=str;/* ... */

}void innocuous(void){

const char str[] = “Surprise, surprise”;}/* ... */dont_do_this();innocuous();

63/88=> p 의 메모리는 dont_do_this후 삭제됨

=> p 에 “ This will change” 할당

객체를 선언할 때 적절한 지속공간을 지정하라

Page 64: 선언과초기화 (DCL)

DCL30-C 해결방법

void this_is_OK(void){const char str[] = “Everything OK”;const char *p=str;/* ... */

}/* p 는 문자열 str 의 스코프 밖에서 접근할 수 없다 .*/

64/88

객체를 선언할 때 적절한 지속공간을 지정하라

Page 65: 선언과초기화 (DCL)

규칙

DCL30-C 객체를 선언할 때 적절한 지속공간을 지정하라

DCL31-C 식별자를 사용하기 전에 먼저 선언하라

DCL32-C 서로에게 보이는 식별자가 유일한지를 보장하라 DCL33-C 함수 인자에서 restrict 로 지정된 소스 포인터와

목적 포인터가 동일한 객체를 참조하지 않게 하라 DCL34-C 캐시될 수 없는 데이터에는 volatile 을 사용하라 DCL35-C 함수 정의와 맞지 않는 타입으로 함수를 변환하지

마라 DCL36-C 링크 분류에서 충돌되는 식별자를 선언하지 마라

65/88

Page 66: 선언과초기화 (DCL)

변수 선언

C90– 변수와 함수에 대해 암시적 타입을 허용– 생략시 signed int 로 지정됨– 암시적 선언으로 타입 체크가 느슨해짐

C99– 암시적 함수 선언 금지– 컴파일러가 C99 를 지원하는게 없어서 암시적 선언을 허용

66/88

식별자를 사용하기 전에 먼저 선언하라

Page 67: 선언과초기화 (DCL)

DCL31-C 부적절한 코드 예int main(void) {

int c = foo();printf(“%d\n”, c);return 0;

}

int foo(int a) {return a;

}

67/88

식별자를 사용하기 전에 먼저 선언하라

=> main() 함수에서 foo() 호출전 명백한 프로토타입이 제시되어야 함

foo()

Page 68: 선언과초기화 (DCL)

DCL31-C 해결방법int foo(int);int main(void) {

int c = foo();printf(“%d\n”, c);return 0;

}

int foo(int a) {return a;

}

68/88

식별자를 사용하기 전에 먼저 선언하라

Page 69: 선언과초기화 (DCL)

규칙

DCL30-C 객체를 선언할 때 적절한 지속공간을 지정하라 DCL31-C 식별자를 사용하기 전에 먼저 선언하라

DCL32-C 서로에게 보이는 식별자가 유일한지를 보장하라

DCL33-C 함수 인자에서 restrict 로 지정된 소스 포인터와 목적 포인터가 동일한 객체를 참조하지 않게 하라

DCL34-C 캐시될 수 없는 데이터에는 volatile 을 사용하라 DCL35-C 함수 정의와 맞지 않는 타입으로 함수를 변환하지

마라 DCL36-C 링크 분류에서 충돌되는 식별자를 선언하지 마라

69/88

Page 70: 선언과초기화 (DCL)

식별자 룰

두 스코프에서 서로 참조되는 식별자가 있는 경우 이름 구별

다른 스코프 영역이라면 중복된 이름의 식별자 이용 가능

식별자의 유일성 보장을 위한 글자수 – 내부 식별자나 매크로 이름으로 63 개의

문자– 외부 식별자로 31 개의 문자

70/88

서로에게 보이는 식별자가 유일한지를 보장하라

Page 71: 선언과초기화 (DCL)

DCL32-C 부적절한 코드 예

extern int *global_symbol_definition_lookup_table_a;extern int *global_symbol_definition_lookup_table_b;

71/88

=> 첫 32 개 문자가 동일해 유일성 보장 X

서로에게 보이는 식별자가 유일한지를 보장하라

Page 72: 선언과초기화 (DCL)

DCL32-C 해결방법

extern int *a_global_symbol_definition_lookup_table;extern int *b_global_symbol_definition_lookup_table;

72/88

=> 첫 31 개 글자내에서 구별 가능하도록

서로에게 보이는 식별자가 유일한지를 보장하라

Page 73: 선언과초기화 (DCL)

규칙

DCL30-C 객체를 선언할 때 적절한 지속공간을 지정하라 DCL31-C 식별자를 사용하기 전에 먼저 선언하라 DCL32-C 서로에게 보이는 식별자가 유일한지를 보장하라

DCL33-C 함수 인자에서 restrict 로 지정된 소스 포인터와 목적 포인터가 동일한 객체를 참조하지 않게 하라

DCL34-C 캐시될 수 없는 데이터에는 volatile 을 사용하라 DCL35-C 함수 정의와 맞지 않는 타입으로 함수를 변환하지

마라 DCL36-C 링크 분류에서 충돌되는 식별자를 선언하지 마라

73/88

Page 74: 선언과초기화 (DCL)

restrict

포인터 선언에 사용할 수 있는 한정자 포인터의 라이프사이클 동안 한 포인터

변수만 그 변수가 참조하는 메모리에 접근할 수 있음

Aliasing 을 방지함

74/88

함수 인자에서 restrict 로 지정된 소스 포인터와 목적 포인터가 동일한 객체를 참조하지 않게 하라

Page 75: 선언과초기화 (DCL)

DCL33-C 부적절한 코드 예

char str[] = “test string”;char *ptr1 = str;char *ptr2;ptr2 = ptr1 + 3;memcpy(ptr2, prt1, 6);

75/88

함수 인자에서 restrict 로 지정된 소스 포인터와 목적 포인터가 동일한 객체를 참조하지 않게 하라

=> ptr1 과 ptr2 가 참조하는 영역이 겹침memcpy() 함수후 결과 예측 불가

ptr1

ptr2

Page 76: 선언과초기화 (DCL)

DCL33-C 해결방법

char str[] = “test string”;restrict char *ptr1 = str;char *ptr2;ptr2 = ptr1 + 3;memmove(ptr2, prt1, 6);

76/88

=> memcpy() 대신 memmove() 대체

함수 인자에서 restrict 로 지정된 소스 포인터와 목적 포인터가 동일한 객체를 참조하지 않게 하라

Page 77: 선언과초기화 (DCL)

규칙

DCL30-C 객체를 선언할 때 적절한 지속공간을 지정하라 DCL31-C 식별자를 사용하기 전에 먼저 선언하라 DCL32-C 서로에게 보이는 식별자가 유일한지를 보장하라 DCL33-C 함수 인자에서 restrict 로 지정된 소스 포인터와

목적 포인터가 동일한 객체를 참조하지 않게 하라

DCL34-C 캐시될 수 없는 데이터에는 volatile 을 사용하라

DCL35-C 함수 정의와 맞지 않는 타입으로 함수를 변환하지 마라

DCL36-C 링크 분류에서 충돌되는 식별자를 선언하지 마라

77/88

Page 78: 선언과초기화 (DCL)

volatile

“ 변덕스러운” 의미 변 수 가 여 러 요 인 에 의 해 변 경 될

가능성이 있는 경우 컴파일러로 하여금 최적화 금지 요청하는 경우 사용

비동기 시그널 처리가 가능하게 함

78/88

캐시될 수 없는 데이터에는 volatile 을 사용하라

Page 79: 선언과초기화 (DCL)

DCL34-C 부적절한 코드 예

#include <signal.h>sig_atomic_t i;void handler(int signum){

i = 0;}int main(void){

i = 1;signal(SIGINT, handler);while(i){

/* 내부작업 */}return 0;

}

79/88

캐시될 수 없는 데이터에는 volatile 을 사용하라

=> i 값이 캐시된다면while 을 통해 무한루프

=> 인터럽트 있음 (i=0) 없음 (i=1)

Page 80: 선언과초기화 (DCL)

DCL34-C 해결방법

#include <signal.h>volatile sig_atomic_t i;void handler(int signum){

i = 0;}int main(void){

i = 1;signal(SIGINT, handler);while(i){

/* 내부작업 */}return 0;

}

80/88

=> i 가 while 루 프 들어갈때 , 시그널 핸들러 호출시 원래 주소 접근 보장

캐시될 수 없는 데이터에는 volatile 을 사용하라

Page 81: 선언과초기화 (DCL)

규칙

DCL30-C 객체를 선언할 때 적절한 지속공간을 지정하라 DCL31-C 식별자를 사용하기 전에 먼저 선언하라 DCL32-C 서로에게 보이는 식별자가 유일한지를 보장하라 DCL33-C 함수 인자에서 restrict 로 지정된 소스 포인터와

목적 포인터가 동일한 객체를 참조하지 않게 하라 DCL34-C 캐시될 수 없는 데이터에는 volatile 을 사용하라

DCL35-C 함수 정의와 맞지 않는 타입으로 함수를 변환하지 마라

DCL36-C 링크 분류에서 충돌되는 식별자를 선언하지 마라

81/88

Page 82: 선언과초기화 (DCL)

DCL35-C 부적절한 코드 예

/* my_function 은 반환 타입이 void 다 */static void my_function(int a){

/* ... */return;

}int main(void){

int x;int (*new_function)(int a) = my_function;x = (*new_function)(10); return 0;

}

82/88

=> 반환값이 void 형인 my_function 에서 결과값 x 처리

함수 정의와 맞지 않는 타입으로 함수를 변환하지 마라

x

Page 83: 선언과초기화 (DCL)

DCL35-C 해결방법

/* my_function 은 반환 타입이 int 다 */static int my_function(int a){

/* ... */return a;

}int main(void){

int x;int (*new_function)(int a) = my_function;x = (*new_function)(10); return 0;

}

83/88

<= 함수의 매개변수 및 반환형을 통일해 문제 해결

함수 정의와 맞지 않는 타입으로 함수를 변환하지 마라

Page 84: 선언과초기화 (DCL)

규칙

DCL30-C 객체를 선언할 때 적절한 지속공간을 지정하라 DCL31-C 식별자를 사용하기 전에 먼저 선언하라 DCL32-C 서로에게 보이는 식별자가 유일한지를 보장하라 DCL33-C 함수 인자에서 restrict 로 지정된 소스 포인터와

목적 포인터가 동일한 객체를 참조하지 않게 하라 DCL34-C 캐시될 수 없는 데이터에는 volatile 을 사용하라 DCL35-C 함수 정의와 맞지 않는 타입으로 함수를 변환하지

마라

DCL36-C 링크 분류에서 충돌되는 식별자를 선언하지 마라

84/88

Page 85: 선언과초기화 (DCL)

식별자 분류

외부링크 : 외부로 링크된 식별자는 전체 프 로 그 램 에 서 , 즉 모 든 컴 파 일 단 위 나 라이브러리에서 동일한 객체나 함수를 나타냄

내부링크 : 주어진 컴파일 단위에서 동일한 객체나 함수를 나타내도록 되어있는 식별자임

링 크 없 음 : 식 별 자 에 링 크 가 없 다 면 이 식별자를 사용한 경우 새로운 변수나 타입이 선언됨

85/88

링크 분류에서 충돌되는 식별자를 선언하지 마라

Page 86: 선언과초기화 (DCL)

DCL36-C 부적절한 코드 예

int i1 = 10;static int i2 = 20;extern int i3 = 30;int i4;static int i5;

int i1;int i2;int i3;int i4;int i5;

int main(void) {/* ... */

}

86/88

/* 정의 , 외부링크 */

링크 분류에서 충돌되는 식별자를 선언하지 마라

/* 정의 , 내부링크 *//* 정의 , 외부링크 *//* 임시정의 , 외부링크 *//* 임시정의 , 내부링크 */

/* 유효한 임시정의 *//* 유효하지 않음 , 이전정의와 불일치 *//* 유효한 임시정의 *//* 유효한 임시정의 *//* 유효하지 않음 , 이전정의와 불일치 */

Page 87: 선언과초기화 (DCL)

DCL36-C 해결방법

int i1 = 10;static int i2 = 20;extern int i3 = 30;int i4;static int i5;

int main(void) {/* ... */

}

87/88

링크 분류에서 충돌되는 식별자를 선언하지 마라

/* 정의 , 외부링크 *//* 정의 , 내부링크 *//* 정의 , 외부링크 *//* 임시정의 , 외부링크 *//* 임시정의 , 내부링크 */

Page 88: 선언과초기화 (DCL)

규칙

DCL30-C 객체를 선언할 때 적절한 지속공간을 지정하라 DCL31-C 식별자를 사용하기 전에 먼저 선언하라 DCL32-C 서로에게 보이는 식별자가 유일한지를 보장하라 DCL33-C 함수 인자에서 restrict 로 지정된 소스 포인터와

목적 포인터가 동일한 객체를 참조하지 않게 하라 DCL34-C 캐시될 수 없는 데이터에는 volatile 을 사용하라 DCL35-C 함수 정의와 맞지 않는 타입으로 함수를 변환하지

마라 DCL36-C 링크 분류에서 충돌되는 식별자를 선언하지 마라

88/88