고정적인 값 = symbolic const
- 코드 상에서 literal 을 사용할 경우, 가독성이 떨어질 수 있다.
- 가급적 literal 을 직접 사용하기 보다는 symbolic 상수을 사용해 적절한 이름을 붙여 코드의 의도를 명확히 해야한다.
- const 객체
- enum
- 객체형 매크로
방식 | 평가 시점 | 메모리 소비 유무 | 디버깅 시 심볼 | 타입 체크 | 컴파일 타임 상수 표현식 |
---|
const 지정 | 런타임 | O | O | O | X |
열거형 | 컴파일 타임 | X | O | O | O |
매크로 | 전처리 | X | X | X | O |
const
객체
- 장점
- const로 지정된 객체는 특정 스코프 내에서 사용가능
- 컴파일러가 타입 체크 제공
- 디버깅 도구 사용 시, 객체의 이름을 나타낼 수 있음
- 단점
- 각 객체는 메모리 사용하며 런타임에 약간의 오버헤드 유발
- 컴파일 타임에서 정수형 상수가 필요한 곳에서는 사용할 수 없음
- struct 내부 bit 단위 로 정의된 멤버
- 배열의 크기(가변 배열 예외)
- 열거형 상수 값
- case 상수 값
enum
- 장점
- 열거형 상수는 정수로 나타낼 수 있는 정수형 상수 표현식(
int
)를 나타낼 때 사용
const
와 달리 메모리 사용 XX
- 단점
- 열거형의 열거자는 정수형으로 매핑된다.
- 열거자의 값이 중복되면 모호성으로 인한 에러 발생
열거형 선언 법
- 명시적 선언하지 않는다.
- 첫 번째 열거자에 대해서만 값을 지정
- 모든 열거자에 대해 명시적으로 값 지정
객체형 매크로
- 전처리 단계에서 사용되는 객체형 매크로(object-like macro)
#define identifier replacement-list
- 객체형 매크로는 전처리기에 의해 치환되는 구조, 컴파일 과정에서는 매크로 심볼 볼 수 없음
- 그래서 대부분의 컴파일러들은 매크로 이름을 따로 저장하고 디버거에 전달
- 매크로는 다음 이름으로 적용될 수 있는 스코프 규칙들을 고려하지 않았기 때문에 의도하지 않는 방식으로 치환되어 unexpected behaviour 유발 가능
- 객체형 매크로는 메모리 소비X, 포인터로 가리킬 수 없음
- C 프로그래머들은 심볼릭 상수로 객체형 매크로를 사용하는 편이다.
const
예
DCL-00. immutable object는 const!
- immutable 객체는 const 로 보장해야한다.
1. 부적절한 코드
- 다음 코드의 객체의 값이 의도치 않게 변경되고 있다.
1 해결방법
- const 붙이자!
scope 內 변수 재사용 금지
DCL-01. 내부 스코프에서 변수 이름을 재사용하지 말 것
- 변수의 이름을 재사용할 경우, 코드 상에서 혼란을 가중
- global 변수가 사용되는 범위에서 어떤 변수든 중복해서 전역 변수 이름을 사용하면 안된다.
- 어떤 블록 안에서 이미 사용되고 있는 변수와 동일한 이름으로 다른 블록에서 선언하면 안된다.
- 여튼 수명 다한 객체 참조는 매우 위험하다!!
1. 부적절한 코드
- local 변수가 global 변수를 가린다!!!!
변수 재활용
1. 부적절한 코드
- 수명을 다한 local 객체를 global 변수가 참조하여 임의의 코드를 수행할 수 있는 위험성 존재
1. 해결 방법
- 전역 포인터를 유효 객체(메모리 할당)하여 가리키게한다.
지역변수 포인터
2. 부적절한 코드
- 다음 코드는 지역 객체의 주소를 반환하고 있다.
arr
의 주소를 받고 싶었는데, main
함수의 pArr
의 주소를 얻는다.
지역변수는 함수를 나가면, 없어지기 때문
진짜 할당하려면 malloc
으로 힙 할당 해야함
2. 해결 방법
-
main에서 미리 할당하고 넘겨준다.
-
함수에서 malloc
으로 힙할당
선언
함수 선언은 앞에 반드시
- 함수 선언 시 반드시 함수의 선언 정보가 존재해야한다.
- 컴파일러가 타입 정보를 정확하게 체크할 수 있도록!!
- 함수의 선언 정보가 없는 상태로 함수를 호출할 경우, 컴파일러는 경고를 내지만 에러는 발생하지 않음
함수 선언은 미리
#include <stdio.h>
int func(int a, int b, int c);
Function Pointer Variable
#include <stdio.h>
int add(int a, int b){
return a + b;
}
int main(){
int(*fp)(int);
...
}
추상 데이터 타입(ADT) 사용
- 추상 데이터 타입(ADT, Abstract Data Type)은 프로그램의 대상이 되는 사물이나 현상을 추상화하여 정의한 것
- 추상 데이터 타입을 사용하면 그 타입의 세부적인 구현을 감출 수 있기 때문에, 정보 은닉(information hiding)을 할 수 있다.
- 헤더 파일에 함수 선언
- 각 파일에서 구현
(e.g. stack.h
stack.c
main.c
)
1. 부적절한 코드
- 아래의 코드는 스택 자료구조의 내부 구조가 노출되어 외부로부터 내부 데이터를 보호 할 수 없다.
1. 해결 방법
- 불완전한 타입으로 선언한 후, 포인터를 사용하라!