-
씹어먹는 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; }
'언어 > C' 카테고리의 다른 글
[C] 문자열 저장 : 메모리구조 (0) 2024.05.30 [C] boostcamp 코딩테스트 유형 Q.5 (0) 2024.05.30 [C] boostcamp 코딩테스트 유형 Q.4 (0) 2024.05.30 [C] while문으로 1부터 100구하기, 반복조건 count <101일까? count == 100일까? (0) 2024.05.30