[C언어] 배열, 포인터

강지원·2024년 11월 17일

리눅스 기반 C언어

목록 보기
14/24
post-thumbnail

1. 배열

- 하나씩 변수로 지정해줘도 되지만, 배열로 저장하면 주소가 나란히 저장됨.
- 간격은 자료형의 크기와 같음

1-1. 선언

자료형 변수이름[변수개수];


int num[3] = {}; // 0으로 초기화

-> num[0] num[1] num[2] // 0부터 시작 

1-2. 사용

1-3. 배열의 주소

int a = arryA // arryA 메모리주소의 시작번지

2. 포인터

메모리 공간에는 기계어가 쓰여져있음.(명령어인지, 변수인지 - 메모리는 알 수가 없음)

포인터 : 메모리 값을 담아두는 그릇
ex)
1) int a -> 0X123 메모리주소에 10으로 저장됨.
2) 포인터 int* pa 선언 : 메모리 번지 0X123을 저장함.
3) a의 주소를 저장하는 법 pa = &a
4) *pa=3; -> pa에 들어있는 주소 0x123으로 가서 0x123번지의 값을 3으로 바꿔라.

a = 3
pa == a의 주소 == &a
pa == pa에 있는 주소 안의 값 == 3
주소,포인터로 사용하려면 pa
그 안에 있는 값을 사용하려면
pa

처음 선언할 때 int * pa에서 *은 포인터라는 것을 알려주기 위함이다.

2-2 다중 포인터

2-3 포인터 자료형

int : 4바이트 정수를 할당해줘
int * : 4바이트 포인터를 선언해줘(int 형태의 주소값을 넣을 것이라고 암시적표현)

2-4 포인터 자료형의 size

int * : 8 바이트
char * : 8 바이트
float * : 8 바이트
double * : 8바이트
short * : 8바이트
-> 메모리 주소값을 담기 때문에 메모리 주소의 최대값을 담을 수 있어야 하기 때문

컴퓨터 구조
32bit 컴퓨터 : 라인이 32개 -> 메모리를 32비트만큼 읽어올 수 있음(32비트 = 4바이트)

64bit 컴퓨터 : 라인이 64개 -> 메모리 64비트 만큼 읽어옴(8바이트)

3. 배열 == 포인터 ?

만약 아래와 같은 배열이 있다면

int a[10];

a는 a[10]의 시작주소를 가지고 있는 포인터이다.
따라서 a = &a[0] 이다.
이를 보여주자면

위와 같은 코드가 있다고 하자.
어셈블리코드로 확인해 보면

둘 다 같은 주소에 저장되는 것을 확인할 수 있다
따라서 arry는 포인터로 사용되는 것이다.

3-1 포인터에 자료형이 필요한 이유

포인터는 주소값을 저장하는 것이기 때문에 자료형이 뭐든 간에 8byte이다.
그럼 왜 자료형을 적어주는가?

이러한 코드를 어셈블리어로 확인해보면

이렇게 확인할 수 있다.

int형인 배열에서, 코드에서는 (a+2),(a+3),(a+4)를 실행해도 실제로는 주소값 4바이트씩 차이나도록 저장이 된다.

char형인 배열에서, 코드에서는 (c+2),(c+3),(c+4)를 실행해도 실제로는 주소값 1바이트씩 차이나도록 저장이 된다.

따라서 컴파일러가 선언된 자료형을 보고 알아서 주소값 계산을 해주는 것이다.

4. swap 함수

포인터를 이용해 값을 변경하는 코드이다.

#include <stdio.h>

int swap(int * pa);
int main(){

        int a[6] = {0};
        int i = 0;
        for(i = 0;i<6;i++){
                printf("a[%d] = %d\n",i,a[i]);
        }
        printf("------------------\n");

        swap(a);
        for(i = 0;i<6;i++){
                printf("a[%d] = %d\n",i,a[i]);
        }
        return 1;
}
int swap(int * pa){

        *(pa + 3) = 99;
        *(pa + 5) = 33;

        return 1;
}

실행결과는 아래와 같다

a[0] = 0
a[1] = 0
a[2] = 0
a[3] = 0
a[4] = 0
a[5] = 0
------------------
a[0] = 0
a[1] = 0
a[2] = 0
a[3] = 99
a[4] = 0
a[5] = 33

swap 함수에서 포인터를 넘겨 달라했는데 a를 넘겼더니 에러없이 된다.
a 가 포인터 역할을 한다는게 성립한다.

main 함수에서 swap함수를 호출하면 어떤 일이 일어나는지 보자.

  1. main 함수 호출
    스택공간에 main함수의 필요한 용량을 확보를 하고 그 공간을 기준으로 연산을 한다.
    베이스 포인터 레지스터(rbp)를 기준으로 변수에 접근한다.

  2. swap 함수 호출
    메인으로 돌아올 수 있는 정보를 스택에 저장하고 sp는 swap 함수쪽으로 올라간다. rbp는 원래 자리를 가리키고 있음
    swap 함수에 필요한 변수공간들을 스택에 할당해주고, rbp도 같이 위치가 올라간다.
    함수 내의 지역변수는 rbp를 기준으로 하다보니까 main 함수를 접근할 수 없게 된다.
    swap 함수가 main 함수에서 참조한 주소를 가지고 메인함수의 주소로 가서 값을 변경할 수 있는 것이다.

  3. swap 함수 종료
    swap 함수가 바로 삭제되는 것이 아니라 SP가 메인정보로 내려오고 메인정보에 따라 main 함수 스택공간으로 내려옴
    그러다가 다른 함수(예: sum() )를 호출하게 되면 스택에 있던 swap 함수가 지워지고 새로운 함수를 스택 공간에 할당해준다

5. 포인터 가지고 놀기

5-1 swap()을 이용해서 순서 바꾸기

  1. swap() 포인터 인자에 주소를 넘겨주는 형태
#include <stdio.h>

void swap(int * a, int * b){
        int c;
        c = *a;
        *a = *b;
        *b = c;
}

int main(){

        int a = 3;
        int b = 4;

        swap(&a,&b);
        printf("a = %d, b = %d\n",a,b);
        return 1;
}     
  1. swap() 포인터 인자에 포인터를 넘겨주는 형태
#include <stdio.h>

void swap(int * a, int * b){
        int c;
        c = *a;
        *a = *b;
        *b = c;
}
int main(){

        int a = 3;
        int b = 4;
        
        int *pa = &a;
        int *pb = &b;
        
        swap(pa,pb);
        printf("a = %d, b = %d\n",a,b);
        return 1;
}

5-2 while문으로 출력

#include <stdio.h>

int main(){
        char str[100] = "hello world\n";
        char * pstr = str;

        while(*pstr){
                putchar(*pstr++);
        }

        return 1;
}

5-3 함수형 포인터

#include <stdio.h>

void test(){
        printf("func test\n");
}
void test22(){
        printf("func test22\n");
}

int main(){

        void (*fp)();
        fp = test;
        fp();

        fp = test22;
        fp();

        return 1;
}

0개의 댓글