본문 바로가기
C C++/C언어 기초

C언어 - 포인터와 배열 이해의 모든 것 (배열 이름, 포인터로 배열 가리키기, 상수 형태 문자열의 포인터)

by Go! Jake 2022. 5. 2.

앞 서 살펴 본 것과 같이 포인터는 다른 변수의 주소를 저장하는 것을 의미하였습니다. 그렇다면 배열과의 관계는 어떨까요? 배열의 주소도 마찬가지로 포인터를 통해 저장할 수 있습니다.

포인터와 배열

우리는 앞 서 특정 변수의 주소를 포인터 변수에 저장하였고, 이를 포인터 변수를 통해 값에 접근하여 사용하였습니다. 요약하면 아래와 같습니다.

- 배열을 포인터 변수에 저장할 수 있는가? YES

- 배열 요소별로 포인터 변수에 저장할 수 있는가? YES

- 포인터를 통해 배열에 접근할 수 있는가? YES

즉, 배열이라는 형태만 다를 뿐 기본적인 내용은 일반 변수와 크게 다르지 않습니다.

 

첫 번째 알아야할 부분은 '배열의 이름'이 포인터라는 점입니다. 주소값입니다. 또한 '상수 형태의 포인터'로, 주소값 변경이 되지 않습니다.

#include <stdio.h>

int main(void)
{
	int arr[3]={0,1,2};
	
	printf("배열 이름: %p\n", arr);
	printf("배열 첫 번째 요소: %p\n", &arr[0]);
	printf("배열 두 번째 요소: %p\n", &arr[1]);
	printf("배열 세 번째 요소: %p\n", &arr[2]);
	
	return 0;
}

출력:배열 이름: 000000000062FE10
배열 첫 번째 요소: 000000000062FE10
배열 두 번째 요소: 000000000062FE14
배열 세 번째 요소: 000000000062FE18

 

여기서, 우리는 두 가지 특징을 눈 여겨 봐야합니다.

1) 배열 이름과 배열 첫 번째 요소의 주소는 같다. 즉 arr과 &arr[0]은 동일하다.

설명: 배열의 이름인 arr이 배열의 주소값이며 이는 arr[0]의 주소와 같습니다. 동일하지만 차이점으로 보면 주소값의 변경이 가능한지, 또는 불가능한지입니다. arr은 주소 값 변경되지 않습니다. '상수 형태의 포인터'입니다. arr[0]의 주소값은 변경이 가능합니다.

주소 값도 *연산자 사용하여 접근이 가능합니다.

2) 요소별 주소가 4씩 커지고 있다.

설명: 배열이 int형 배열이므로 각 요소별 할당 메모리 공간의 크기는 4바이트입니다. 4바이트씩 나란히 붙어서 할당되는 것을 알 수 있습니다.

배열 이름과 배열 요소에 접근하기

아래는 포인터 관점에서 '배열'이 가지는 특성입니다.

배열의 이름은 포인터이며 배열을 정의할 때 자료형이 포인터 자료형이 된다. 예를 들어 아래와 같다.

int arr[5];인 경우, arr은 int형 포인터 상수이다. int* arr과 같다. 즉 배열을 선언할 때 자료형이 1차원 배열이름의 포인터 형이 된다.

 

아래 예시를 살펴 보자.

#include <stdio.h>

int main(void)
{
	int arr[3]={0,1,2};
	printf("%d\n", *arr);
	return 0;
}

출력:

0

포인터인 배열 이름 arr에 *연산자를 통해 값에 접근하였습니다. 즉 arr[0]을 참조한 것입니다. 이에 따라 배열의 이름을 통해 첫 번째 요소에 접근하였습니다.

 

배열 이름을 통해 첫 번째 배열에는 접근을 하였는 데, 나머지 배열 주소에는 어떻게 접근할까요?

위에서 봤던 바와 같이 &arr[요소 번호]로 접근하면 됩니다.

#include <stdio.h>

int main(void)
{
	int arr[3]={0,1,2};
	printf("배열의 이름 %p\n", arr);
	printf("배열 첫번째 요소 주소 %p\n", &arr[0]);
	printf("배열 두번째 요소 주소 %p\n", &arr[1]);
	printf("배열 세번째 요소 주소 %p\n", &arr[2]);
	return 0;
}

출력:

배열의 이름 000000000062FE10
배열 첫번째 요소 주소 000000000062FE10
배열 두번째 요소 주소 000000000062FE14
배열 세번째 요소 주소 000000000062FE18

 

 

그리고 마찬가지로 해당 값에 일반 변수처럼 *연산자로 주소에 접근할 수 있습니다. *&arr[요소 번호]와 같은 형태입니다. 배열의 이름으로 접근하는 경우 *arr이 됩니다.

#include <stdio.h>

int main(void)
{
	int arr[3]={0,1,2};
	printf("배열의 이름 %d\n", *arr);
	printf("배열 첫번째 요소 주소 %d\n", *&arr[0]);
	printf("배열 두번째 요소 주소 %d\n", *&arr[1]);
	printf("배열 세번째 요소 주소 %d\n", *&arr[2]);
	return 0;
}

출력:

배열의 이름 0
배열 첫번째 요소 주소 0
배열 두번째 요소 주소 1
배열 세번째 요소 주소 2

포인터로 배열을 가리키는 경우

#include <stdio.h>

int main(void)
{
	int arr[3]={0,1,2};
	int* ptr = arr; // int* ptr = &arr[0]과 동일 
	
	printf("%d %d\n", *ptr, *arr);
	printf("%d %d\n", ptr[0], arr[0]);
	printf("%d %d\n", ptr[1], arr[1]);
	printf("%d %d\n", ptr[2], arr[2]);
		
	return 0;
}

출력:

0 0
0 0
1 1
2 2

간단히 int* ptr = arr과 같이 초기화하여 배열을 가리키고, 지정한 포인터를 배열과 같이 사용할 수 있습니다.

 

포인터의 증가 및 감소연산

포인터 연산은 배열에서 중요한 요소가 됩니다. 

#include <stdio.h>

int main(void)
{
	int arr[3]={0,1,2};
	int* ptr = arr;
	
	printf("%p\n", ptr);
	printf("%d\n", *ptr);
	ptr+=1;
	printf("%p\n", ptr);
	printf("%d\n", *ptr);

	return 0;
}

출력:

000000000062FE00
0
000000000062FE04
1

살펴 보면, ptr+=1을 하였는 데, 주소값은 4만큼 이동한 것을 알 수 있습니다. int형의 4byte만큼 이동한 것인데요, 쉽게 얘기해서 다음 배열 요소를 가리키고 있는 것입니다. 예를 들어, &arr[0] ==> &arr[1]로 참조하는 주소가 달라졌습니다.

 

각각 *ptr은 arr[0]에 저장된 요소, *(ptr+1)은 arr[1], *(ptr+2)은 arr[2]에 저장된 요소에 접근합니다.

 

결과적으로 우리는 아래 수식을 이해하고 외우는 게 좋습니다.arr[i]=*(arr+i), 즉 주소값은 포인터이고 배열 숫자에 의해 참조값을 바꿔 배열을 참조할 수 있습니다.

 

상수 형태의 문자열 을 가리키는 포인터

문자열은 주로 아래와 같이 선언된다.

1) char str1[ ] = "My String"

2) char* str2 = "My String"

 

1번은 변수 형태의 문자열이고 문자열 일부 변경이 가능하다. 따라서 '변수 형태의 문자열'이라고 한다.

2번은 포인터 기반 문자열이며 변경이 불가능하다. 따라서 '상수 형태의 문자열'이라고 한다.

 

1번 변수 형태의 문자열은 아래와 같이 변경이 가능하다.

#include <stdio.h>

int main(void)
{
	char str1[] = "My String";
	char* str2 = "My String";
	
	str1[0]='A';
	
	printf("%s", str1);
	
	return 0;
}

출력:

Ay String

 

2번 상수 형태의 문자열은 변경이 불가능하다.

#include <stdio.h>

int main(void)
{
	char* str1 = "My String";
	
	str1[0]='A';
	
	printf("%s", str1);
	
	return 0;
}

 

실행되지 않음.

 

결과적으로 char* str = "My String";의 선언은 아래와 같이 동작한다.

1) 문자열이 메모리 공간에 저장됨.

2) 저장된 주소값이 str에 저장됨.

해당 내용은 윤성우 열혈 C프로그래밍을 참조하였습니다.

 

댓글