배열의 이름은 포인터 

배열의 이름은 포인터이며 값을 변경할 수 없는 상수 형태의 포인터이다. 다음 코드를 보자.

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
 
int main(void) {
    int arr[3= { 123 };    
    printf("배열의 이름 : %p \n", arr);
    printf("배열의 첫번째 원소 : %p \n"&arr[0]);
    printf("배열의 두번째 원소 : %p \n"&arr[1]);
    printf("배열의 세번째 원소 : %p \n"&arr[2]);
 
    return 0;
}
cs

<실행결과>

위의 코드와 실행결과를 통해 배열 arr가 int형 이므로 각각 4바이트씩 주소 값의 차이가 나는 것을 볼 수 있으며 모든 배열 요소가 메모리 공간에 나란히 할당된다는 것을 알 수 있다. 또한 배열의 이름의 주소 값이 010FFB88이고 배열의 첫 번째 원소의 주소 값이 010FFB88로 동일한 것을 알 수 있다.

또한 배열의 이름은 대입 연산자의 피연산자가 될 수 없으므로 다음과 같이 정리할 수 있다.

 

배열의 이름은 배열의 시작 주소 값을 의미하며, 그 형태는 값의 저장이 불가능한 상수이다.

 

즉 배열의 이름은 상수형태의 포인터이다. 따라서 배열의 이름을 "포인터 상수"라고 부른다.

결국 배열의 이름은 포인터 이므로 * 연산자를 사용할 수 있다.

<소스코드>

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
 
int main(void) {
    int arr[3= { 123 };    
    
    printf("배열의 첫번째 원소 : %d \n"*arr);
    *arr += 100;
 
    printf("%d %d %d", arr[0], arr[1], arr[2]);
    return 0;
}
cs

 

<실행결과>

*연산자를 통해 6번 라인에서 *arr을 출력하는 것을 알 수 있다. 이 경우 배열의 첫 번째 원소가 출력이 된다.

또한 7번 라인을 통해 배열의 첫 번째 원소에 접근하여 100을 더하여 101로 변경이 된 것을 확인할 수 있다.


포인터를 대상으로 하는 증가 감소 연산

먼저 다음의 예제를 먼저 살펴보자.

int* ptr1 = 0x0010;

이 경우 ptr1++;를 하게 되면 0x0011이 아니라 int형이므로 4byte가 증가하여 0x0014가 된다.


double* ptr2 = 0x0010;

이 경우 ptr2++;를 하게 되면 0x0011이 아니라 double형이므로 8byte가 증가하여 0x0018이 된다.

 

즉 이를 일반화하면 type형 포인터를 대상으로 n의 크기만큼 증가 혹은 감소 시 n*sizeof(type)의 크기만큼 증가 혹은 감소한다는 것을 알 수 있다.


결론

그래서 우리는 중요한 사실을 유도할 수 있는데 바로 arr[i] == *(arr + i) 라는 것이다.

arr + i 는 arr의 type만큼 주소 값이 증가하기 때문에 arr [i]라는 것이다. 이것은 매우 중요하므로 잘 기억해야 한다.


예제

길이가 5인 int형 배열 arr을 선언하고 이를 1, 2, 3, 4, 5로 초기화 한 다음 이 배열의 첫 번째 요소를 가리키는 포인터 변수 ptr을 선언한다. 그다음 포인터 변수 ptr에 저장된 값을 증가시키는 형태의 연산을 기반으로 포인터 변수 ptr에 저장된 값을 변경시키지 않고 값을 3씩 증가시키고, 정상적으로 증가가 이루어졌는지 확인하는 예제를 작성해보자. (출처 윤성우 님의 열혈 C 프로그래밍 chapter 13 p.299 문제 1)

<소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
 
int main(void) {
    int arr[5= { 12345 };    
    int* ptr = arr; 
 
    for (int i = 0; i < 5; i++) { // ptr로 모든 원소 2씩 증가
        *(ptr + i) += 2;
    }
 
    for (int i = 0; i < 5; i++) { // 확인하기 위해 출력하는 
        printf("%d ", arr[i]);
    }
    
    return 0;
}
cs

<실행결과>


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

문자열을 표현하는 방식에는 2가지가 있다.

  1. 배열을 기반으로 하는 문자열 - char str[] = "Hello world!";
  2. 포인터를 이용하는 문자열 - char * str = "Hello world!";

이를 그림으로 표현하면 다음과 같다.

배열을 기반으로 하는 문자열
포인터 변수 str

배열을 기반으로 하는 문자열은 변경이 가능하다. 그렇지만 포인터를 이용하는 문자열은 변경이 불가능하다.  사실상 배열 형태의 str, 포인터 변수 str 모두 첫 문자 H의 주소 값을 가리키긴 하지만 배열 str은 다른 것을 가리킬 수 없는 반면에 포인터 변수 str은 다른 위치를 가리킬 수 있다. 다시 말하면

char * str = "Hello world!";

str = "Bye~";

로 변경이 가능하다는 뜻 이다. 예시를 통해 살펴보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
 
int main(void) {
    char str1[] = "This is Array";
    const char* str2 = "This is Pointer";
    printf("str1 : %s \nstr2 : %s", str1, str2);
 
    //str1 = "This is Array and can't change"; // 배열 형태는 가르키는 대상 변경 불가능
    str2 = "This is Pointer and can change!"// 포인터 형태는 가르키는 대상 변경 가능
    printf("str1 : %s \nstr2 : %s", str1, str2);
 
    str1[0= 't'// 배열 형태는 문자열 변경 가능
    //str2[0] = 't'; // 포인터 형태는 문자열 변경 불가능
 
    return 0;
}
cs

<실행결과>


포인터 배열

포인터 변수로 이루어져 주소 값의 저장이 가능한 배열이 포인터 배열이고 선언방식은 다음과 같다.

int * arr1[20]; // 길이가 20인 int형 포인터 배열 arr1

dounle * arr2[10]; // 길이가 10인 double형 포인터 배열 arr2

예제는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
 
int main(void) {
    int num1 = 10, num2 = 20, num3 = 30;
    int* arr[3= { &num1, &num2, &num3 };
 
    for (int i = 0; i < 3; i++) {
        printf("%d\n"*arr[i]);
    }
    return 0;
}
cs

<실행결과>

 

이를 그림으로 표현하면 다음과 같다.

이는 다음의 형태로 문자열을 저장할 수도 있다. 위와 비슷하게 예시 코드를 보자.

 

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
 
int main(void) {
    const char* strArr[3= { "Simple""String""Array" };
 
    for (int i = 0; i < 3; i++) {
        printf("%s\n", strArr[i]);
    }
    return 0;
}
cs

<실행결과>


 

'C' 카테고리의 다른 글

6. 다차원 배열  (0) 2021.06.25
5. 포인터와 함수  (0) 2021.06.25
3. 포인터  (0) 2021.06.21
2. 배열을 이용하여 문자열의 표현  (0) 2021.06.21
1-1 배열 예제  (0) 2021.06.21

+ Recent posts