1. 포인터와 문자열
포인터와 문자열 둘은 아주 관련이 깊고, 배열에 문자열을 저장하고 %s 서식문자를 이용하여 문자열을 출력했던 원리를 이제 알 수 있습니다.
#include <stdio.h>
int main(void)
{
char str[] = "C is very easy!";
printf("%s\n", str);
return 0;
}
실행결과
C is very easy!
계속하려면 무 키나 누르십시오 . . .
str이라는 배열을 선언하고 C is very easy!라는 문장을 저장했습니다. 이 때, 배열의 이름 str은 문자열이 저장된 곳을 가르키는 포인터입니다. 그리고 문자열의 마지막에는 항상 null문자 \0이 자동으로 저장된다고 했습니다. %s 서식문자는 배열이름 str이 가르키는 주소값에서 출력을 시작하여 null문자 \0을 만나면 출력을 종료합니다.
배열의 이름이 포인터인 만큼, 포인터에도 문자열 저장이 됩니다.
#include <stdio.h>
int main(void)
{
char * ptr = "C is very easy!";
int i = 0;
printf("%s\n", ptr); // %s 서식문자를 이용한 출력
while(ptr[i]!='\0') // %s 서식문자를 이용하지 않고 문자를 하나하나 출력
{
printf("%c", ptr[i]);
i++;
}
printf("\n");
return 0;
}
실행결과
C is very easy!
C is very easy!
계속하려면 아무 키나 누르십시오 . . .
포인터에 문자열을 저장한 이후에 %s 서식문자를 이용해서 출력을 했습니다. 포인터에 어떻게 문자열을 담을 수 있는 것일까요? 포인터는 메모리공간의 주소를 담는 변수인데 말입니다. 사실 위의 코드에서 "C is very easy!"가 반환하는 것은 가장 첫 번째 데이터인 C의 주소입니다. 큰 따움표 " "로 둘러쌓인 문자열이 반환하는 것은 가장 첫 번째 byte의 주소값 입니다. 따라서 포인터에 "문자열"을 저장하면 문자열의 첫 번째 주소값이 저장되고 출력을 할 때는 그 위치부터 null문자가 나올 때 까지 출력을 해주는 것입니다.
이렇게 편리한 기능을 제공하는 %s 서식문자가 없다면 우리는 문자를 하나하나 출력해야 합니다. 위에서 보여드리는대로 while 반복문을 이용해서 널 문자가 아닌 동안에만 출력을 하게끔 코드를 작성합니다. 이조차도 할 수 있는 이유는 바로 null문자가 존재하기 때문입니다. null문자 '\0'은 문자열의 끝을 알려주기 때문에 우리가 문자열을 구분할 수 있게 해주는겁니다.
2. %s 서식문자와 & 연산자
이제 scanf 함수를 사용하여 %s형태로 배열이나 포인터에 문자열을 저장할 때 &연산자를 사용하지 않는 이유를 설명할 수 있습니다. 변수에 어떤 문자나 숫자를 저장할 때는 &연산자를 사용했습니다. 이는 scanf 함수가 &연산자가 반환시켜주는 주소로 가서 입력받은 데이터를 저장해주기 때문입니다. 하지만 배열의 이름이나 포인터는 그 자체가 주소값이기 때문에 &연산자를 사용하지 않는겁니다.
이 전 강의에서 배열의 이름은 상수 포인터이기때문에 가르키는 위치를 변경할 수 없다고 했고 변수 포인터는 말 그대로 변수이기 때문에 다른곳을 가르키도록 재정의 할 수 있다고 했습니다.
예상처럼 배열의 이름은 다른곳을 가르키도록 변경할 수 없습니다.
오늘의 내용과는 별 상관없지만 한 가지 더 알려드리고 오늘 내용을 끝내겠습니다. 바로 포인터 배열이란 거에요.
보통 배열에는 숫자나 문자, 문자열 같은 값들을 저장합니다. 포인터 배열이란 것은 딱히 특별한 것이 아닌 배열의 요소로서 일반 변수가 아닌 변수의 주소값을 가지는 것 일 뿐입니다.
2. 포인터 배열
선언 방법은 포인터를 저장할 배열이기 때문에 type * name[length]; 의 형태로 선언을 해야합니다.
#include <stdio.h>
int main(void)
{
int num1 = 10, num2 = 20, num3 = 30, num4 = 40, num5 = 50;
int * arr[5] = {&num1, &num2, &num3, &num4, &num5};
printf("%#x\n", arr[0]);
printf("%#x\n", arr[1]);
printf("%#x\n", arr[2]);
printf("%#x\n", arr[3]);
printf("%#x\n", arr[4]);
printf("\n");
printf("%d\n", *arr[0]);
printf("%d\n", *arr[1]);
printf("%d\n", *arr[2]);
printf("%d\n", *arr[3]);
printf("%d\n", *arr[4]);
return 0;
}
실행결과
0x4ff8cc
0x4ff8c0
0x4ff8b4
0x4ff8a8
0x4ff89c
10
20
30
40
50
계속하려면 아무 키나 누르십시오 . . .
배열 arr에 num1부터 num5까지의 주소값을 저장했습니다. 각 배열의 요소인 arr[0]~arr[4]까지를 출력하면 당연히 num1~num5의 주소값이 반환이 됩니다. 이제 arr[0]~arr[4]는 주소값이기 때문에 *연산자를 이용하여 그 주소값에 해당하는 메모리공간의 데이터에 저장할 수 있어요. 위의 코드를 그림으로 나타내보면 다음과 같습니다.
arr가 가지고 있는 것은 num1~ num5까지의 주소값이고 * 연산자를 이용해서 직접 그 데이터에 접근하는 것입니다.
배열에 포인터를 담을 수 있다고 했습니다. 그렇다면 배열의 하나의 요소로서 문자열 자체를 담을 수도 있습니다. 위에서 말씀드린 대로 큰 따움표 " "가 반환하는 것은 할당된 메모리공간의 가장 첫 번째 주소값이기 때문입니다.
#include <stdio.h>
int main(void)
{
char * arr[] = {"one", "two", "three", "four", "five"};
printf("%#x\n", arr[0]);
printf("%#x\n", arr[1]);
printf("%#x\n", arr[2]);
printf("%#x\n", arr[3]);
printf("%#x\n", arr[4]);
return 0;
}
실행결과
0xc75760
0xc7575c
0xc75754
0xc7574c
0xc75744
계속하려면 아무 키나 누르십시오 . . .
arr[i]로 데이터에 접근을 해서 출력을 했는데 그 결과를 보시면 배열 arr가 요소로 가지고 있는 것은 메모리공간의 주소라는게 나옵니다. 이처럼 포인터 배열은 주소값을 저장하기 때문에 첫 번째 주소값을 반환시켜주는 문자열을 저장할 수 있는 겁니다.
결국 포인터 배열은 일반변수가 아닌 포인터변수를 담고 있는 배열일 뿐인 것입니다. 그렇기 때문에 첫 번째 데이터의 주소값을 반환하는 "문자열" 역시도 담을 수 있는 것입니다.
'프로그래밍 언어 > C' 카테고리의 다른 글
(C언어) 27 - 포인터와 함수(2) (0) | 2020.05.14 |
---|---|
(C언어) 26 - 포인터와 함수 (0) | 2020.05.14 |
(C언어) 24 - 포인터와 배열 (0) | 2020.05.14 |
(C언어) 23 - 포인터 (0) | 2020.05.14 |
(C언어) 22 - 배열(2) - 배열과 문자열 (0) | 2020.05.14 |