- 포인터 초기화 시 주의할 점
포인터를 초기화할 때는 여러 가지 방법이 존재한다. 포인터의 type마다, 또 선언과 동시에 초기화하냐에 따라서 코드의 라인 수가 달라질 수 있다. 따라서 여러 가지 경우의 수를 들어 주의할 점을 전하고자 한다.
#include <stdio.h>
int main(){
int x = 3;
int* intPtr1 = &x;
int* intPtr2;
intPtr2 = &x;
int* intPtr3 = x;
int* intPtr4;
intPtr4 = x;
//------------------------------//
char str[] = "KATE";
char* strPtr1 = str;
char* strPtr2;
strPtr2 = str;
char* strPtr3 = &str;
char* strPtr4;
strPtr4 = &str;
//------------------------------//
char* charPtr1 = "JANE";
char* charPtr2;
charPtr2 = "JANE";
char* charPtr3 = &"JANE";
char* charPtr4;
charPtr4 = &"JANE";
return 0;
}
코드가 긴 관계로, int pointer type를 다루는 섹션부터 보자.
#include <stdio.h>
int main(){
int x = 3;
int* intPtr1 = &x;
int* intPtr2;
intPtr2 = &x;
int* intPtr3 = x;
int* intPtr4;
intPtr4 = x;
return 0;
}
- 선언과 동시에 초기화냐 / 선언 이후에 추가적인 초기화 작업이냐
결론부터 말하자면 컴파일러의 입장에서 둘의 컴파일 속도는 거의 일치한다. 따라서 기호에 맞게 사용하면 되는데, 통상적으로 선언과 동시에 초기화한다. 그 이유는 가독성과 효율적인 메모리 관리에 있다. 변수를 선언하는 것만으로도, 메모리에 그 변수만 사용할 수 있는 자리가 마련된다. 효율적인 메모리는 결국 효율적인 코드, 전반적인 이득이므로, 작은 곳에서 습관을 들여놓으면 좋을 것이라는 생각이다.
- 초기화할 때는 변수 타입이 같게 설정해야 한다
intPtr1과 intPtr3, intPtr2와 intPtr4의 차이는 선언할 때 'pointing당하는' 변수에 &를 붙여야 하냐 마냐의 차이이다. 이때 우리가 집중해야 할 규칙은, '초기화 시 변수 타입이 같게 설정해야 한다' 이다.
평상시에 아무 의심 없이 int a= 3; / char s[] = "JANE"; 을 사용할 때 간과하고 있는 것이 있다. int type변수에는 int type값을, 각 원소가 character type인 배열에는 각 원소가 character type인 문자열로 초기화되어야 하듯, int pointer type 변수에는 int pointer type 값이 초기화되어야 한다.
intPtr3, intPtr4의 초기화 과정을 살펴보자. intPtr3, intPtr4는 int pointer type인 반면, x는 int type변수가 아닌가? 컴파일 오류가 날 것이다. 오류를 피하기 위해서는 우리가 x라는 int type 변수를 int pointer type로 알맞게 '성형'을 시켜줘야 한다.
intPtr1, intPtr2를 살펴보면, 포인터 변수에 &x을 초기화하고 있다. &연산은 변수의 주소를 반환하는 동시에 [&x]이 int pointer가 된다. 따라서 int, float, double... 등등 수에 관련된 변수는 포인터에 초기화할 때 &연산을 꼭 붙여주어야 문제가 없겠다.
다음으로, 문자 배열이 포인터에 어떻게 초기화되는지 살펴보자.
#include <stdio.h>
int main(){
char str[] = "KATE";
char* strPtr1 = str;
char* strPtr2;
strPtr2 = str;
char* strPtr3 = &str;
char* strPtr4;
strPtr4 = &str;
return 0;
}
배열 식별자가 있는 character type 배열에 관해 포인터에 어떻게 초기화해야 하는지 살펴보자. 앞에서 서술했듯이, 초기화할 때는 동일한 변수 타입을 갖고 있어야 정상적으로 작동한다고 했다. 그렇기 때문에 이번 코드에서는, R-Value - 다시 말해 대입당하는 변수의 타입이 character pointer type여야 한다.
strPtr1, strPtr2의 방식대로 str에 그대로 초기화해야 하는 것일까? 아니면 strPtr3, strPtr4처럼 &str에 초기화해야 하는 것일까. 결론부터 말하자면 strPtr1, strPtr2처럼 str에 그대로 초기화하는 쪽이 맞다.
- 배열의 식별자는 &, sizeof연산과 사용되지 않을 때는 '첫 번째 원소의 포인터' 값을 지닌다
strPtr1, strPtr2의 초기화 방식처럼, str변수가 단독으로 쓰였기 때문에 이는곧 str 배열의 첫 번째 원소의 포인터 - character pointer type을 지닌다. 그렇기 때문에 배열의 식별자가 있을 때 포인터로 초기화하는 경우에는 & 없이 배열의 식별자 그대로 초기화하면 된다.
- 왜 &를 사용하면 안 되는 것일까?
strPtr3, strPtr4는 배열의 식별자에 &가 붙었다. 그렇기 때문에 &str이 의미하는 것은 각 원소가 character type인, 길이 5짜리 배열 자체의 포인터 이다. "char type 배열의 포인터"가 아니라 "길이 5짜리, char type 배열의 포인터" 인 것이다. 포인터 변수는 변수의 주소 뿐만 아니라 주소의 type까지 저장하고 있다는 것을 잊지 말아라.
strPtr3, strPtr4는 char* type변수이고, &str은 char(*)[5] type 변수이다. 따라서 변수가 맞지 않으므로 오류가 발생하게 된다.
번외로, &str같은, 배열의 포인터는 2차원 배열에서 종종 쓰인다.
#include <stdio.h>
int main() {
int arr[3] = { 1, 2, 3 };
int(*ptrArr)[3] = &arr;
printf("%p\n", ptrArr); // arr의 주소 (배열 전체의 주소)
printf("%p\n", &arr); // 동일한 주소 출력됨
printf("%p\n", &arr[0]); // 첫 번째 요소의 주소 (값이 다름)
printf("%d\n", (*ptrArr)[0]); // 1
printf("%d\n", (*ptrArr)[1]); // 2
printf("%d\n", (*ptrArr)[2]); // 3
return 0;
}
위 코드에서 ptrArr은, 각 원소가 int type이고 길이가 3인 배열을 가리킬 수 있는 pointer type 변수다. 따라서 길이가 3인 배열을 받아야 하기 때문에, &arr을 사용하여 배열 전체를 받아와야지 정상적으로 초기화가 가능하다.
ptrArr은 배열 전체를 받아왔기 때문에 첫 번째 printf에서는 arr배열 전체의 주소를 출력한 것이다. 또한 두번째 printf에서는 배열의 식별자가 &와 사용되었기 때문에 arr배열 전체의 주소를 출력한 것이고, 3번째 printf에서는 arr배열의 첫 번째 원소의 주소를 출력한다.
다음으로 배열의 식별자가 없을 경우 포인터가 어떻게 초기화되는지 살펴보자.
#include <stdio.h>
int main(){
char* charPtr1 = "JANE";
char* charPtr2;
charPtr2 = "JANE";
char* charPtr3 = &"JANE";
char* charPtr4;
charPtr4 = &"JANE";
return 0;
}
식별자가 없는 문자열의 경우, 위 경우처럼 포인터의 선언과 동시에 초기화할 때만 식별자 없는 문자열을 만들 수 있다. 하지만 프로그램 상에서 이해하기 쉽게 설명하자면, 저 때 식별자로 '간주' 할 수 있는 것은 character pointer type의 charPtr, 그리고 문자열 리터럴 그 자체인 "JANE"이다.
위 '간주'를 참이라고 인식하고 위 코드를 분석한다면 무엇이 맞고 무엇이 틀린지 확연히 구분지을 수 있을 것이다.
charPtr1, charPtr2는 = "JANE"라고 되어있는데, "JANE"이란 배열의 식별자는 &, sizeof와 사용되지 않았기 때문에 배열의 포인터값을 반환한다. character pointer type의 변수에 character pointer type의 값을 대입하는 것은 지극히 정상적인 일이므로 charPtr1, charPtr2는 옳은 문장이 된다.
반면 charPtr3, charPtr4는 = &"JANE"라고 되어 있다. 배열의 식별자가 &이랑 사용되었으니 이는 '배열 전체의 포인터'값이 된다. 다시 말해, &"JANE"은 배열 길이가 5인 character pointer type인 것이다. 따라서 type이 맞지 않으므로 오류가 발생한다.
- 정리
1. 포인터 초기화
선언과 동시에 초기화, 선언 이후 초기화 모두 가능. 하지만 선언과 동시에 초기화 선호
2. 초기화할 때는 변수 타입 동일하게
포인터 변수에 문자열의 정보를 저장하고 싶다면 & 사용 없이 식별자로 초기화하자.
2차원 배열에서는 가끔 &를 사용한 식별자로 초기화하곤 한다.
3. 식별자가 없는 배열이라면, '문자열 그대로' 또는 문자열의 정보가 저장된 '포인터 변수'가 식별자가 된다.
char* s = "JANE"라면, "JANE" 또는 s가 식별자가 된다.
'C' 카테고리의 다른 글
C / 함수의 인자 평가 순서 (0) | 2025.04.09 |
---|---|
C / 포인터를 향하여 - 프로그램 메모리 구조 - Ch.3 (0) | 2025.04.07 |
C / 포인터를 향하여 - Ch.1 (3) | 2025.04.02 |