728x90
반응형
C 언어 포인터 완전 정복: 초보자를 위한 친절한 안내서
1. 포인터란 무엇일까요?
- 컴퓨터는 데이터를 메모리라는 공간에 저장합니다. 각 데이터는 메모리 상의 특정 위치(주소)에 저장되는데, 이 주소를 저장하는 특별한 변수가 바로 포인터입니다.
- 즉, 포인터는 데이터가 있는 곳을 가리키는 표지판과 같습니다.
2. 왜 포인터를 사용할까요?
- 효율적인 메모리 관리: 큰 데이터를 직접 복사하는 대신, 포인터를 이용해 데이터의 위치만 전달하면 메모리 사용량을 줄일 수 있습니다.
- 함수 간 데이터 공유: 함수에 포인터를 전달하면, 함수 내부에서 원본 데이터를 직접 변경할 수 있습니다.
- 동적 메모리 할당: 프로그램 실행 중에 필요한 만큼 메모리를 할당하고 해제할 수 있어, 메모리 사용을 유연하게 조절할 수 있습니다.
- 데이터 구조 구현: 연결 리스트, 트리 등 복잡한 데이터 구조를 구현하는 데 필수적입니다.
3. 포인터 선언 및 사용
- 선언: 자료형* 포인터_변수_이름;
- 예: int* ptr; (int형 데이터의 주소를 저장하는 포인터 ptr 선언)
- 주소 할당: 포인터_변수_이름 = &변수_이름;
- & 연산자는 변수의 주소를 가져옵니다.
- 예: ptr = # (변수 num의 주소를 포인터 ptr에 저장)
- 간접 참조: *포인터_변수_이름
- * 연산자는 포인터가 가리키는 곳에 저장된 값을 가져옵니다.
- 예: printf("%d", *ptr); (ptr이 가리키는 곳에 저장된 값 출력
4. 예시로 이해하기
#include <stdio.h>
int main() {
int num = 10; // num 변수 선언 및 초기화
int* ptr = # // ptr 포인터 선언 및 num의 주소 할당
printf("num의 값: %d\n", num); // num 값 출력: 10
printf("ptr이 가리키는 값: %d\n", *ptr); // ptr이 가리키는 값 출력: 10
printf("num의 주소: %p\n", &num); // num의 주소 출력
printf("ptr의 값: %p\n", ptr); // ptr의 값(num의 주소) 출력
*ptr = 20; // ptr이 가리키는 곳의 값 변경
printf("num의 값 변경 후: %d\n", num); // num 값 출력: 20
return 0;
}
5. 주의 사항 ⚠️
- NULL 포인터: 어떤 곳도 가리키지 않는 포인터입니다. NULL 포인터를 역참조하면 프로그램이 비정상적으로 종료될 수 있습니다.
- 댕글링 포인터: 이미 해제된 메모리 영역을 가리키는 포인터입니다. 댕글링 포인터를 사용하면 예측할 수 없는 결과가 발생할 수 있습니다.
- 잘못된 포인터 연산: 포인터 연산을 잘못하면 메모리 접근 오류가 발생할 수 있습니다.
C 언어 포인터 완전 정복: 초보자부터 숙련자까지, 모든 것을 파헤쳐 봅시다!
1. 포인터의 기본 개념 다시 살펴보기
- 포인터: 메모리 상의 주소를 저장하는 특별한 변수입니다.
- 주소 연산자 (&): 변수의 메모리 주소를 얻는 데 사용합니다.
- 간접 참조 연산자 (*): 포인터가 가리키는 메모리 위치에 저장된 값에 접근하는 데 사용합니다.
2. 포인터와 배열
- 배열 이름은 포인터: 배열 이름은 배열의 첫 번째 요소의 주소를 나타내는 포인터입니다.
- 배열 요소 접근: 배열_이름[인덱스]와 *(배열_이름 + 인덱스)는 같은 의미입니다.
- 포인터 연산: 포인터에 정수를 더하거나 빼면, 포인터가 가리키는 메모리 위치가 이동합니다. 이동하는 크기는 포인터의 자료형에 따라 달라집니다.
3. 포인터와 함수
- 값에 의한 호출 (Call by Value): 함수에 인자를 전달할 때 값을 복사하여 전달합니다. 함수 내부에서 인자 값을 변경해도 원본 값은 변하지 않습니다.
- 참조에 의한 호출 (Call by Reference): 함수에 인자를 전달할 때 주소를 전달합니다. 함수 내부에서 포인터를 이용해 원본 값을 직접 변경할 수 있습니다.
4. 포인터와 문자열
- 문자열: C 언어에서 문자열은 문자 배열로 표현됩니다.
- 문자열 포인터: 문자열의 시작 주소를 저장하는 포인터입니다.
- 문자열 처리 함수: strlen, strcpy, strcat 등 다양한 문자열 처리 함수는 문자열 포인터를 인자로 받습니다.
5. 동적 메모리 할당
- malloc: 메모리를 할당하고, 할당된 메모리의 시작 주소를 반환합니다.
- calloc: 메모리를 할당하고, 0으로 초기화한 후 할당된 메모리의 시작 주소를 반환합니다.
- realloc: 이미 할당된 메모리의 크기를 변경합니다.
- free: 할당된 메모리를 해제합니다.
6. 고급 포인터 활용
- 함수 포인터: 함수의 주소를 저장하는 포인터입니다. 함수 포인터를 사용하면 함수를 인자로 전달하거나, 실행 시점에 호출할 함수를 결정할 수 있습니다.
- void 포인터: 어떤 자료형의 주소든 저장할 수 있는 포인터입니다. void 포인터를 사용하면 다양한 자료형의 데이터를 처리하는 함수를 작성할 수 있습니다.
- 포인터 배열: 포인터를 저장하는 배열입니다. 포인터 배열을 사용하면 여러 개의 포인터를 효율적으로 관리할 수 있습니다.
- 다중 포인터: 포인터를 가리키는 포인터입니다. 다중 포인터를 사용하면 복잡한 데이터 구조를 구현하거나, 함수에 포인터를 전달하여 함수 내부에서 포인터 값을 변경할 수 있습니다
C 언어 포인터 완전 정복: 심화 학습으로 포인터 마스터 도전!
1. 포인터와 배열: 더 깊이 이해하기
- 배열과 포인터의 관계: 배열 이름은 상수 포인터이며, 배열의 첫 번째 요소의 주소를 나타냅니다. 하지만 배열 이름 자체는 변경할 수 없습니다.
- 포인터 산술 연산: 포인터에 정수를 더하거나 빼면, 포인터가 가리키는 메모리 위치가 이동합니다. 이동하는 크기는 포인터의 자료형 크기에 따라 결정됩니다. 예를 들어 int* ptr에 1을 더하면 4바이트(일반적으로 int형 크기)만큼 이동합니다.
- 다차원 배열과 포인터: 다차원 배열도 포인터를 사용하여 접근할 수 있습니다. 다차원 배열의 각 행은 1차원 배열로 간주되며, 행의 시작 주소를 저장하는 포인터 배열을 사용하여 접근할 수 있습니다.
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int* ptr = arr; // 배열의 첫 번째 요소 주소를 포인터에 저장
printf("arr[0]: %d, *ptr: %d\n", arr[0], *ptr); // 10, 10
printf("arr[2]: %d, *(ptr + 2): %d\n", arr[2], *(ptr + 2)); // 30, 30
ptr += 3; // 포인터를 3칸 이동 (12바이트 이동)
printf("*ptr: %d\n", *ptr); // 40
return 0;
}
- 배열 arr의 이름은 첫 번째 요소의 주소를 나타내는 포인터입니다.
- ptr + 2는 세 번째 요소의 주소를 나타내고, *(ptr + 2)는 세 번째 요소의 값을 나타냅니다.
- ptr += 3은 포인터를 3칸 이동시켜 네 번째 요소를 가리키도록 합니다.
3. 동적 메모리 할당: 예시로 능숙하게 다루기
#include <stdio.h>
#include <stdlib.h>
int main() {
int* ptr = (int*)malloc(5 * sizeof(int)); // 5개의 int형 변수를 저장할 메모리 할당
if (ptr == NULL) {
printf("메모리 할당 실패!\n");
return 1;
}
for (int i = 0; i < 5; i++) {
ptr[i] = i * 10;
}
for (int i = 0; i < 5; i++) {
printf("%d ", ptr[i]); // 0 10 20 30 40
}
printf("\n");
free(ptr); // 할당된 메모리 해제
return 0;
}
- malloc을 사용하여 5개의 int형 변수를 저장할 메모리를 할당하고, 그 시작 주소를 ptr에 저장합니다.
- ptr을 배열처럼 사용하여 값을 저장하고 출력합니다.
- free를 사용하여 할당된 메모리를 해제합니다
4. 고급 포인터 활용: 예시로 실력 향상
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
int main() {
int (*func_ptr)(int, int); // 함수 포인터 선언
func_ptr = add;
printf("add(3, 5): %d\n", func_ptr(3, 5)); // 8
func_ptr = subtract;
printf("subtract(3, 5): %d\n", func_ptr(3, 5)); // -2
return 0;
}
- func_ptr은 두 개의 int형 인자를 받고 int형 값을 반환하는 함수의 주소를 저장하는 포인터입니다.
- func_ptr에 add 함수와 subtract 함수의 주소를 각각 할당하고, 함수 포인터를 통해 함수를 호출합니다.
C 언어 포인터 완전 정복: 구조체와 함께 더 깊이 있는 이해!
1. 구조체와 포인터: 데이터의 집합을 효율적으로 관리하기
- 구조체: 관련된 여러 데이터를 하나로 묶어 새로운 자료형을 만드는 기능입니다.
- 구조체 포인터: 구조체 변수의 주소를 저장하는 포인터입니다.
- 접근 연산자:
- . 연산자: 구조체 변수를 통해 멤버에 접근할 때 사용합니다. (예: student.name)
- -> 연산자: 구조체 포인터를 통해 멤버에 접근할 때 사용합니다. (예: ptr->name)
2. 구조체 포인터: 예시로 쉽게 이해하기
#include <stdio.h>
#include <string.h>
struct Student {
char name[20];
int age;
float grade;
};
int main() {
struct Student student1 = {"Alice", 20, 3.8};
struct Student* ptr = &student1; // student1의 주소를 ptr에 저장
printf("student1.name: %s\n", student1.name); // Alice
printf("ptr->name: %s\n", ptr->name); // Alice
strcpy(ptr->name, "Bob"); // ptr을 통해 name 멤버 변경
printf("student1.name 변경 후: %s\n", student1.name); // Bob
return 0;
}
- student1 구조체 변수를 선언하고 초기화합니다.
- ptr 구조체 포인터를 선언하고 student1의 주소를 저장합니다.
- . 연산자와 -> 연산자를 사용하여 구조체 멤버에 접근하고 값을 출력합니다.
- ptr->name을 사용하여 구조체 멤버의 값을 변경합니다.
3. 동적 메모리 할당과 구조체: 유연한 메모리 관리
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student {
char name[20];
int age;
float grade;
};
int main() {
struct Student* ptr = (struct Student*)malloc(sizeof(struct Student));
if (ptr == NULL) {
printf("메모리 할당 실패!\n");
return 1;
}
strcpy(ptr->name, "Charlie");
ptr->age = 22;
ptr->grade = 3.5;
printf("ptr->name: %s, ptr->age: %d, ptr->grade: %.1f\n",
ptr->name, ptr->age, ptr->grade); // Charlie, 22, 3.5
free(ptr);
return 0;
}
- malloc을 사용하여 구조체 크기만큼 메모리를 할당하고, 그 시작 주소를 ptr에 저장합니다.
- ptr-> 연산자를 사용하여 구조체 멤버에 접근하고 값을 할당합니다.
- free를 사용하여 할당된 메모리를 해제합니다.
4. 연결 리스트: 구조체와 포인터의 환상적인 조합
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node* next;
};
int main() {
struct Node* head = NULL;
struct Node* second = NULL;
struct Node* third = NULL;
head = (struct Node*)malloc(sizeof(struct Node));
second =
head->data = 1;
head->next = second;
second->data = 2;
second->next = third;
third->data = 3;
third->next = NULL;
struct Node* current = head;
while (current != NULL) {
printf("%d ", current->data); // 1 2 3
current = current->next;
}
printf("\n");
free(third);
free(second);
free(head);
return 0;
}
- Node 구조체는 데이터와 다음 노드를 가리키는 포인터를 멤버로 가집니다.
- 세 개의 노드를 동적으로 생성하고, 포인터를 이용하여 연결 리스트를 구성합니다.
- while 루프를 사용하여 연결 리스트를 순회하며 데이터를 출력합니다.
- 할당된 메모리를 해제합니다.
728x90
반응형
'개발 일지 > C언어' 카테고리의 다른 글
tolower 함수란? (0) | 2024.09.15 |
---|---|
구조체란? (0) | 2024.08.23 |
strcpy 함수 (0) | 2024.08.21 |
2024 08 04 개발일지 C언어 찍먹하기 2 (조건문) (0) | 2024.08.05 |
2024 08 03 개발일지 C언어 찍먹하기 (0) | 2024.08.04 |