포인터와 배열의 관계


사실 배열의 이름은 포인터이다. 단 그 값을 바꿀 수 없는 상수 형태의 포인터 이다.

ex)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
 
int main()
{
    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;
}
 
 

cs

출력결과
배열의
이름 : 0x7ffeefbff5fc 

번째 요소 : 0x7ffeefbff5fc 

번째 요소 : 0x7ffeefbff600 

번째 요소 : 0x7ffeefbff604 


int형 배열요소간 주소 값의 차는 4바이트 이다. 

모든 배열요소가 메모리 공간에 나란히 할당된다 는 것을 위에 예제를 통해 확인할 수 있다.

또 한, 배열의 첫 번째 바이트의 주소 값과, 배열의 이름의 주소 값이 같음을 알 수 있다.


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


포인터변수와 배열의 이름의 비교 !


주소 값의 변경만 차이점을 보고 있다. 이 점에서 배열의 이름은 '상수 형태의 포인터'라고 말할 수 있다. 그래서 배열의 이름을 '포인터 상수'라고 부르기도 한다.


1차원 배열이름의 포인터 형과 배열이름을 대상으로 하는 *연산


int arr[5]; // arr1은 int형 포인터 상수

배열 arr이 가리키는 것의 배열의 첫 요소이다. 배열의 첫 요소가 int형 변수이니 int *(int형 포인터)라는 결론

double arr2[7] ; // arr2 는 double형 포인터 상수

배열 arr2이 가리키는 것의 배열의 첫 요소이다. 배열의 첫 요소가 doble형 변수이니 doble *(double형 포인터)라는 결론


1차원 배열이름의 포인터 형은 배열의 이름이 가리키는 대상을 기준으로 결정하면 된다.


ex)*연산

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>
 
int main()
{
    int arr1[3]={10,20,30};
    double arr2[3]={1.1,2.2,3.3};
    
    printf("%d %g \n",*arr1,*arr2);
    *arr1 += 100;
    *arr2 += 120.5;
    printf("%d %g \n",*arr1,*arr2);
    
    return 0;
}
 
 

cs

출력결과

10 1.1 

110 121.6 


포인터를 배열의 이름처럼 사용할 수 있다.

배열의 이름과 포인터 변수는 변수나 상수냐의 특성적 차이가 있을 뿐, 둘 다 포인터이기 때문에 포인터 변수로 할 수 있는 연산은 배열의 이름으로도 할 수 있고, 배열의 이름으로 할 수 있는 연산은 포인터 변수로도 할 수 있다.

ex)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>
 
int main()
{
    int arr[3]={15,25,35};
    int *ptr = arr; // int *ptr=&arr[0] 과 동일한 문장
    
    printf("%d %d \n",ptr[0],arr[0]);
    printf("%d %d \n",ptr[1],arr[1]);
    printf("%d %d \n",ptr[2],arr[2]);
    printf("%d %d \n",*ptr,*arr);
    
    return 0;
}
 
 

cs

출력결과

15 15 

25 25 

35 35 

15 15 


6행 : 주석에서 보이듯이 배열이름을 이용해서 변수 ptr을 초기화해도 그 결과는 같다.

8~11행 : 포인터 변수와 배열의 이름을 대상으로 수행가능한 연산의 형태에는 차이가 없다.


포인터연산

포인터 변수를 대상으로 하는 연산은 증가 및 감소만 가능하다. ( + , - , ++ , -- , += , -= )


ex) sizeof(type)만큼 증가 및 감소가 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<stdio.h>
 
int main()
{
    int *ptr1 = 0x0010;
    double *ptr2 = 0x0010;
    
    printf("%p %p \n", ptr1+1, ptr1+2); // 4증가하고 8증가한다.
    printf("%p %p \n", ptr1+1, ptr1+2); // 8증가하고 16증가한다.
    
    printf("%p %p \n", ptr1, ptr2);
    ptr1++;     // 4증가한다.
    ptr2++;     // 8증가한다.
    printf("%p %p \n", ptr1, ptr2);
    
    return 0;
}
 
 

cs

출력결과

00000014 00000018    // sizeof(int)만큼 4증가, 8증가

00000018 00000020    // sizeof(double)만큼 8증가, 16증가

00000010 00000010    // 8행과 9행에서 진행된 +연산은 피연산자의 값을 변경시키는 연산이 아니므로 값 변경 X 

00000014 00000018    // 12,13행 : ++ 이는 포인터 변수에 저장된 값 자체를 변경시키는 연산


int형 포인터를 대상으로 1을 증가시키면 4가 증가하고 double형 포인터를 대상으로 1을 증가시키면 8이 증가한다.

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

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


ex)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<stdio.h>
 
int main()
{
    int arr[3= {11,22,33};
    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");
    
    return 0;
}
 
 

cs

출력결과

11 22 33 

11 22 33 22 11


6행 : 포인터 변수 ptr이 배열arr을 가리키고 있다.

ptr은 int형 포인터이므로 값을 1 증가시키는 연산을 할 때마다 실제로는 4가 증가한다. 

*(++ptr);    // ptr에 저장된 값 자체를 변경

*(ptr+1);    // ptr에 저장된 값은 변경되지 않음


arr[i] == *(arr+i) // 중요한 결론


arr[0]     arr[1]     arr[2]

*(arr+0)   *(arr+1)   *(arr+2)


위의 두 문장은 같다 !


ex)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
 
int main()
{
    int arr[3= {11,22,33};
    int *ptr=arr;
   
    printf("%d %d \n",ptr[0],*arr);
    printf("%d %d \n",ptr[1],*(arr+1));
    printf("%d %d \n",ptr[2],*(arr+2));
    
    return 0;
}
 
 

cs

출력결과

11 11 

22 22 

33 33 

같다!


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


두 가지 형태의 문자열 표현


char str1[ ] = "My String";

이는 배열을 기반으로 하는 '변수 형태의 문자열' 선언이다. 변수라 하는 이유는 문자열의 일부를 변경할 수 있기 때문이다.


char *str2 = "Your String";

이는 메모리 공간에 문자열 "Your String"가 저장되고, 문자열의 첫 번째 문자인 Y의 주소 값이 반환된다. 그리고 그 반환 값이 포인터 변수 str2에 저장된다. 그래서 str2를 char형 포인터로 선언한 것이다. char형 문자 Y의 주소 값이 저장되기 때문에


str1은 그 자체로 문자열 전체를 저장하는 배열, str2는 메모리상에 자동으로 저장된 문자열 'Your String"의 첫 번째 문자를 단순히 가리키고만 있는 포인터 변수이다. 하지만 str1도 실제로 문자 M의 주소 값이기 때문에 주소값을 담고 있다는 측면에서는 동일하다. 무엇이 차이가 있을까 ? 차이점은 배열이름 str1은 계속해서 문자 M이 저장된 위치를 가리키는 상태이어야 하지만 포인터 변수 str2는 다른 위치를 가리킬 수 있다.

char *str = "Your team"; 

str = "Our team"; // str이 가리키는 대상을 Our team으로 변경


하지만 배열 이름인 str1은 상수형태의 포인터이기 때문에 위와 같이 가리키는 대상을 변경할 수 없다.

또 하나 차이점


char str1[ ] = "My String";


위 문장은 애초에 문자열이 배열에 저장된다. 그리고 배열을 대상으로 값의 변경이 가능하기 때문에 이를 '변수형태의 문자열' 이라고 한다.


char *str = "Your String"; 이는 상수형태의 문자열


ex)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#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


포인터 변수로 이루어진 포인터 배열

포인터 변수로 이뤄진, 그래서 주소 값의 저장이 가능한 배열을 가리켜 포인터 배열 이라고 한다.

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

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


ex)

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

cs

출력결과

10

20

30


arr[0] -> 10 (num1)

arr[1] -> 20 (num2)

arr[2] -> 30 (num3)


문자열도 저장할 수 있다.


char *str[3]; // 길이가 3인 char형 포인터 배열

ex)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdio.h>
 
int main()
{
    char * str[3]={"Simple","Cprogramming","Hello"};
    
    printf("%s\n",str[0]);
    printf("%s\n",str[1]);
    printf("%s\n",str[2]);
    
    return 0;
}
 
 

cs

출력결과

Simple

Cprogramming

Hello


5행 : 큰 따옴표로 묶여서 표현되는 문자열은 그 형태에 상관없이 메모리 공간에 저장된 후 그 주소값이 반환된다.

즉 위 문장이 실행되면 초기화 리스트에 선언된 문자열들은 메모리 공간에 저장되고, 그 위치에 저장된 문자열의 주소 값이 반환된다. 따라서 문자열이 저장된 이후에는 다음의 형태가 된다.

char *str[3] = {0x1004, 0x1048, 0x2018};     반환된 주소 값은 임의로 결정

반환된 주소 값은 문자열의 첫 번째 문자의 주소 값이니, char형 포인터 배열에 저장이 가능한 것이다.

'programming > C' 카테고리의 다른 글

C // 다차원배열  (0) 2017.12.22
C // 포인터와 함수  (0) 2017.12.21
C // 포인터  (0) 2017.12.19
C // 1차원배열  (0) 2017.12.18
C // 재귀함수  (0) 2017.12.16

+ Recent posts