본문 바로가기
C

포인터와 배열.

by oncerun 2023. 4. 29.
반응형

 

 

C언어의 Hello World는 문자열을 입력받는 것으로 보통 시작하더라.

 

그런데 문자열을 나타내는 타입이 없는 C언어에서 Hello World는 배열을 사용하는데 여기서 포인터의 개념이 바로 들어간다. 

 

무섭게 Hello World부터 포인터라니 

 

char 형 포인터를 알아보자. 

 

포인터 변수는 주소값을 갖는다고 공부했다. 그런데 어떻게 문자열을 받을 수 있을까?

char *cp = "hello world";

 

이상하다. 포인터 변수에는 주소를 할당한다고 했는데, 예제를 보면 문자열을 할당하고 있다. 

 

printf("%s\n", cp);
printf("%c\n", *cp);
printf("%c\n", *(cp + 1));
printf("%c\n", *(cp + 2));
printf("%c\n", *(cp + 3));

 

 

궁금하면 출력을 해보면 된다.

 

char형 포인터 변수에 문자열을 할당하면 포인터 변수는 문자열의 시작 주소를 갖는 것 같다.

    printf("%x\n", cp++);
    printf("%x\n", cp++);
    
    
    
    for(int i =0 ; i < 11; i++)
        printf("%c\n", cp[i]);

 

포인터 변수라면서 마치 배열과 같은 형식을 사용해 출력이 가능하다. 이것은 무엇일까?

 

 

배열과 포인터. 무슨 관계가 있는 것 같다. 

 

 

*cp에서. cp는 주소 값을 표현하고 그렇기에 주소값을 할당해 준다고 했다. *cp는 해당 주소에 있는 값을 표현할 때 사용한다고 했다. 

 

문자열의 시작 주소를 갖고 있다면 이를 포인터의 주소 번지를 1btye씩 증가시켜 반복해도 출력할 수 있을 것이다.

for(int i =0 ; i < 11; i++)
    printf("%c\n", *(cp + i));
        
        
        == 출력 ==
h
e
l
l
o
 
w
o
r
l
d

 

우선 앞에서 보았던 포인터와 배열의 관계도 보아야 한다. 

 

1. 1차원 배열의 참조. 

 

    char s[] = "computer";
    char *p = s;

포인터를 사용하여 배열의 내용을 참조할 수 있다. 

 

배열도 주소를 저장하고 있구나. 그렇기에 기억공간의 주소를 가진다는 부분에 있어 서로 같은 개념일 수 있겠다. 

 

p의 주소 값에 s[] 배열의 시작주소를 갖고 있다는 것이다. 배열은 주소공간에 크기만큼 물리적으로 순차적으로 배치되어 있으니 포인터 변수의 다음 주소로 이동하면서 값을 출력하면 배열의 모든 값을 순회할 수 있는 것이다. 

 

그렇다면 다음은 같을 것이다.

 

s [1] == *(p+1) == p [1]는 실제 값이 같을 것이다.

 

p+1 == &s[1] 는 동일한 주소 값을 가지고 있다. 

 

 

2차원 배열은 조금 다른게, 행과 열이라는 개념이 있는데, 포인터를 증감연산자를 통해 늘렸을 때 

 

a [2][3]이라고 가정하면 a [1][0]을 표현하는 포인터는 p + 3이 될 것이다. 

 

이는 배열을 포인터 형식으로 사용할 수도 있고, 포인터를 배열 형식으로 참조할 수 있는 상호 호환관계에 있다는 것을 알 수 있다.

 

 

차이가 있다. 기억공간의 확보이다. 

 

배열은 기억공간을 고정적으로 확보한다. 

포인터는 기억공간 중 자료영역을 유동적으로 확보할 수 있다. 

 

어 근데 너무 위험하지 않나? 가변적으로 포인터의 공간을 확보하다 부족하면?..

 

런타임에 공간이 필요해서 포인터의 주소 값을 늘려서 특정 값을 할당할 수 있다는 건데 매우 유동적이긴 하지만 이게 안전한 건가?  안전하게 코드를 작성하지 않을까?

 

음.. 포인터 배열에 대해 알아보자..

 

포인터 배열을 말 그대로 포인터 변수들을 담고 있는 배열이다. 

 

hello world를 하다 보면 문자열을 담는 경우가 있는데 이 경우 char *p [3]와 같이 선언해서 사용하는 것을 보았다. 

 

그럼 이는 메모리에 어떻게 표현될까?

 

포인터 배열의 주소를 하나를 할당하고. 그런데 주소 길이는 어떻게 정의하지?

 

포인터는 가변적이라면서 미리 포인터 변수의 주소를 초기화한다면 그만큼 차지할 것 같은데 

 

가변적으로 추가하면 의도치 않게 다른 메모리 주소의 값을 참조할 수 도 있지 않을까? 

 

이에 대한 답은 다음과 같다.

 

포인터 배열은 말 그대로 포인터 변수들을 담고 있는 배열입니다. 예를 들어, `char *p [3]`는 크기가 3인 포인터 배열을 선언한 것으로, 이 배열은 `char` 형식을 가리키는 포인터 변수 3개를 담을 수 있습니다.

포인터 변수는 메모리 주소를 저장하기 때문에, 포인터 배열은 배열의 각 요소에 저장된 포인터 변수들이 가리키는 메모리 주소를 가집니다. 

 

이러한 메모리 주소의 길이는 포인터 변수가 가리키는 자료형에 따라 결정됩니다. 예를 들어, `char` 형식을 가리키는 포인터 변수의 길이는 1바이트이며, `int` 형식을 가리키는 포인터 변수의 길이는 4바이트입니다.

포인터 배열의 각 요소는 배열의 인덱스를 사용하여 접근할 수 있으며, 각 요소는 포인터 변수를 담고 있습니다. 

 

이러한 포인터 변수의 주소는 배열의 시작 주소와 포인터 변수의 크기를 곱한 값으로 결정됩니다. 

 

예를 들어, `char *p[3]` 배열의 시작 주소가 `0x1000`이고, 각 포인터 변수의 크기가 4바이트라면, 

 

`p [0]`의 주소는 `0x1000`이 되고, `p [1]`의 주소는 `0x1004`이고, `p [2]`의 주소는 `0x1008`이 됩니다.

포인터 배열에 요소를 추가하는 것은 배열의 크기를 늘리는 것과 같습니다.

 

 따라서, 새로운 요소를 추가하려면 배열의 크기를 늘려야 합니다.

 

 이를 위해서는 새로운 메모리 공간을 할당하고, 기존의 배열 요소들을 복사한 후에 새로운 요소를 추가해야 합니다.

 

 이 과정에서 의도치 않은 메모리 접근이 일어날 가능성은 있지만, 메모리를 올바르게 할당하고 복사하는 등의 작업을 정확하게 수행한다면 이를 방지할 수 있습니다.

 

 

확실히 2차원 배열보다는 기억공간의 절약효과가 있다. 포인터 자료의 크기만큼 기억공안을 할당하기 때문이다. 

 

이중 포인터

 

이중 포인터는 포인터 변수의 주소를 가리키는 포인터입니다. 이를 이용하면 다음과 같은 예시를 작성할 수 있습니다.


#include <stdio.h>

void change_value(int **p) {
    int b = 2;
    *p = &b;
}

int main() {
    int a = 1;
    int *p = &a;
    printf("p: %p, *p: %d\n", p, *p); // p: 0x7ffee5a5b5c8, *p: 1

    change_value(&p);

    printf("p: %p, *p: %d\n", p, *p); // p: 0x7ffee5a5b5c8, *p: 2
    return 0;
}



위 예시에서 `change_value` 함수는 이중 포인터 `p`를 인자로 받고 있습니다. 이 함수는 `b`라는 변수를 선언하고, `b`의 주소를 `p`가 가리키는 포인터 변수에 저장하고 있습니다.

`main` 함수에서는 `a`라는 변수를 선언하고, `p` 포인터 변수를 이용하여 `a`의 주소를 저장하고 있습니다. `p`를 출력하면 `a`의 주소를 가리키고 있음을 확인할 수 있습니다.

그리고 `change_value` 함수를 호출하면서 `&p`를 전달하고 있습니다. 이는 `p` 포인터 변수의 주소를 가리키는 포인터 변수의 주소를 전달하고 있음을 의미합니다. 따라서 `change_value` 함수에서 `*p`를 이용하여 `p`가 가리키는 포인터 변수에 `b`의 주소를 저장하면, `main` 함수에서 `p`를 출력할 때 `b`의 주소가 출력되게 됩니다.

따라서 위 예시에서는 이중 포인터를 이용하여 함수 내에서 포인터 변수에 저장된 값을 변경하고, 이를 함수 외부에서도 반영할 수 있게 되었습니다.

 

 

 

 

 

 

반응형

'C' 카테고리의 다른 글

메모리 동적 할당  (0) 2023.05.02
구조체  (0) 2023.05.01
포인터  (0) 2023.04.29
C언어에서 배열을 어떻게 다룰까?  (0) 2023.04.17
입력 및 출력 프로그램  (0) 2023.04.16

댓글