You are on page 1of 5

TECHNICAL FEATURE

Beginner Corner

초보자를위한
C 언어프로그램스타일가이드 Ⅲ
지난 호에는 공백 사용, 단문 및 복합문, 연산자 등에 대해서 살펴보았다. 이번 호에는 어떻게 하면 우수한
이식성을 갖춘 프로그램을 작성할 수 있을까? 프로그램을 작성하는데 있어 가장 중요한 이식성(Portability)에 대해서
알아보자.

편역: 박찬일/(주)하솜정보기술 대표이사


pak@hasom.com

여야 한다. 한 기계에 최적화하는 것은 좋지 않은 프로그램


을 만들 수가 있다. 이에 대한 기록 작업(documentation)
본 자료는“Recommended C style and Coding
Standards”
를 편역한 것으로, 이는 AT&T사의 벨연구소에서
은 가능한 상세히, 국지적으로 하여야 하며, 이 기록에는 동

발표된 논문인“Indian Hill C style and Coding Standards”


작하는 원리와 필요한 이유를 필히 적어야 한다.

를 기초로 하여 알기 쉽도록 풀어서 설명했다.


■근본적으로 이식성을 가질 수 없는 부분도 있음을 알아야
한다. 예를 들면, 프로그램 status word, 어셈블러, 입출력
드라이버 등과 관련된 특정기계를 지원하도록 설계된 코드
이식성(Portability)
등이 이에 해당된다. 비록 이와 같은 경우에도 기계에 독립
프로그램의 이식성은 무엇보다도 중요한 사항이다. 그럼 지 적이기 위한 많은 루틴(routine)들이나 데이터 구조 등이
금부터 이식성을 갖춘 프로그램을 짜기 위한 방법에 대해서 알 있을 수 있다.
아보자. 본 지에서 의미하는 이식성이란? 서로 다른 기계에서
헤더파일과 컴파일 플래그만 바꿈으로서 컴파일 되거나 실행될 ■기계 종속성이 있는 코드와 없는 코드는 서로 다른 파일로
수 있음을 말하는 것으로, 여기서 헤더파일은 기계마다 달라질 분리해야 한다. 그렇게 함으로써 프로그램이 새로운 기계
수 있는 #define이나 typedef을 포함하고 있다. 또한 일반적으 에 이식될 때 바꾸어야 할 것을 보다 쉽게 결정할 수 있다.
로 새로운 기계(new machine)란? 다른 기계, 다른 운영체제, 그리고 적당한 헤더파일에 기계 종속성과 관련된 것에 대
다른 컴파일러 등을 말한다. 다음은 이식성을 갖춘 프로그램을 해 주석을 달아야 한다.
짜기 위해 피해야 하는 함정들을 나열한 것이다.

“구현시에 따른다(implementation defined)”
라고 설명한
■이식성이 뛰어난 코드를 우선적으로 작성하고 직접적으로 부분은 기계의 특성(컴파일러)과 관련된 사항임을 주지해
사용될 기계에 최적화하기 위한 자세한 사항은 후에 고려하 야 한다(편주: C 언어의 바이블인“The C Programming

106
초보자를 위한 C 언어 프로그램 스타일 가이드 Ⅲ

Language”
에서는 위와 같은 용어를 많이 사용하고 있다).
표 1. C 언어 기본 타입의 크기

type PDP-11 VAX/11 68000 Cray-2 Unisys Harris 80386


■WORD의 크기를 항시 조심해야 한다. 객체(편주: 여기서
char 8 8 8 8 9 8 8
는 변수 등을 의미)들의 크기는 직관적으로 알 수 없다. 포 short 16 16 8/16 64(32) 18 24 8/16
인터는 항시 integer와 같은 크기가 아니며, 서로 간에 같 int 16 32 16/32 64(32) 36 24 16/32

을 수도 혹은 전적으로 서로 교환하여 사용할 수도 또는 없 long 32 32 32 64 36 48 32


char* 16 32 32 64 72 24 16/32/48
을 수도 있다. 표 1은 다양한 기계 및 컴파일러에서 C 언어
int* 16 32 32 64(32) 72 24 16/32/48
의 기본 타입(type)의 크기를 보여준다.
int(*)( ) 16 32 32 64 576 24 16/32/48

어떤 기계는 주어진 타입에 대해 여러 개의 가능한 크기를 갖


기도 한다. 그 크기는 컴파일러의 속성과 옵션 플래그에 따라 표 2. C 언어 타입의 크기

변한다. 다음 표 2는 대부분의 시스템에서 확실한 타입의 크기 type minimum Bits No Smaller than

를 보여주고 있다(편주: C 언어에서는 이와 같은 방식으로 타입 char 8


short 16 char
의 크기를 정의하고 있다). unsinged와 signed의 비트 크기는
int 16 short
같다.
long 32 int
float 24

“void*”타입은 어떠한 데이터의 포인터를 담을 수 있는 충 double 38 float

분한 공간 확보를 보장한다.“void(*)()”
는 함수의 포인터를 any* 14
char* 15 any*
담을 수 있다. generic 포인터(편주: 어떤 타입이든지 담을
void* 15 any*
수 있는 포인터)가 필요할 때는 위와 같은 타입을 이용하면
된다(구식 컴파일러는 char*, char(*)()를 사용). 위의 경우
에서 이들 변수를 사용하기 전에는 사용하고자 하는 정확한 ■integer 상수 0(zero)은 어떤 포인터 타입(type)으로도 형
타입으로 형변환(cast)시켜야 함을 주지해야 한다. 변환이 될 수 있다. 이것을 null 포인터라고 하며 같은 타입
의 다른 일반적인 포인터와는 다르다. null 포인터는 상수

“int*”
와“char*”
가 서로 같은 크기라 할지라도 이들은 서 0과 같은 지를 비교할 수 있다. 반면에 null 포인터는 0의
로 다른 형태(format)를 갖고 있는 경우가 있다. 예를 들어 값을 갖고 있는 일반적인 변수와는 같은 지를 비교할 수 없
아래와 같은 문장은 sizeof(int*)와 sizeof(char*)가 같은 을 수도 있다.
기계라 할지라도 제대로 실행되지 않는 경우가 있음을 보
여준다. 즉 free 함수는“*char”
를 받는 것으로 인식했으 null 포인터는 항상 모든 비트가 0으로 구성된 것만은 아니며
나 실제로는“*int”
를 받음으로서 제대로 동작을 못한다. 서로 다른 타입의 null 포인터는 다를 수도 있다. 어떤 null 포
인터를 다른 타입의 포인터로 형변환을 하면 변환되는 타입의
int*p = (int *)malloc(sizeof(int)); null 포인터로 형변환된다.
free(p);

■ANSI C에서는 같은 공간을 어드레싱(addressing)하고 있


■변수의 크기가 정확하지 않을 수도 있음을 주지해야 한다. 는 같은 타입의 포인터를 비교했을 때 같은 값이 나온다. 0
즉, Cray-2 기종은 int의 크기가 64비트이다. 그러나 long 이 아닌(non-zero) integer 상수를 포인터 타입으로 형변
을 int로 형변환 시켰다가 다시 이것을 long으로 형변환을 환을 하였을 때, 그것은 다른 일반적인 포인터와 동일하게
시키면 32비트로 데이터가 잘릴 수도 있다(truncated). 된다. 반면에 ANSI C가 아닌 컴파일러는 포인터가 같은

Embedded World 107


TECHNICAL FEATURE

공간을 어드레싱(addressing)하여도 비교했을 때 다른 값 대한 주석을 달아야 한다. unsigned의 특징은 unsigned


이 나올 수가 있다. 다음의 두 포인터는 비교했을 때 같거 char를 선언함으로써 보장된다.
나 같지 않을 수도 있으며 같은 공간을 사용할 수도 그렇지
않을 수도 있다. ■ASCII 코드체계를 당연한 것으로 생각해서는 안된다. 그렇
지 않다면 이에 대한 기록 및 지역화를 하여야 할 것이다.
((int *)2) char 크기가 8비트보다 클 수 있음을 주지하여야 한다.
((int *)3)

■2의 보수(2’
s complement) 표현을 이용하여 프로그램하
만약 null이 아닌“magic”포인터(편주: 일반 변수처럼 사 지 말아야 한다. 일반적으로 수식계산에서 shift 연산을 대
용하는 포인터 변수)가 필요하다면 어떤 공간을 확보하거 체하는 최적화 방안으로 사용되는 경우가 이에 해당된다고
나, 기계종속적인 포인터로서 취급하여야 한다. 하겠다.
만약 굳이 필요하다면 기계 종속적인 코드에 #ifdef을 사용
extern int x_int_dummy; /* in x.c */ 하거나, 프로그램에는 #ifdef 매크로를 사용하여야 한다.
#define X_FAIL (NULL) 프로그램이 이식(porting)될 때 잡기 어려운 버그와 불분
#define X_BUSY (&x_int_dummy)
명함에 의한 문제에 대처하여 시간을 절약하기 위해 이에
대해 비중을 두어야 한다.
#define X_FAIL (NULL)
#define X_BUSY MD_PTR1
/* MD_PTR1 from“machine.h”*/
■Word 크기나 값의 허용치(value range)가 중요하다면
typedef을 사용하여야 한다. 큰 프로그램에서는 이전 프로
그램에 공통적으로 사용되는 type의 크기에 민감한 것
■float 값은 정밀도(precision)와 범위(range)로 구성된다 (width-sensitive)과 관련한 파일을 갖고 있는데, 이렇게
(편주: 컴퓨터는 부동소수값을 가수와 지수로 분리하여 처 관리함으로써 크기에 민감한 코드를 찾는데 도움을 얻을
리한다). 그리고 이것은 객체(변수)의 크기에 독립적으로 수 있어 이후 수정시 쉽게 작업할 수가 있다. 단순한 순환
처리된다. 문에서 16비트나 32비트를 사용할 수 있는 카운터의 경우
그러므로 32비트 부동소수값의 overflow/underflow는 서 는 integer를 사용하는 것이 유리하다. 이는 기계에서 가장
로 다른 기계에서 서로 다른 값을 갖게 된다. 마찬가지로 최적하게 돌아갈 수 있는 단위이기 때문이다.
4.9 곱하기 5.1의 값은 두 개의 서로 다른 기계에서 서로 다
른 값을 갖게 된다. rounding과 truncation은 놀랄만한 차 ■데이터 정열에 주의하여야 한다(data alignment). 예를 들
이를 일으킨다. 면 다양한 기계에서 4바이트 integer가 어떠한 메모리 번
지에 위치하거나 혹은 짝수번지에 위치할 수도 있다. 그러
■어떤 기계에서는 double이 float보다 정밀도(precision)와 므로 비록 타입의 크기가 같은 이기종이라고 할지라도
범위(range)가 적은 경우도 있다. struct에서 각각의 멤버는 다양한 기계에서 위치가 서로
다르게 될 수 있다.
■signed char를 주의하여야 한다. 어떤 VAX 기계는 다른
일반적인 기계와는 달리 수식에서 사용될 때 char가 ■기종에 따라서는 주소가 증가하는 경우에 Word 안의 바이
signed extended 된다. 코드가 signed나 unsigned를 가 트가 증가하는 순서로 배열된 경우도 있고(little-endian),
정하고 있다면, 이는 이식성이 좋지 않은 경우가 된다. 만 또는 감소하는 순으로 배열된 경우도(big-endian) 있음을
약 부득이 signed나 unsigned를 가정하려고 한다면, 이에 주지하여야 한다. 즉, Word 안의 바이트(byte)의 정열순서

108
초보자를 위한 C 언어 프로그램 스타일 가이드 Ⅲ

는 서로 같지 않을 수가 있다. 그러므로 어떤 변수의 바이


s =“/dev/tty??”;
트 및 비트의 순서가 왼쪽에서 오른쪽으로 증가한다고 가
strcpy(&s[8],ttychars);
정하고 프로그램을 하면 안된다.

■서로 다른 컴파일러는 다른 방식으로 struct를 return함을 ■어드레스(address) 공간에는 변수사이에 빈공간이 있을 수


주지하여야 한다. 이와 같은 문제는 다른 컴파일러에서 컴 있다. 어레이에서 할당되지 않은 공간(편주: 어레이의 범위
파일된 라이브러리에서 struct를 return할 때 발생한다. 를 벗어난 공간이나 어레이 시작 전의 공간)을 사용하면 프
struct 포인터는 여기에 해당되지 않는다. 로그램이 동작하지 않는다. 또한 그 어드레스를 비교하면
어떤 경우에는 틀린 결과를 돌려보내기도 하고 영원히 반
■함수에 매개변수를 전달하는 구조(mechanism)에 대해 어 복하기도 한다.
떤 가정도 하여서는 안된다. 특히 포인터, 변수의 순서 및
크기에 관한 것은 주의를 해야한다. 아래 코드는 이식성이 ■오직‘==’
와‘!=’
만이 어떤 타입의 포인터를 비교할 수 있
결여된 예이다. 을 뿐이며‘<’
,‘<=’
,‘>’
,‘>=’
는 같은 어레이의 어드레스
에 대해서만 비교할 수 있다.

c = foo(getchar( ),getchar( ));


■Word의 크기는 쉬프트(shift)와 마스크(mask)에 영향을
미친다. 다음과 같은 코드는 68000계열의 기종에서 오른
char
쪽의 3비트를 clear시키며 다른 기종에서는 변수 위부분의
foo(c1,c2,c3) 2바이트까지도 clear시킬 수 있다.

char c1,c2,c3;
x &=0177770;
{
char bar = *(&c1 + 1);
return(bar); /* often wo’nt return c2 */ 다음과 같은 코드는 모든 기종에서 사용가능하며 위의 문제
} 를 피할 수 있는 코드이다.

x &=~07;

위의 예제는 많은 문제점을 내포하고 있다. 즉 스택(stack)이


메모리 번지에서 위 혹은 아래쪽으로 커질 수도 있으며, 변수가 ■가능하면 lint를 사용하는 것이 좋다. 프로그램 버그나 일
함수에 전달될 때 자동적으로 값이 확장될 수도 있다. 예를 들 관성이 결여된 것을 찾는데 유용하게 사용되는 도구이다.
면 char 타입이 integer 타입으로 전달되어질 수도 있다. 매개
변수가 스택에 왼쪽 변수부터 혹은 오른쪽 변수부터 넣을 수도 ■컴파일러의 확장기능을 주의하여야 한다. 만약 이 코드를
있고, 레지스터에 넣을 수도 있다. 사용한다면 이에 관한 문서를 남기고, 이 코드는 이식성이
결여된 코드로 간주하여야 한다.
■어떤 기종에서는 NULL char 포인터‘(char *)0’
을 NULL
스트링과 동일하게 처리하기도 하므로 주의하여야 한다.
변수, 함수명의 규약

■스트링 상수(string constant)를 절대로 수식해서는 안된 개개의 프로젝트들을 수행할 때 그들 나름대로 변수 및 함수


다. 아래는 이와 관련된 것으로 잘 알려진 예이다. 의 이름을 부여하는 규약이 있다. 일반적인 규약은 다음과 같다.

Embedded World 109


TECHNICAL FEATURE

�이름의 앞이나 뒤에 밑줄문자(__)는 시스템을 위해 예약되


어 있으므로 일반 사용자가 사용해서는 안된다.
�#define 콘스턴트는 항상 대문자이어야 한다.
�enum 콘스턴트는 첫 글자를 대문자화하거나 아니면 전부
대문자를 사용하여야 한다.
�함수, typedef, 변수들의 이름과 struct, union, enum의
타입이름(tag name)은 한상 소문자를 사용해야 한다.
�대부분의 매크로 함수는 대문자를 사용하지만, 함수로서
함께 사용되는 매크로 함수는 소문자를 사용하기도 한다
(getchar, putchar 등). 소문자를 사용하는 매크로 함수는
일반적인 함수같이 매개변수를 한번 사용하고 변수에 값을
할당하지 않는 경우에 한해 사용한다. 왜냐하면 함수는 매
개변수를 통해 값을 받는다. 그러나 변수에 값을 할당했다
고 할지라도 함수가 끝나면 그 값을 돌려주지 않는다. 이는
매개변수를 스택에 넣고 사용하기 때문이다.
�대문자만 다른 Foo, foo와 같은 이름은 사용하지 않아야
한다. 마찬가지로 foobar, foo_bar와 같은 것도 피해야 하
는데, 이는 혼란스러울 수가 있기 때문이다.
�서로간의 비슷한 이름은 사용하지 말아야 한다. 많은 터미
널과 프린터에서 아라비아 숫자‘1’
, i의 대문자‘I’
, L의
소문자‘l’
이 서로 비슷하게 보일 수 있기 때문이다. 특히
변수에서 소문자‘l’
은 사용치 말아야 하는데, 이는 상수
‘1’
과 비슷하게 보일 수 있다.

일반적으로 프로그램 모듈에 속해있음을 표시하기 위해 전역


변수 앞부분에 모듈별로 공통된 이름을 붙이는 것이 좋다. 전역
변수는 전체적인 프로그램 구조에서 그룹(group)지어지는 것이 인터넷에서읽는
좋다. typedef이 사용된 이름은 끝에 _t를 붙이는 것이 좋다. 그 Embedded Worlde-Magazine
리고 다양한 표준 라이브러리와 상충이 되는 이름의 사용은 피
Embedded World의 특징은 제작비용, 및 유통비용, 판매가격이 상대적으
해야 한다. 어떤 시스템은 생각했던 것보다 더 많은 라이브러리
로 인쇄책자보다 저렴하며 수정 및 재가공이 가능합니다. 또한 멀티미디어
코드를 갖고 있는 경우도 있고, 또 이후에 프로그램이 추가될 기능이 있어 표현이 다양하고 광고효과가 뛰어난 장점을 지니고 있습니다.

수 있음을 염두에 둬야 한다.


Embedded World를 www.EmbeddedWorld.co.kr에서 이용하시려면
Adobe Acrobat eBook Reader를 설치해야 합니다.(eBook Service 참조)

다음 호에서는 상수, 조건부 컴파일, 고려사항 등에 대해 살


펴보도록 하자.
02-2026-5700
www.embeddedworld.co.kr

110

You might also like