2011. 9. 14. 17:10

[ 031 ] - 여러개의 상수를 선언할 때 #define보다 열거형을 사용하자.

우리는 #define 문의 사용에 익숙하기 때문에 보통 상수를 선언할 때 #define 문을 사용하는 경우가 많습니다.

한두 개의 상수를 선언하는 경우라면 #define 문을 사용해도 되지만 여러 개의 상수를 순차적으로 정의할 필요가

있을 때에는 #define 문을 사용하는 것보다 열거형을 사용하는 것이 좋습니다.

#define KOREAN 10

#define ENGLISH 11

#define MATH 12

#define HISTORY 13

위와 같이 과목에 따라 과목의 코드를 #define 문을 사용하여 상수로 처리하면

열거형을 사용할 때 좀더 간단하게 표현할 수 있습니다.

enum {

    KOREAN = 10,

    ENGLISH,

    MATH,

    HISTORY

};

 

특히 열거형을 사용할 때의 장점은 단순히 코드를 작성할 때의 시간과 수고를 덜어준다는 것뿐만 아니라

열거형을 사용해서 상수를 선언하면 서로 다른 이름의 상수를 같은 값으로 정의할 수 있는 오류를 막을 수 있습니다.

 

[ 032 ] - 비트 연산을 할 때는 자료형의 크기를 고려하자.

비트 연산을 사용할 때 가장 주의해야 할 것은 현재 사용하는 자료형의 크기입니다.

문자형은 1바이트의 자료형, 정수형은 4바이트의 자료형이라는 것을 잊으면 안 됩니다.

특히 초보 프로그래머들이 아무 생각없이 사용하는 정수형은 4바이트의 크기를 갖기 때문에 비트 연산을 사용할 때도

그 크기를 반드시 고려해서 프로그래밍해야 합니다.

 

[ 033 ] - ~ 연산자와 ! 연산자의 차이를 확실하게 알아 두자.

NOT 연산자는 ! 연산자와 ~ 연산자의 두 가지 종류가 있습니다.

이 두 연산자는 비슷한 기능을 하는 것처럼 보여도 실제로 적용한 결과는 아주 다른 것을 알 수 있습니다.

! 연산자는 논리 연산자이므로 0 과 1만 나타내기 때문에 주로 제어문이나 조건문에서 사용하고,

~ 연산자는 다른 비트 연산자들과 함께 사용하며 0비트의 경우에는 1비트, 1비트인 경우에는 0비트로 비트 변환을

하는 연산자라는 기능을 정확하게 이해해야 합니다.

연산자를 잘못 사용해서 생기는 문제는 디버깅하기가 무척 까다롭습니다.

소스 코드의 길이가 수천, 수만 줄의 분량이라면 찾다가 포기하는 경우가 부지기수입니다.

저도 연산자 하나를 잘못 사용해서 하룻밤을 꼬박 지새고 모니터의 목을 조르며 흥분한 기억이 납니다.

사실 아무것도 아닌 것 같이 보이는 간단한 기능의 차이를 아는 것이 고수와 초보의 차이라는 것을 잊지 마세요.

 

[ 034 ] - 모든 변수는 메모리에 할당된다는 사실을 기억하자.

초보 프로그래머들이 프로그래밍을 하다 착각하기 쉬운 것 가운데 하나는 프로그램의 코드에서 사용한

변수들이 메모리와 상관없이 사용된다는 것을 잊는 것입니다.

예를 들면 int num;이라고 선언한 경우 이 변수 num을 프로그래머는 알파벳 문자 num으로 생각하지만

실제 컴퓨터에서는 'num'이라는 이름을 갖는 정수형 크기인 4바이트 공간으로 인식합니다.

물론 컴퓨터에서도 'num'이라는 이름을 알기 때문에 num이라는 변수를 두 개 이상 중복하여 사용할 수 없습니다.

하지만 num = 15;와 같이 변수 num에 어떤 값을 대입하는 과정을 보면 변수 num에 할당된 4바이트 메모리 공간 안에

15라는 값을 복사하는 과정으로 처리됩니다. 이와 같이 모든 데이터가 메모리에 할당된다는 것이

C 프로그래밍 언어에서 포인터를 사용할 수 있는 근본 취지인 것입니다.

따라서 변수 하나 하나가 각각의 메모리 공간을 가지며 그 메모리 공간은 포인터를 사용해서

접근할 수 있다는 점을 꼭 기억하고 프로그래밍 하세요.

 

[ 035 ] - 10진수 표현보다 2진수나 16진수에 더 익숙해지자.

컴퓨터를 공부하다 보면 반드시 2진법에 대해서 배웁니다. 컴퓨터 자체가 0과 1 두 가지 값만을

사용하므로 문자나 숫자, 부동소수점에 상관없이 오직 0과 1로 나타내기 때문에

컴퓨터를 사용할 때 2진법에 대한 이해는 필수입니다.

컴퓨터에서 0과 1을 사용하여 숫자를 표현하는 방법은 간단합니다.

다음은 3개의 비트를 사용해서 0부터 7까지의 10진수를 표시한 예입니다.

000 → 0

001 → 1

010 → 2

011 → 3

100 → 4

101 → 5

110 → 6

111 → 7

이와 같은 방법을 이용해서 컴퓨터는 숫자와 문자를 자유롭게 표현할 수 있습니다.

하지만 인간은 컴퓨터와 달리 10진수의 표현 방식에 더 익숙합니다. 초보 프로그래머들은 주로 10진수를 사용하지만

경험이 좀더 많은 프로그래머들은 10진수보다 2진수나 16진수를 이용합니다.

특히 2진수보다 16진수를 더 잘 이용하는데 이렇게 16진수를 이용하는 이유는 컴퓨터 내부에서

모든 데이터는 무조건 2진수로 표현되지만 2진수를 직접 사용하기에 코드의 길이가 너무 길기 때문에 2진수를

좀더 짧게 표현할 수 있는 16진수를 이용하는 것입니다.

실무에서 프로그래밍을 할 때에는 11, 12, 13 … 과 같은 10진수보다 A, B, C … 와 같은

16진수 표현법을 익혀 두는 것이 코드를 만들거나 다른 고수들의 코드를 분석할 때에도 도움이 될 것입니다.

 

[ 036 ] - 메모리의 주소 개념에 대해서 제대로 알고 프로그래밍하자.

메모리를 사용할 때는 메모리의 주소를 이용하여 접근합니다.

이와 같은 규칙은 '서울시 ○○구 △△동 ●●번지'의 주소 개념과 비슷합니다.

메모리도 각각의 주소를 나누어서 사용하며 C 언어에서는 메모리의 주소를 포인터로 처리할 수 있습니다.

모든 데이터가 메모리에 저장되어 있기 때문에 데이터를 사용할 때에도 주소를 이용하며,

프로그램에서 사용하는 모든 변수, 함수 등도 주소로 접근합니다.

예를 들면 다음과 같은 코드가 있습니다.

int num;

num = 15;

 

위의 코드는 변수 num 안에 15라는 값을 대입하는 내용입니다.

위의 코드는 겉으로 보기에 주소를 전혀 사용하지 않는 것 같지만 컴퓨터는 위의 코드를 주소로 바꾸어서 사용합니다.

mv 0x4727ac, $15

 

위의 어셈블리어 코드는 num = 15;라는 코드가 실제 컴파일된 후의 모습을 나타냅니다.

컴퓨터의 입장에서는 num이라는 변수는 전혀 사용하지 않으며

오직 변수 num에 대한 0x4727ac라는 주소 값을 이용합니다.

초보 프로그래머들 가운데에는 메모리 공간에 변수 num이라는 이름을 가진 구역이 생기고,

그 안에 15라는 값을 넣는다고 오해하는 경우가 있는데 모든 변수는 주소를 이용해서 연산한다는 것을 꼭 기억하세요.

 

또 프로그램을 만들다 보면 포인터의 주소나 변수의 주소값을 출력하는 다음과 같은 코드를 사용하는 경우가 있습니다.

printf("Addr of data : %#x\n", data);

 

위의 결과가 다음과 같을 때 위의 변수 data의 주소 값 0x4727ac가

실제 메모리의 물리적인 주소를 의미하는지 생각해 봅시다.

Addr of data : 0x4727ac

 

대부분의 초보 프로그래머들은 이 주소가 실제 메모리의 물리적인 주소라고 생각하겠지만,

운영체제가 없는 임베디드 프로그램과 같이 특수하게 절대 주소를 사용하는 분야가 아니라

보통 일반적으로 사용하는 PC나 워크스테이션과 같은 컴퓨터에서 출력하는 주소는 절대 주소가 아닙니다.

예를 들면 제 주소가 '서울시 광진구 자양3동 123-45번지'라고 할 때, 이 주소는 절대 주소가 아니라 상대 주소입니다.

절대 주소라면 [위도 : 123° 45′ 67″, 경도 : 123° 45′ 67″]과 같이 표현해야 할 것입니다.

마찬가지로 위의 경우와 같이 컴퓨터에서 출력하는 주소도 절대 주소가 아니라 상대 주소라는 것을 이해해야 합니다.

따라서 메모리의 주소라고 해서 실제 물리적인 메모리의 주소라고 생각하면 안 된다는 사실을 기억하기 바랍니다.

 

[ 037 ] - 문자열을 다루는 세 가지 방법을 알아 두자.

C 언어에서는 세 가지 방법을 사용하여 문자열 데이터를 저장합니다.

이 가운데 두 가지 방법은 배열을 이용하는 것이고 마지막 하나는 문자형 포인터를 이용하는 방법입니다.

char buf[5] = {'a', 'b', 'c', 'd', 'e'};

char buf[5] = "abcde";

char *ptr = "abcde";

대부분의 프로그래머가 사용하는 방법은 세 번째 문자형 포인터를 이용하는 방법입니다.

 

[ 038 ] - 문자열의 끝에는 반드시 '\0' 표시를 하자.

문자열을 사용할 때에는 문자열의 끝에 '\0' 표시를 해주어야 합니다.

char *ptr = "abcde";와 같이 문자형 포인터를 사용하는 경우라면 자동으로 문자열의 끝에 '\0' 기호가 추가되지만

그렇지 않다면 항상 프로그래머가 추가해주어야 합니다.

특히 문자열을 복사하기 위해서 malloc()으로 새로운 문자열을 메모리에 할당하려면

다음과 같이 문자열의 크기에 1을 더해서 '\0' 문자도 복사해주어야 합니다.

dstptr = (char *)malloc(strlen(srcptr) + 1);

 

 

[ 039 ] - malloc() 함수를 사용할 때는 sizeof() 함수와 strlen() 함수를 구별해서 사용하자.

간혹 프로그래머들 가운데 sizeof() 함수와 strlen() 함수의 사용을 혼동하는 사람들이 있습니다.

sizeof() 함수는 현재의 자료형의 크기를 구하기 위해서 사용하는 함수이고,

strlen() 함수는 문자열의 크기를 구하기 위해서 사용하는 함수입니다.

두 함수의 역할이 완전히 다르기 때문에 절대 헷갈리면 안 됩니다.

 

다음 코드를 입력하고 실행 결과가 어떻게 나오는지 확인해 보세요.

문자형 포인터의 sizeof(ptr1)의 결과와 문자형 포인터의 strlen(ptr1)의 결과는

다음과 같이 서로 다른 것을 확인할 수 있습니다.

ptr1의 sizeof() 결과 : 4

ptr1의 strlen() 결과 : 6

sizeof() 함수는 문자형 포인터의 자료형의 크기이므로 포인터의 크기인 4바이트를 출력하지만,

strlen()함수는 문자형 포인터가 가리키는 문자열의 크기를 의미하므로 6바이트가 출력되는 것입니다.

단 실제 데이터는 5바이트이지만 문자열의 마지막에 '\0' 문자가 추가되어서 모두 6바이트가 됩니다.

 

[ 040 ] - 구조체 포인터를 사용할 때는 sizeof(*ptr) 형식을 사용하자.

sizeof() 함수를 사용할 때 또 하나 저지르기 쉬운 실수는 구조체 포인터의 경우입니다.

다음 코드를 입력하고 실행해 보세요.

마지막 세 줄의 printf() 문을 확인해 보면 sizeof(ptr), sizeof(*ptr), sizeof(NODE)로 서로 다른데

결과는 어떻게 나오는지 다음의 결과를 살펴봅시다.

ptr의 sizeof() 결과 : 4

ptr의 sizeof() 결과 : 12

NODE의 sizeof() 결과 : 12

 

sizeof(ptr)는 구조체, 문자에 상관없이 포인터의 크기인 4바이트를 돌려줍니다.

실제 구조체의 크기를 구하려면 sizeof(*ptr)나 sizeof(NODE)로 알 수 있습니다.

이 가운데 특히 sizeof(*ptr)를 사용해야 하는데 sizeof(ptr)를 사용해서 실수하는 경우가 있으므로 주의하기 바랍니다.

Posted by devanix