[코딩도장] Unit 34 ~ 35
1. 포인터
1. 포인터 사용하기
C언어에서 변수는 메모리에 저장이 된다
변수가 담긴 메모리 주소는
포인터(pointer) 변수에 저장한다
포인터 변수는 다음과 같이 * 를 사용해서 선언한다
int *numPtr; // 포인터 변수 선언
int num1 = 10; // int형 변수를 선언하고 10 저장
numPtr = &num1; // num1의 메모리 주소를 포인터 변수에 저장
포인터 변수를 선언할 때는 자료형 뒤에 * 를 붙인다
포인터 변수를 선언한 뒤 & 로 변수의 주소를 구해서 포인터 변수에 저장한다
int * 는 int 형 공간을 가리키는 포인터이다
포인터는 위와 같이 특정 메모리의 주소를 가리킬 때 사용한다
메모리 주소에 접근해서 값을 가져오려면
역참조 연산자( dereference ) * 를 사용한다
numPtr 은 num1 의 메모리주소를 가져온다
*numPtr 은 num1 의 메모리 주소 안에 있는 값을 가져온다
역참조 연산자는 자료형을 바꾸는 효과를 낸다
int *numPtr 에서 *numPtr 로 역참조 하면
int 포인터 → int 자료형으로 바뀌게 된다
int *numPtr;
int num1 = 10;
numPtr = num1; // 컴파일 경고, numPtr은 int 포인터형이고 num1은 int형이라 자료형이 일치하지 않음
*numPtr = num1; // *numPtr은 int형이고 num1도 int형이라 자료형이 일치함
주소 연산자 & 도 자료형을 바꾸는 효과를 낸다
int *numPtr;
int num1;
numPtr = &num1; // numPtr은 int 포인터형이고, &num1은 int형 변수의 주소이므로 자료형이 일치함
// numPtr은 pointer to int, &num1은 address of int이므로 자료형이 일치함
변수는 메모리 주소를 몰라도 값을 가져오거나 저장할 수 있다
주소 연산자( & )는 변수의 메모리 주소 값을 구한다
역참조 연산자( * )는 메모리 주소에 접근하여 값을 가져온다
포인터는 변수의 메모리 주소만 가리킨다
2. void 포인터
void 포인터는 자료형이 정해지지 않은 포인터이다
void *ptr;
void *자료형이름; 으로 선언할 수 있다
void 포인터는 어떤 자료형이든 저장할 수 있기 때문에
범용 포인터라고도 불린다
void 포인터는 값을 가져오거나 저장할 크기가 정해지지 않았기 때문에
void 포인터는 역참조를 할 수 없다
3. 이중 포인터 사용하기
포인터를 선언할 때 *를 두 번 사용하면 이중 포인터가 된다
int *numPtr1; // 단일 포인터 선언
int **numPtr2; // 이중 포인터 선언
int num1 = 10;
포인터도 실제로는 변수이기 때문에 주소를 구할 수 있다
포인터의 메모리 주소는 이중 포인터에 저장해야 한다
numPtr2 처럼 역참조 연산자를 두 번 사용하게 되면
numPtr2 ← numPtr1 ← num1
같은 모양으로 num1 의 값을 가져올 수 있다
4. 널( NULL ) 포인터
아무것도 가리키지 않는 포인터를 널 포인터( null pointer )라고 한다
int *numPtr1 = NULL; // 포인터에 NULL 저장
실무에서는 다음과 같이 포인터가 NULL 인지 확인한 뒤 NULL 이면 메모리를 할당하는 패턴을 사용한다
if (ptr == NULL) // ptr이 널 포인터라면
{
ptr = malloc(1024); // 1024바이트만큼 메모리 할당
}
2. 메모리
1. 메모리 사용하기
C언어에서 메모리를 할당하려면 malloc 함수를 사용해야 한다
메모리 할당, 해제 함수는 stdlib.h 헤더 파일에 선언되어 있다
메모리는 malloc → 사용 → free 패턴으로 사용한다
자료형 *포인터이름 = malloc(크기); // 일정 크기만큼 동적 메모리 할당
free(포인터); // 동적 메모리 해제
메모리를 할당할 때는 malloc 함수를 사용하고
할당할 메모리 공간의 크기를 지정해준다
원하는 시점에 원하는만큼 메모리를 할당할 수 있기 때문에
동적 메모리 할당(dynamic memory allocation)이라 부른다
같은 메모리 주소라도 내부적으로는 차이가 있다
int num1 = 20; // int형 변수 선언
int *numPtr1; // int형 포인터 선언
numPtr1 = &num1; // num1의 메모리 주소를 구하여 numPtr에 할당
int *numPtr2; // int형 포인터 선언
numPtr2 = malloc(sizeof(int)); // int의 크기 4바이트만큼 동적 메모리 할당
일반 변수의 메모리 주소를 할당한 numPtr1 은 스택( Stack )에 생성되고
malloc 함수를 이용해 메모리를 할당한 numPtr2 는 힙( Heap )에 생성된다
스택과 힙의 가장 큰 차이점은 메모리 해제이다
스택에 생성된 변수는 메모리를 따로 해제해 줄 필요가 없다
하지만 힙에서 할당한 메모리는 반드시 해제를 해주어야 한다
메모리 해제는 free 함수를 이용해서 할 수 있다
free(numPtr2); // 동적으로 할당한 메모리 해제
※ 메모리 해제는 필수적으로 해주어야 한다
메모리 해제를 하지 않으면 메모리 사용량이 계속 증가하게 되고
시스템이 프로그램을 강제종료 시키거나 메모리 할당에 실패하게 된다
이런 현상을 메모리 누수( memory leak )라고 한다
2. memset 함수 사용하기
memset 함수를 사용하면 메모리의 내용을 원하는 크기만큼 특정값으로 설정할 수 있다
string.h 또는 momory.h 헤더 파일을 포함해야 한다
memset(numPtr, 0, 8); // numPtr이 가리키는 메모리를 8바이트만큼 0으로 설정
memset 함수는 주로 설정할 값을 0으로 지정하여 메모리를 초기화 할 때 사용한다
3. 심사문제
34.10 포인터와 주소 연산자 사용하기
표준 입력으로 정수가 입력됩니다. 다음 소스 코드를 완성하여 입력된 정수가 출력되게 만드세요.
정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int *numPtr1;
int **numPtr2;
int num1;
scanf("%d", &num1);
___________________
___________________
printf("%d\n", **numPtr2);
return 0;
}
정답
numPtr1 = &num1;
numPtr2 = &numPtr1;
35.7 두 정수의 합 구하기
표준 입력으로 두 정수가 입력됩니다(입력 값의 범위는 0~1073741824). 다음 소스 코드를 완성하여 입력된 두 정수의 합이 출력되게 만드세요.
정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main()
{
int num1;
int num2;
_________________________________
_________________________________
scanf("%d %d", &num1, &num2);
*numPtr1 = num1;
*numPtr2 = num2;
printf("%d\n", *numPtr1 + *numPtr2);
free(numPtr1);
free(numPtr2);
return 0;
}
정답
int *numPtr1 = malloc(sizeof(int));
int *numPtr2 = malloc(sizeof(int));
'Coding > C' 카테고리의 다른 글
[C언어] 문자열 사용하기- 코딩도장 (0) | 2024.01.12 |
---|---|
[C언어] 배열, 배열 포인터 사용하기 - 코딩도장 (0) | 2024.01.07 |
[C언어] goto 사용하기 - 코딩도장 (0) | 2023.12.18 |
[C언어] 반복문(for, while, do while) - 코딩도장 (0) | 2023.12.17 |
[C언어] switch 분기문 - 코딩도장 (0) | 2023.12.13 |