[C언어] 포인터와 배열의 관계와 인덱스활용, 포인터를 활용한 문자열 표현

    배열과 포인터의 관계

    배열의 이름도 포인터이다. 단, 그 값을 바꿀 수 없는 '상수 형태의 포인터'이다.


    즉 배열 변수는 어떠한 값으로도 바꿀 수 없다. 포인터 변수와의 차이도 이것뿐이다. 이 것은 그냥 배경지식으로 알고 가고 다음 예제를 통해 배열의 값과 주소를 보자


    #include <stdio.h>
     
    int main() {
        // 배열
        int arr[3= { 51015 };
     
        printf("arr = %d\n", arr);
        for (int i = 0; i < 3; i++) {
            printf("arr[i] : %d\n", arr[i]);
        }
        for (int i = 0; i < 3; i++) {
            printf("&arr[i] : %d\n"&arr[i]);
        }
        return 0;
    }
    cs


    실행결과 2가지를 알 수 있다.  주소는 4씩 증가하는 것을 볼 수있다. 즉 "int형 배열요소간 주소 값의 차는 4바이트" 라는 것을 알 수 있고 

    또 arr의 값과 &arr[0]의 값은 같다. 그래서 arr의 값은 자신의 첫번째 인덱스 주소의 값이라는 것을 알 수 있다.


    앞서 포인터 변수는 주소를 저장하는 변수이다. 배열 arr도 똑같이 주소를 저장하고 있어 배열도 포인터 변수라고 할 수 있지만 위에서 배열의 값은 다른 값으로 바꿀수 없어 이렇게 정의한다.


    배열의 이름은 "상수 형태의 포인터" , "포인터 상수"  



    인덱스를 활용한 배열 데이터값 출력

    배열의 이름도 포인터이기 때문에 배열의 이름을 피연산자로 하는 * 연산이 가능하다.  다음 예제를 보자


    printf("*arr = %d\n"*arr);
    printf("*arr+1 = %d\n"*arr+1);
    printf("*(arr+1) = %d\n"*(arr+1));
    cs


    역시 예상대로 *arr은 arr[0]의 데이터가 나온다. 그러니까 *arr은 자기가 가리키는 데이터인데 arr이 arr[0]의 주소이기 때문에 arr[0]의 데이터가 나오는 것이다. 


    *arr + 1 의 값은 6이 나온다 *arr가 먼저 연산되고 5가된다 그리고 +1 되어서 값이 6이 나오는 것이다

    *(arr + 1) 은   arr 주소값이 하나 증가 한다 1바이트 증가하는건데 여기서 +1 은 바로 인덱스를 의미한다. 이 부분을 제대로 다시 보자


    #include <stdio.h>
     
    int main() {
        int arr1[3= { 51015 };
        double arr2[3= { 5.510.515.};
        
        printf("(arr1 + 1) = %d\n", (arr1 + 1));
        printf("(arr1 + 2) = %d\n", (arr1 + 2));
        printf("*(arr1 + 1) = %d\n\n"*(arr1 + 1));
     
        printf("(arr2+ 1) = %d\n", (arr2 + 1));
        printf("(arr2 + 2) = %d\n", (arr2 + 2));
        printf("*(arr2 + 1) = %.2f\n"*(arr2 + 1));
     
        return 0;
    }
    cs


    arr1 + 1 과 arr1 + 2의 주소의 차이는 4이다 하지만 arr2는 8씩 차이가 난다 여기서 확인할 수 있는건 int는 주소가 4바이트씩 증가하고 double은 주소가 8바이트씩 증가한다는 것이다. 아까 (arr + 1) 에서 1은 인덱스를 뜻한다고 했다. (arr + 1) 에서 1 은 arr[1] 이라고 볼 수 있다. 


    여기서 유추할 수 있는건 포인터를 대상으로 하는 증가연산의 결과는 


    int형 포인터를 대상으로 n증가  =  n X sizeof(int) 의 크기만큼 증가

    double형 포인터를 대상으로 n 증가 = n X sizeof(double)의 크기만큼 증가


    이라고 볼 수 있다. 이제 포인터변수에 배열주소를 저장하여 값을 다뤄보는 예제를 보자


    #include <stdio.h>
     
    int main() {
        int arr[3= {51015};
        int * ptr = arr;
        printf("%d %d %d \n"*ptr, *(ptr + 1), *(ptr + 2));
     
        printf("%d "*ptr); ptr++;
        printf("%d "*ptr); ptr++;
        printf("%d "*ptr); ptr--;
        printf("%d "*ptr); ptr--;
        printf("%d "*ptr); printf("\n");
     
        *(ptr + 1= 20;
        printf(" *(arr + 1) = %d\n"*(arr + 1));
     
        return 0;
    }
    cs


    포인터 변수 ptr에 arr을 저장하여 같은 배열을 바라보게 하였다. 그래서 *ptr, *(ptr + 1), *(ptr + 2) 는 arr배열의 값 순서대로 5, 10, 15가 찍히는 것이다.

    또 마지막으로 진짜로 같은 배열을 참조하고 있는지 확인하기 위해서 *(ptr + 1) = 20을 저장하여 *(arr + 1)을 출력하였더니 역시나 값은 20으로 바껴있었다.


    다시 정리하는 김에 arr[i] == *(arr + i) 라는 것을 기억하자



    두 가지 형태의 문자열 표현

    문자열을 선언은 2가지 방법이 있다.


    1. char str1[] = "My String";

    2. char * str2 = "Your String" 


    첫번째 방식은 배열을 이용하는 것이고 두번째 방식은 포인터 변수를 사용하는 것이다. 둘다 문자열을 선언하는 방법으로 똑같지만 엄청난 차이가 있다.



    그림에서 보듯이 첫번째 방식은 배열 자체가 만들어지고, 두번째 방식은 자동 할당된 문자열을 포인터 변수가 가리키는 것이다.


    이 차이에서 나오는 2가지는


    1. 배열이름 str1은 문자 M이 저장된 위치를 가리키는 상태이지만 포인터 변수 str2는 다른 위치를 가리킬 수 있다.

    2. 하지만 str1은 문자열을의 값을 바꿀수 있지만 str2는 문자열을의 값을 바꿀수 없다.


    다음 예제를 보자


    #include <stdio.h>
     
    int main() {
        char str1[] = "My String";
        char * str2 = "Your String";
        printf("%s %s \n", str1, str2);
     
        str2 = "Our String";
        printf("%s %s \n", str1, str2);
     
        str1[0= 'X';
        //str2[0] = 'X';
        printf("%s %s \n", str1, str2);
     
        return 0;
    }
    cs


    // str2[0] = 'X' 에서 컴파일 에러는 안나지만 str2의 값이 나오지 않는다 왜냐하면 str2의 문자열은 상수 형태의 문자열 이기 때문이다. 중요한것은 연산은 허용한다는 것이다. 


    다시 정리하자면 char str1[]을 "변수 형태의 문자열" 이라고 하고 char * str2 의 문자열을 "상수 형태의 문자열" 이라고 부른다.



    윤성우, 열혈 C프로그래밍, ORANGE MEDIA

    그림출처, http://hith77.tistory.com/35?category=763250

    댓글

    Designed by JB FACTORY