ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [C] 포인터
    언어/C 2024. 10. 5. 13:26

    https://modoocode.com/23

     

    씹어먹는 C 언어 - <12 - 1. 포인터는 영희이다! (포인터)>

    모두의 코드 씹어먹는 C 언어 - <12 - 1. 포인터는 영희이다! (포인터)> 작성일 : 2009-11-09 이 글은 101063 번 읽혔습니다. 이번 강좌에서는포인터에 대한 완벽한 이해*, & 단항 연산자의 의미우왕~ 안녕

    modoocode.com

    아래 내용은 위 링크를 참고해서 만들었습니다. 

    포인터란

    • 메모리 상에 위치한 특정한 데이터의 (시작) 주소값을 보관하는 변수
    • 포인터에는 형이 있다.
      • 그 이유는 *p = &a 일 때, 포인터 p에는 변수 a의 주소값이 들어 있다. 문제는 a가 메모리에서 차지하는 모든 주소들의 위치가 들어 있는 것이 아니라 시작 주소만 들어가 있다. 
      • 따라서 *p 라고 했을 때, 컴퓨터는 메모리에서 얼마만큼 읽어들어야 할지 알 길이 없다. 
      • 즉, int *p; 이면 이 포인터가 "int 데이터를 가리키는 구나" 라고 알게 되어 시작 주소로 부터 정확히 4바이트를 읽어 들어 값을 바꾸게 된다.
      • 만약 주소 a, b, c, d에 각각의 값이 있다고 하더라도, int 타입 포인터 p가 이 주소를 가리키면, 그 4바이트를 결합하여 하나의 정수로 인식합니다.
      • 메모리 주소 a, b, c, d에 각각 저장된 값들이 예를 들어 0x01, 0x02, 0x03, 0x04라고 한다면, 이 값들은 int 타입으로 읽힐 때 다음과 같이 해석될 수 있습니다:
        • 리틀 엔디안 방식에서는 0x04030201로,
        • 빅 엔디안 방식에서는 0x01020304로 해석됩니다.
    #include <stdio.h>
    int main() {
        int *p;
        int a;
        p = &a;
        a = 2;
        printf("a의 주소는 %p \n", &a);
        printf("a의 값은 %d \n", a);
        printf("*p의 값은 %d \n", *p);
        printf("p가 가리키는 값은 %p \n", p);
        printf("p의 주소값은 %p \n", &p);
        return 0;
    }

     

    결과

    a의 주소는 0x7ffe4a95ecec 
    a의 값은 2 
    *p의 값은 2 
    p가 가리키는 값은 0x7ffe4a95ecec 
    p의 주소값은 0x7ffe4a95ecf0

     

    헷갈렸던 부분

    int a;
    int *p;
    
    p = &a;
    *p = 3;
    • 위와 같이 선언 되면 p에 a의 주소를 넣고, 값으로는 3을 넣겠다 라는 말이다. 
    • 그런데 포인터 변수를 선언할 때, 왜 *를 쓰지?
    • 그리고 주소를 넣는거면 *p = &a ; 는 성립되는 건가? 주소'값' 이라서 되나?
    • &p = &a; 해야될 거 같은 데 말이지. 
    • *는 선언할 때, 포인터임을 알리는 역할과 포인터가 가리키는 값을 가져와라 라는 2가지 의미를 가지고 있다.
    • (물론 곱셈도 있다.)
    • 따라서 int *p; 를 하게 되면 int 형을 가리키는 말이다. 따라서 시작 주소로부터 4바이트 만큼 가져오게 된다.  

     

    포인터 값 바꾸기

    #include <stdio.h>
    int main() {
        int a = 2;
        int *pa;
        pa = &a;
        *pa = 3;
        printf("a : %d \n", a);
        printf("*p : %d", *pa);
    }

     

    결과

    a : 3 
    *p : 3
    • pa는 a의 시작주소를 알게 된다. 
    • *pa를 통해  a에 직접 접근할 수 있다. 

    구조체 포인터

    • 구조체 변수는 타입이 제각각인 데 어떻게 포인터가 메모리에서 데이터를 읽어올까?
    • 구조체의 멤버(원소) 변수들이 메모리에서 연속적으로 저장된다. 각 멤버 변수는 선언된 순서대로 메모리에 배치되고 그에 맞게 포인터가 접근하게 된다. 
    • 각 멤버 변수의 오프셋을 계산해 정확한 메모리 위치에 접근하게 된다. 
    struct Person p1 = { 25, 'M', 180.5 };
    struct Person *p = &p1;

     

    • p->age를 참조하면, p가 가리키는 메모리의 시작 주소에서 첫 4바이트를 읽습니다.
    • p->gender는 age 다음의 메모리 주소에서 1바이트를 읽습니다.
    • p->height는 그 다음 메모리 주소에서 4바이트를 읽습니다
    • 구조체 포인터는 이렇게 구조체의 시작 주소를 가지고, 멤버 변수의 타입과 구조체 내부에서의 오프셋에 맞춰 각 변수를 읽어오는 방식으로 동작합니다.
    • 메모리 정렬을 통해 변수들 사이에 패딩을 추가해 all 4 byte로 맞출 수도 있음. 

    구조체 포인터 인자값 전달

    #include <stdio.h>
    struct test {
        int age;
        int gender;
    };
    int set_human(struct test a, int age, int gender);
    
    int main(){
        struct test human;
        set_human(human, 10, 1);
        printf("AGE : %d // Gender : %d ", human.age, human.gender);
        return 0;
    }
    
    int set_human(struct test a, int age, int gender) {
        a.age = age;
        a.gender = gender;
        return 0;
    }
    • 위 소스 코드에서는 함수에 인자를 잘 전달한 거 같지만 그렇지 못한다.
      AGE : 0 // Gender : 0 ​
    • 그 이유는 특정한 변수의 값을 다른 함수를 통해 바꾸려면 변수의 주소값을 전달해야 하기 때문이다. 
    • 실제 main 함수에서의 human이 아니라 set_human 함수의 a라는 human과 별래의 구조체 변수의 age 멤버의 값이 바뀌게 되는 것. 만약 main 함수에서 바꾸면 human.age = age; 해도 바뀐다. 
    • (*a).age = 1; == a->age = age; 같은 말이다. 
    • 함수 안에서 구조체를 변경할 때, 구조체 포인터를 사용해 원본 데이터를 변경할 수 있다. 만약 구조체를 포인터 없이 함수에 전달하면, 구조체의 복사본이 함수에 전달되기 때문에 원본이 수정되지 않는다. 
    #include <stdio.h>
    struct test {
        int age;
        int gender;
    };
    int set_human(struct test a, int age, int gender);
    
    int main(){
        struct test human;
        set_human(human, 10, 1);
        printf("main에서 AGE : %d // Gender : %d ", human.age, human.gender);
        return 0;
    }
    
    int set_human(struct test a, int age, int gender) {
        a.age = age;
        a.gender = gender;
        printf("구조체에서 a.age : %d \n", a.age);
        return 0;
    }
    구조체에서 a.age : 10 
    main에서 AGE : 0 // Gender : 0

     

    따라서 아래 소스 코드처럼 수정해줘야한다.

    #include <stdio.h>
    struct test {
        int age;
        int gender;
    };
    int set_human(struct test *a, int age, int gender);
    
    int main(){
        struct test human;
        set_human(&human, 10, 1);
        printf("main에서 AGE : %d // Gender : %d ", human.age, human.gender);
        return 0;
    }
    
    int set_human(struct test *a, int age, int gender) {
        a->age = age;
        (*a).gender = gender;
        return 0;
    }
Designed by Tistory.