10장 메모리 관리
10장 강의 목표
• 변수와 메모리 이해
• 동적 메모리 할당
• 동적 할당과 연결 리스트
• 공유 메모리
• 메모리 관리 함수
기말고사: 8,9,10장 시험
20점 - 코드 괄호
15점 - 프로그램 관련, 프로그램 실행 시켰을 때 결과가 뭐가 나오는지 (최근 과제보다 훨씬 쉬운 형태)
10장 프로세스 이미지와 관련된 내용, 그림 작성하라는 것은 아니고 그게 무슨뜻인지 이해할 수 있으면 된다.
지난 중간고사 평균 15점인데 이번엔 20점이 충분히 나올 것으로 예상
가능한한 쉬운 내용만 보고 복잡한 프로그램은 하지 마세요.
10.1 변수와 메모리
프로세스
• 프로세스는 실행중인 프로그램이다.
• 프로그램 실행을 위해서는
- 프로그램의 코드, 데이터, 스택, 힙, U-영역 등이 필요하다.
• 프로세스 이미지(구조)는 메모리 내의 프로세스 레이아웃
• 프로그램 자체가 프로세스는 아니다 !
프로세스 구조
• 프로세스 구조
• 코드 세그먼트(code segment)
- 기계어 명령어
• 데이터 세그먼트(data segment)
- e.g. int maxcount = 99; (initialized)
- e.g. long sum[1000]; (uninitialized)
• 스택(stack)
- 지역 변수, 매개 변수, 반환주소, 반환값, 등
• 힙(heap)
- 동적 메모리 할당
- malloc() in C,
- new class() in Java
vars.c
#include <stdio.h>
#include <stdlib.h>
int a = 1;
static int b = 2;
int main() {
int c = 3;
static int d = 4;
char *p;
p = (char *) malloc(40);
fun(5);
}
void fun(int n)
{
int m = 6;
...
}
정적 변수와 데이터 영역
#include <stdio.h>
#include <stdlib.h>
int a = 1;
static int b = 2;
int main() {
int c = 3;
static int d = 4;
char *p;
p = (char *) malloc(40);
fun(5);
}
void fun(int n)
{
int m = 6;
...
}
지역 변수와 실행시간 스택: main() 호출
#include <stdio.h>
#include <stdlib.h>
int a = 1;
static int b = 2;
int main() {
int c = 3;
static int d = 4;
char *p;
p = (char *) malloc(40);
fun(5);
}
void fun(int n)
{
int m = 6;
...
}
지역 변수와 실행시간 스택: fun() 호출
#include <stdio.h>
#include <stdlib.h>
int a = 1;
static int b = 2;
int main() {
int c = 3;
static int d = 4;
char *p;
p = (char *) malloc(40);
fun(5);
}
void fun(int n)
{
int m = 6;
...
}
할당 방법에 따른 변수들의 분류
변수 구분 | 변수 종류 |
정적 변수 | 전역변수, static 변수 |
자동 변수 | 지역변수, 매개변수 |
동적 변수 | 힙 할당 변수 |
10.2 동적 메모리 할당
동적 메모리 할당
• 동적 할당을 사용하는 이유
- 필요할 때 필요한 만큼만 메모리를 요청해서 사용하여
- 메모리를 절약한다.
• malloc( )
• calloc( )
• realloc( )
• free( )
메모리 할당
#include <stdlib.h>
void *malloc(size_t size);
size 크기의 메모리를 할당하며 그 시작주소를 void* 형으로 반환한다.
void free(void *p);
포인터 p가 가리키는 메모리 공간을 해제한다.
• 힙에 동적 메모리 할당
• 라이브러리가 메모리 풀을 관리한다
• malloc() 함수는 메모리를 할당할 때 사용하고 free()는 할당한 메모리를 해제할 때 사용한다.
메모리 할당 예
• char *p;
• p = (char *) malloc(40);
• int *p;
• p = (int *) malloc(10 * sizeof(int));
구조체를 위한 메모리 할당 예
struct student {
int id;
char name[10];
};
struct student *p;
p = (struct student *) malloc(sizeof(struct student));
구조체 배열을 위한 메모리 할당 예
struct student *p;
p = (struct student *) malloc(n * sizeof(struct student));
#include <stdio.h>
#include <stdlib.h>
void main(void){
void *p1, *p2, *p3;
p1 = malloc(16);
// free(p1);
p2 = malloc(32);
p3 = malloc(16);
printf("p1 = %ld\n", (long int)p1);
printf("p2 = %ld\n", (long int)p2);
printf("p3 = %ld\n", (long int)p3);
./ppp
p1 = 110748166330016
p2 = 110748166330048
p3 = 110748166330096
free(p1); (O),,
p1 = 1065943311384960
p2 = 1065943311384992
p3 = 1065943311384960
이 코드는 메모리 할당을 사용하여 포인터 변수를 초기화하고, 각 포인터가 가리키는 메모리 주소를 출력하는 프로그램입니다. 각 줄을 한 줄씩 해석해 보겠습니다.
1. `#include <stdio.h>`
- 표준 입출력 함수들을 사용하기 위해 `stdio.h` 헤더 파일을 포함시킵니다.
2. `#include <stdlib.h>`
- 메모리 할당 함수들을 사용하기 위해 `stdlib.h` 헤더 파일을 포함시킵니다.
3. 빈 줄
- 가독성을 위한 빈 줄입니다.
4. `void main(void)`
- 메인 함수의 정의를 시작합니다. 여기서 `void`는 반환 값이 없음을 나타내며, `void` 인자는 인수가 없음을 나타냅니다.
5. `{`
- 메인 함수의 시작을 나타냅니다.
6. `void *p1, *p2, *p3;`
- 세 개의 포인터 변수를 선언합니다.
- `void*` 타입은 모든 타입의 포인터를 저장할 수 있는 일반 포인터 타입입니다.
- `p1`, `p2`, `p3`는 각각 할당된 메모리의 주소를 저장합니다.
7. 빈 줄
- 가독성을 위한 빈 줄입니다.
8. `p1 = malloc(16);`
- `malloc` 함수를 호출하여 16바이트의 메모리를 할당하고, 할당된 메모리의 시작 주소를 `p1`에 저장합니다.
- `malloc` 함수는 성공 시 할당된 메모리 블록의 시작 주소를 반환하고, 실패 시 `NULL`을 반환합니다.
9. `// free(p1);`
- 이 줄은 주석 처리되어 있으며, `p1`에 할당된 메모리를 해제하는 코드입니다.
- 주석 처리가 되어 있으므로 이 코드는 실행되지 않습니다.
10. `p2 = malloc(32);`
- `malloc` 함수를 호출하여 32바이트의 메모리를 할당하고, 할당된 메모리의 시작 주소를 `p2`에 저장합니다.
11. `p3 = malloc(16);`
- `malloc` 함수를 호출하여 16바이트의 메모리를 할당하고, 할당된 메모리의 시작 주소를 `p3`에 저장합니다.
12. `printf("p1 = %ld\n", (long int)p1);`
- `printf` 함수를 사용하여 `p1`이 가리키는 메모리 주소를 출력합니다.
- `(long int)p1`은 `p1`의 값을 `long int` 타입으로 형변환하여 출력합니다.
13. `printf("p2 = %ld\n", (long int)p2);`
- `printf` 함수를 사용하여 `p2`가 가리키는 메모리 주소를 출력합니다.
- `(long int)p2`은 `p2`의 값을 `long int` 타입으로 형변환하여 출력합니다.
14. `printf("p3 = %ld\n", (long int)p3);`
- `printf` 함수를 사용하여 `p3`가 가리키는 메모리 주소를 출력합니다.
- `(long int)p3`은 `p3`의 값을 `long int` 타입으로 형변환하여 출력합니다.
### 출력 예시
프로그램이 실행되면 할당된 메모리의 주소가 출력됩니다. 예를 들어:
```
p1 = 12345678
p2 = 12345698
p3 = 123456b8
```
이 결과는 다음을 보여줍니다:
- `p1`은 16바이트의 메모리를 할당받아 그 시작 주소를 가리킵니다.
- `p2`는 32바이트의 메모리를 할당받아 그 시작 주소를 가리킵니다.
- `p3`은 16바이트의 메모리를 할당받아 그 시작 주소를 가리킵니다.
주석 처리가 된 `free(p1);` 줄은 할당된 메모리를 해제하는 기능을 합니다. 이를 주석 처리하지 않고 실행하면, `p1`에 할당된 메모리가 해제되어 `p2`와 `p3`의 메모리 할당 위치가 달라질 수 있습니다.
동적 할당: stud1.c
#include <stdio.h>
#include <stdlib.h>
struct student {
int id;
char name[20];
};
/* 입력받을 학생 수를 미리 입력받고 이어서 학생 정보를 입력받은 후,
이들 학생 정보를 역순으로 출력하는 프로그램 */
int main()
{
struct student *p; // 동적 할당된 블록을 가리킬 포인터
int n, i;
printf("몇 명의 학생을 입력하겠습니까? ");
scanf("%d", &n);
if (n <= 0) {
fprintf(stderr, "오류: 학생 수를 잘못 입력했습니다.\n");
fprintf(stderr, "프로그램을 종료합니다.\n");
exit(1);
}
p = (struct student *) malloc(n * sizeof(struct student));
if (p == NULL) {
perror("malloc");
exit(2);
}
printf("%d 명의 학번과 이름을 입력하세요.\n", n);
for (i = 0; i < n; i++)
scanf("%d %s\n", &p[i].id, p[i].name);
printf("\n* 학생 정보(역순) *\n");
for (i = n-1; i >= 0; i--)
printf("%d %s\n", p[i].id, p[i].name);
printf("\n");
exit(0);
}
이 코드는 사용자가 입력한 학생 수만큼 동적으로 메모리를 할당하여 학생 정보를 입력받고, 입력받은 학생 정보를 역순으로 출력하는 프로그램입니다. 각 줄을 한 줄씩 해석해 보겠습니다.
1. `#include <stdio.h>`
- 표준 입출력 함수들을 사용하기 위해 `stdio.h` 헤더 파일을 포함시킵니다.
2. `#include <stdlib.h>`
- 메모리 할당 함수와 기타 유틸리티 함수들을 사용하기 위해 `stdlib.h` 헤더 파일을 포함시킵니다.
3. `struct student {`
- 학생 정보를 저장할 구조체 `student`를 정의합니다.
4. ` int id;`
- 학생의 학번을 저장할 정수형 변수를 정의합니다.
5. ` char name[20];`
- 학생의 이름을 저장할 20바이트 크기의 문자열 배열을 정의합니다.
6. `};`
- 구조체 정의의 끝을 나타냅니다.
7. `/* 입력받을 학생 수를 미리 입력받고 이어서 학생 정보를 입력받은 후,`
8. ` 이들 학생 정보를 역순으로 출력하는 프로그램 */`
- 프로그램의 동작을 설명하는 주석입니다.
9. `int main()`
- 메인 함수의 정의를 시작합니다.
10. `{`
- 메인 함수의 시작을 나타냅니다.
11. ` struct student *p;`
- 동적으로 할당된 학생 구조체 배열을 가리킬 포인터 `p`를 선언합니다.
12. ` int n, i;`
- 학생 수를 저장할 정수형 변수 `n`과 반복문에서 사용할 정수형 변수 `i`를 선언합니다.
13. ` printf("몇 명의 학생을 입력하겠습니까? ");`
- 사용자에게 몇 명의 학생 정보를 입력할 것인지 묻는 메시지를 출력합니다.
14. ` scanf("%d", &n);`
- 사용자가 입력한 학생 수를 `n` 변수에 저장합니다.
15. ` if (n <= 0) {`
- 학생 수 `n`이 0 이하인 경우를 확인하는 조건문입니다.
16. ` fprintf(stderr, "오류: 학생 수를 잘못 입력했습니다.\n");`
- 표준 오류 스트림에 오류 메시지를 출력합니다.
17. ` fprintf(stderr, "프로그램을 종료합니다.\n");`
- 표준 오류 스트림에 프로그램 종료 메시지를 출력합니다.
18. ` exit(1);`
- 프로그램을 종료합니다. 종료 코드 1은 오류를 나타냅니다.
19. ` }`
- `if` 조건문의 끝을 나타냅니다.
20. ` p = (struct student *) malloc(n * sizeof(struct student));`
- `malloc` 함수를 호출하여 `n`명의 학생 구조체 배열에 필요한 메모리를 동적으로 할당하고, 그 시작 주소를 `p` 포인터에 저장합니다.
- `sizeof(struct student)`는 학생 구조체의 크기를 계산합니다.
21. ` if (p == NULL) {`
- `malloc` 함수가 메모리 할당에 실패했는지 확인하는 조건문입니다.
22. ` perror("malloc");`
- 표준 오류 스트림에 메모리 할당 실패에 대한 오류 메시지를 출력합니다.
23. ` exit(2);`
- 프로그램을 종료합니다. 종료 코드 2는 메모리 할당 실패를 나타냅니다.
24. ` }`
- `if` 조건문의 끝을 나타냅니다.
25. 빈 줄
- 가독성을 위한 빈 줄입니다.
26. ` printf("%d 명의 학번과 이름을 입력하세요.\n", n);`
- 사용자에게 `n`명의 학생 학번과 이름을 입력하라고 안내 메시지를 출력합니다.
27. ` for (i = 0; i < n; i++)`
- 학생 수 `n`만큼 반복문을 실행합니다. `i`는 0부터 `n-1`까지 증가합니다.
28. ` scanf("%d %s\n", &p[i].id, p[i].name);`
- 각 학생의 학번과 이름을 입력받아 동적 할당된 학생 구조체 배열에 저장합니다.
- `p[i].id`에 학번을 저장하고, `p[i].name`에 이름을 저장합니다.
29. 빈 줄
- 가독성을 위한 빈 줄입니다.
30. ` printf("\n* 학생 정보(역순) *\n");`
- 역순으로 학생 정보를 출력하기 전에 제목을 출력합니다.
31. ` for (i = n-1; i >= 0; i--)`
- 학생 수 `n`만큼 반복문을 역순으로 실행합니다. `i`는 `n-1`부터 0까지 감소합니다.
32. ` printf("%d %s\n", p[i].id, p[i].name);`
- 역순으로 각 학생의 학번과 이름을 출력합니다.
33. 빈 줄
- 가독성을 위한 빈 줄입니다.
34. ` printf("\n");`
- 출력 형식을 위해 빈 줄을 출력합니다.
35. ` exit(0);`
- 프로그램을 종료합니다. 종료 코드 0은 정상 종료를 나타냅니다.
36. `}`
- 메인 함수의 끝을 나타냅니다.
### 요약
- 사용자로부터 입력받을 학생 수를 입력받고, 그 수만큼 메모리를 동적으로 할당합니다.
- 각 학생의 학번과 이름을 입력받아 동적 할당된 메모리에 저장합니다.
- 입력받은 학생 정보를 역순으로 출력합니다.
- 입력받은 학생 수가 0 이하이거나 메모리 할당에 실패한 경우, 적절한 오류 메시지를 출력하고 프로그램을 종료합니다.
동적 할당: stud1.c
$ stud1
몇 명의 학생을 입력하겠습니까? 5
5 명의 학번과 이름을 입력하세요.
1001001 박연아
1001003 김태환
1001006 김현진
1001009 장샛별
1001011 홍길동
^D
* 학생 정보(역순) *
1001011 홍길동
1001009 장샛별
1001006 김현진
1001003 김태환
1001001 박연아
밑에 힙에서 계속 쌓이고
스택과 겹치지 않도록 해야하므로
항상 malloc = null 확인해야 한다.
핵심은 malloc해서 필요한 사이즈만큼 메모리를 할당하고
할당 후 그 포인터를 캐스팅해서 써야 한다.
할당했다면 그 포인터가 널인가 아닌가 검사를 해야한다.
그게 핵심입니다.
배열 할당 calloc()
• 같은 크기의 메모리를 여러 개를 할당할 경우
#include <stdlib.h>
void *calloc(size_t n, size_t size);
size 크기의 메모리를 n개 할당한다. 값을 모두 0으로 초기화한다.
실패하면 NULL를 반환한다.
size는 배열 요소 하나의 크기, n은 배열의 크기
• 이미 할당된 메모리의 크기 변경
#include <stdlib.h>
void *realloc(void *p, size_t newsize);
p이 가리키는 이미 할당된 메모리의 크기를 newsize로 변경한다.
메모리를 할당하여 사용하던 중 부족하면 더 늘려서 사용할 때 사용하는 realloc
사이즈를 줄일 수도 있고 늘릴 수도 있다. 첫 번째 인수는 사용 중인 메모리, 두 번째 인수는 원하는 사이즈의 메모리
calloc() 예
int *p,*q;
p = malloc(10*sizeof(int));
if (p == NULL)
perror(“malloc”);
q = calloc(10, sizeof(int));
if (q == NULL)
perror(“calloc”);
이 코드는 동적 메모리 할당 함수인 `malloc`과 `calloc`을 사용하여 메모리를 할당하고, 할당 실패 시 오류 메시지를 출력하는 예제입니다. 각 줄을 한 줄씩 해석해 보겠습니다.
1. `int *p, *q;`
- 두 개의 정수형 포인터 변수 `p`와 `q`를 선언합니다. 이 포인터들은 각각 `malloc`과 `calloc` 함수를 사용하여 할당된 메모리 블록을 가리키게 됩니다.
2. `p = malloc(10 * sizeof(int));`
- `malloc` 함수를 사용하여 10개의 `int` 크기만큼의 메모리를 할당합니다.
- `10 * sizeof(int)`는 `int` 타입의 크기(`sizeof(int)`)에 10을 곱한 값으로, 10개의 정수를 저장할 수 있는 메모리 크기를 나타냅니다.
- 할당된 메모리 블록의 시작 주소는 포인터 `p`에 저장됩니다.
3. `if (p == NULL)`
- `malloc` 함수가 메모리 할당에 실패한 경우를 확인하는 조건문입니다.
- `malloc`이 실패하면 `NULL`을 반환합니다. `p`가 `NULL`이면 할당이 실패한 것입니다.
4. ` perror("malloc");`
- `malloc` 함수가 실패했을 때, 표준 오류 스트림에 오류 메시지를 출력합니다.
- `perror` 함수는 지정된 문자열("malloc")과 함께 최근에 발생한 시스템 오류 메시지를 출력합니다.
5. `q = calloc(10, sizeof(int));`
- `calloc` 함수를 사용하여 10개의 `int` 크기만큼의 메모리를 할당합니다.
- `calloc` 함수는 두 개의 인수를 받으며, 첫 번째 인수는 할당할 요소의 개수(10), 두 번째 인수는 각 요소의 크기(`sizeof(int)`)입니다.
- `calloc`은 할당된 메모리를 0으로 초기화합니다.
- 할당된 메모리 블록의 시작 주소는 포인터 `q`에 저장됩니다.
6. `if (q == NULL)`
- `calloc` 함수가 메모리 할당에 실패한 경우를 확인하는 조건문입니다.
- `calloc`이 실패하면 `NULL`을 반환합니다. `q`가 `NULL`이면 할당이 실패한 것입니다.
7. ` perror("calloc");`
- `calloc` 함수가 실패했을 때, 표준 오류 스트림에 오류 메시지를 출력합니다.
- `perror` 함수는 지정된 문자열("calloc")과 함께 최근에 발생한 시스템 오류 메시지를 출력합니다.
### 요약
- `int *p, *q;`: 두 개의 정수형 포인터 변수 `p`와 `q`를 선언합니다.
- `p = malloc(10 * sizeof(int));`: `malloc` 함수를 사용하여 10개의 `int` 크기만큼의 메모리를 할당하고, 포인터 `p`에 저장합니다. 할당 실패 시 `NULL`을 반환합니다.
- `if (p == NULL) perror("malloc");`: `malloc` 함수가 실패했는지 확인하고, 실패했을 경우 오류 메시지를 출력합니다.
- `q = calloc(10, sizeof(int));`: `calloc` 함수를 사용하여 10개의 `int` 크기만큼의 메모리를 할당하고, 포인터 `q`에 저장합니다. 할당된 메모리는 0으로 초기화됩니다. 할당 실패 시 `NULL`을 반환합니다.
- `if (q == NULL) perror("calloc");`: `calloc` 함수가 실패했는지 확인하고, 실패했을 경우 오류 메시지를 출력합니다.
이 예제는 `malloc`과 `calloc` 함수를 사용하여 메모리를 할당하고, 할당 실패 시 이를 처리하는 방법을 보여줍니다.
10.3 동적 할당과 연결 리스트
연결 리스트의 필요성
• 예: 여러 학생들의 데이터를 저장해야 한다고 생각해보자.
- 가장 간단한 방법은 구조체 배열을 선언하여 사용하는 것이다.
- 이 방법은 배열의 크기를 미리 결정해야 하는 문제점이 있다.
- 배열의 크기보다 많은 학생들은 처리할 수 없으며 이보다 적은 학생들의 경우에는 배열의 기억공간은 낭비된다.
• 동적 메모리 할당
- 필요할 때마다 동적으로 메모리를 할당하여
- 연결리스트(linked list)로 관리한다
자기 참조 구조체를 위한 메모리 할당
struct student {
int id;
char name[20];
struct student *next;
};
struct student *p;
p = (struct student *) malloc(sizeof(struct student));
이 코드는 `student`라는 자기 참조 구조체를 정의하고, `student` 구조체를 위한 메모리를 동적으로 할당하는 예제입니다. 각 줄을 한 줄씩 해석해 보겠습니다.
1. `struct student {`
- `student`라는 구조체를 정의합니다. 이 구조체는 학생 정보를 저장할 것입니다.
2. ` int id;`
- 학생의 학번을 저장할 정수형 변수 `id`를 선언합니다.
3. ` char name[20];`
- 학생의 이름을 저장할 20바이트 크기의 문자열 배열 `name`을 선언합니다.
4. ` struct student *next;`
- 다음 학생 구조체를 가리킬 포인터 `next`를 선언합니다.
- 자기 참조 구조체(self-referential structure)로, 이 구조체는 다른 `student` 구조체를 가리키는 포인터를 포함합니다.
- 이를 통해 학생 구조체들을 연결 리스트 형태로 만들 수 있습니다.
5. `};`
- 구조체 정의의 끝을 나타냅니다.
6. `struct student *p;`
- `student` 구조체를 가리킬 포인터 변수 `p`를 선언합니다.
- 이 포인터는 동적으로 할당된 `student` 구조체를 가리키게 됩니다.
7. `p = (struct student *) malloc(sizeof(struct student));`
- `malloc` 함수를 사용하여 `student` 구조체 크기만큼의 메모리를 동적으로 할당하고, 그 시작 주소를 포인터 `p`에 저장합니다.
- `sizeof(struct student)`는 `student` 구조체의 크기를 계산합니다.
- `malloc` 함수는 성공 시 할당된 메모리 블록의 시작 주소를 반환하고, 실패 시 `NULL`을 반환합니다.
- `(struct student *)`는 `malloc` 함수가 반환한 `void*` 포인터를 `struct student*` 타입으로 캐스팅합니다.
### 요약
- `struct student { int id; char name[20]; struct student *next; };`
- `student`라는 구조체를 정의합니다. 이 구조체는 학생의 학번(`id`), 이름(`name`), 그리고 다음 학생을 가리키는 포인터(`next`)를 포함합니다.
- `next` 포인터를 통해 여러 `student` 구조체를 연결 리스트로 연결할 수 있습니다.
- `struct student *p;`
- `student` 구조체를 가리킬 포인터 변수 `p`를 선언합니다.
- `p = (struct student *) malloc(sizeof(struct student));`
- `malloc` 함수를 사용하여 `student` 구조체 크기만큼의 메모리를 동적으로 할당하고, 그 시작 주소를 포인터 `p`에 저장합니다.
- 할당된 메모리는 `p`를 통해 접근할 수 있으며, 연결 리스트의 첫 번째 노드로 사용할 수 있습니다.
이 코드는 기본적인 동적 메모리 할당과 자기 참조 구조체의 개념을 보여줍니다. `malloc` 함수를 통해 메모리를 할당하고, 포인터를 사용하여 이 메모리에 접근할 수 있습니다.
동적 할당: stud2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
…
/* 학생 정보를 입력받아 연결 리스트에 저장하고 학생 정보를 역순으로
출력한다. */
int main()
{
int count = 0, id;
char name[20];
struct student *p, *head = NULL;
printf("학번과 이름을 입력하세요\n");
while (scanf("%d %s", &id, name) == 2) {
p = (struct student *) malloc(sizeof(struct student));
if (p == NULL) {
perror("malloc");
exit(1);
}
p->id = id;
strcpy(p->name, name);
p->next = head;
head = p;
}
printf("\n* 학생 정보(역순) *\n");
p = head;
while (p != NULL) {
count++;
printf("학번: %d 이름: %s \n", p->id, p->name);
p = p->next;
}
printf("총 %d 명입니다.\n", count);
exit(0);
}
이 코드는 학생 정보를 입력받아 연결 리스트에 저장하고, 입력된 학생 정보를 역순으로 출력하는 프로그램입니다. 각 줄을 한 줄씩 해석해 보겠습니다.
1. `#include <stdio.h>`
- 표준 입출력 함수들을 사용하기 위해 `stdio.h` 헤더 파일을 포함시킵니다.
2. `#include <stdlib.h>`
- 메모리 할당 함수와 기타 유틸리티 함수들을 사용하기 위해 `stdlib.h` 헤더 파일을 포함시킵니다.
3. `#include <string.h>`
- 문자열 처리 함수들을 사용하기 위해 `string.h` 헤더 파일을 포함시킵니다.
4. `…`
- 필요하지 않은 부분이 생략되었음을 나타냅니다.
5. `/* 학생 정보를 입력받아 연결 리스트에 저장하고 학생 정보를 역순으로 출력한다. */`
- 프로그램의 동작을 설명하는 주석입니다.
6. `int main()`
- 메인 함수의 정의를 시작합니다.
7. `{`
- 메인 함수의 시작을 나타냅니다.
8. `int count = 0, id;`
- 정수형 변수 `count`와 `id`를 선언합니다.
- `count`: 학생 수를 세기 위해 사용됩니다.
- `id`: 입력받은 학생의 학번을 저장할 변수입니다.
9. `char name[20];`
- 학생의 이름을 저장할 20바이트 크기의 문자열 배열 `name`을 선언합니다.
10. `struct student *p, *head = NULL;`
- 두 개의 `student` 구조체 포인터 변수 `p`와 `head`를 선언합니다.
- `p`: 새로 할당된 학생 구조체를 가리킬 포인터입니다.
- `head`: 연결 리스트의 시작(헤드)을 가리키는 포인터입니다. 초기값은 `NULL`입니다.
11. `printf("학번과 이름을 입력하세요\n");`
- 사용자에게 학번과 이름을 입력하라고 안내하는 메시지를 출력합니다.
12. `while (scanf("%d %s", &id, name) == 2) {`
- 학번과 이름을 입력받아 각각 `id`와 `name` 변수에 저장합니다.
- `scanf` 함수는 두 개의 값을 성공적으로 읽으면 2를 반환합니다.
- 입력이 성공적으로 이루어졌을 때만 반복문이 실행됩니다.
13. ` p = (struct student *) malloc(sizeof(struct student));`
- `malloc` 함수를 사용하여 `student` 구조체 크기만큼의 메모리를 동적으로 할당하고, 그 시작 주소를 포인터 `p`에 저장합니다.
- `sizeof(struct student)`는 `student` 구조체의 크기를 계산합니다.
14. ` if (p == NULL) {`
- `malloc` 함수가 메모리 할당에 실패했는지 확인하는 조건문입니다.
- `malloc`이 실패하면 `NULL`을 반환합니다. `p`가 `NULL`이면 할당이 실패한 것입니다.
15. ` perror("malloc");`
- `malloc` 함수가 실패했을 때, 표준 오류 스트림에 오류 메시지를 출력합니다.
16. ` exit(1);`
- 프로그램을 종료합니다. 종료 코드 1은 오류를 나타냅니다.
17. ` }`
- `if` 조건문의 끝을 나타냅니다.
18. ` p->id = id;`
- 새로 할당된 `student` 구조체의 `id` 필드에 입력받은 학번을 저장합니다.
19. ` strcpy(p->name, name);`
- `strcpy` 함수를 사용하여 입력받은 이름을 `student` 구조체의 `name` 필드에 복사합니다.
20. ` p->next = head;`
- 새로 할당된 `student` 구조체의 `next` 필드에 현재 리스트의 헤드 포인터를 저장합니다.
- 이는 새로 입력된 학생이 리스트의 첫 번째 요소가 되도록 설정합니다.
21. ` head = p;`
- `head` 포인터를 새로 할당된 `student` 구조체를 가리키도록 업데이트합니다.
- 이는 새로 입력된 학생이 리스트의 첫 번째 요소가 되도록 합니다.
22. `}`
- `while` 반복문의 끝을 나타냅니다.
23. 빈 줄
- 가독성을 위한 빈 줄입니다.
24. `printf("\n* 학생 정보(역순) *\n");`
- 역순으로 학생 정보를 출력하기 전에 제목을 출력합니다.
25. `p = head;`
- `p` 포인터를 리스트의 시작을 가리키는 `head`로 초기화합니다.
26. `while (p != NULL) {`
- 리스트를 순회하기 위한 반복문을 시작합니다. `p`가 `NULL`이 아닐 때까지 실행됩니다.
27. ` count++;`
- 학생 수를 세기 위해 `count`를 1 증가시킵니다.
28. ` printf("학번: %d 이름: %s \n", p->id, p->name);`
- 현재 학생의 학번과 이름을 출력합니다.
29. ` p = p->next;`
- `p` 포인터를 다음 학생을 가리키도록 업데이트합니다.
30. `}`
- `while` 반복문의 끝을 나타냅니다.
31. `printf("총 %d 명입니다.\n", count);`
- 총 학생 수를 출력합니다.
32. `exit(0);`
- 프로그램을 정상적으로 종료합니다. 종료 코드 0은 정상 종료를 나타냅니다.
33. `}`
- 메인 함수의 끝을 나타냅니다.
### 요약
- 사용자로부터 학번과 이름을 입력받아 동적으로 메모리를 할당하고, 할당된 메모리에 학생 정보를 저장합니다.
- 입력된 학생 정보는 연결 리스트에 저장되며, 새로운 학생 정보는 리스트의 앞에 추가됩니다.
- 입력이 완료되면 저장된 학생 정보를 역순으로 출력합니다.
- 입력된 총 학생 수를 출력합니다.
이 프로그램은 연결 리스트를 사용하여 학생 정보를 동적으로 저장하고 관리합니다. 사용자는 학번과 이름을 입력하고, 프로그램은 이를 리스트에 저장하여 나중에 역순으로 출력할 수 있도록 합니다.
----- End (시험범위)
10.4 공유 메모리
공유 메모리의 필요성
• 공유 메모리
- 프로세스 사이에 메모리 영역을 공유해서 사용할 수 있도록 해준다.
공유 메모리 관련 함수
• 공유 메모리 생성 shmget()
#include <sys/shm.h>
#include <sys/ipc.h>
int shmget(key_t key, size_t size, int shmflg);
key를 사용하여 size 크기의 공유 메모리를 생성하고 생성된 공유 메모리의 ID를 반환한다.
• 공유 메모리 연결 shmat()
#include <sys/shm.h>
#include <sys/ipc.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid 공유 메모리를 이 프로세스의 메모리 위치 shmaddr에 연결하고 그 주소를 반환한다.
• 공유 메모리 연결 해제 shmdt()
#include <sys/shm.h>
#include <sys/ipc.h>
int shmdt(const void *shmaddr);
공유 메모리에 대한 연결 주소 shmaddr를 연결해제 한다.
성공 시 0, 실패 시 –1을 반환한다.
• 공유 메모리 제어 shmctl()
#include <sys/shm.h>
#include <sys/ipc.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid 공유메모리를 cmd 명령어에 따라 제어한다.
• cmd
- IPC_RMID: 공유 메모리 제거
- IPC_SET: buf의 지정된 값을 공유 메모리 정보(사용자 ID, 그룹 ID, 접근권한) 설정
- IPC_STAT: 공유 메모리 정보를 buf에 저장
- SHM_• OCK: 공유 메모리 잠금
- SHM_UNLOCK: 공유 메모리 잠금 해제
공유 메모리: shm1.c
1 #include <sys/ipc.h>
…
7 int main()
8 {
9 int shmid;
10 key_t key;
11 char *shmaddr;
12
13 key = ftok("helloshm", 1);
14 shmid = shmget(key, 1024, IPC_CREAT|0644);
15 if (shmid == -1) {
16 perror("shmget");
17 exit(1);
18 }
19
20 printf("shmid : %d", shmid);
21 shmaddr = (char *) shmat(shmid, NULL, 0);
22 strcpy(shmaddr, "hello shared memory");
23 return(0);
24 }
실행 결과
$shm1
shmid : 17
$ ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 4 chang 600 16384 1 dest
0xffffffff 17 chang 644 1024 0
...
공유 메모리: shm2.c
1 #include <sys/ipc.h>
…
7 int main()
8 {
9 int shmid;
10 key_t key;
11 char *shmaddr;
12
13 key = ftok("helloshm", 1);
14 shmid = shmget(key, 1024, 0);
15 if (shmid == -1) {
16 perror("shmget");
17 exit(1);
18 }
19
20 printf("shmid : %d\n", shmid);
21 shmaddr = (char *) shmat(shmid, NULL, 0);
22 printf("%s\n", shmaddr);
24 return(0);
25 }
실행 결과
shmid : 17
hello shared memory
부모-자식 프로세스 사이의 메모리 공유: shm3.c
1 #include <sys/ipc.h>
2 #include <sys/shm.h>
3 #include <sys/types.h>
4 #include <sys/wait.h>
5 #include <unistd.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8
9 int main()
10 {
11 int shmid;
12 char *shmp1, *shmp2;
13
14 shmid = shmget(IPC_PRIVATE,
10*sizeof(char),IPC_CREAT|0666);
15 if (shmid == -1) {
16 printf("shmget failed\n");
17 exit(0);
18 }
20 if (fork() == 0) {
21 shmp1 = (char *) shmat(shmid, NULL, 0);
22 for (int i=0; i<10; i++)
23 shmp1[i] = i*10;
24 shmdt(shmp1);
25 exit(0);
26 } else {
27 wait(NULL);
28 shmp2 = (char *) shmat(shmid, NULL, 0);
29 for (int i=0; i<10; i++)
30 printf("%d ", shmp2[i]);
31 shmdt(shmp2);
32 if (shmctl(shmid,IPC_RMID,NULL)==-1)
33 printf("shmctl failed\n");
34 }
35 return 0;
36 }
실행 결과
0 10 20 30 40 50 60 70 80 90
10.5 메모리 관리 함수
메모리 관리 함수
• # include <string.h>
• void *memset(void *s, int c, size_t n);
s에서 시작하여 n 바이트만큼 문자 c로 설정한 다음에 s를 반환한다.
• int memcmp(const void *s1, const void *s2, size_t n);
s1과 s2에서 첫 n 바이트를 비교해서, 메모리 블록 내용이 동일하면 0을 반환하고 s1이 s2보다 작으면 음수를 반환하고, s1이 s2보다 크다면 양수를 반환한다.
• void *memchr(const void *s, int c, size_t n);
s가 가리키는 메모리의 n 바이트 범위에서 문자 c를 탐색한다.
c와 일치하는 첫 바이트에 대한 포인터를 반환하거나,
c를 찾지 못하면 NULL을 반환한다.
• # include <string.h>
• void *memmove(void *dst, const void *src, size_t n);
src에서 dst로 n 바이트를 복사하고, dst를 반환한다.
• void *memcpy(void *dst, const void *src, size_t n);
src에서 dst로 n 바이트를 복사한다. 두 메모리 영역은 겹쳐지지 않는다.
만일 메모리 영역을 겹쳐서 쓰길 원한다면 memmove() 함수를 사용해라.
dst를 반환한다.
• 참고 char *strncpy(char *dst, const char *src, size_t n);
mem.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main()
{
char str[32]="Do you • ike • inux?";
char *p,*q;
p = (char *) malloc(32);
memcpy(p, str, strlen(str));
puts(p);
memset(p+12,'l',1);
puts(p);
q = (char *) memchr(p,'l',18);
puts(q);
memmove(str+12,str+7,10);
puts(str);
}
$ mem
Do you • ike • inux?
Do you • ike • inux?
• ike • inux?
Do you • ike • ike • inux
메모리 맵핑
• 메모리 맵핑
- 파일의 일부 영역에 메모리 주소를 부여할 수 있다.
- 마치 변수를 사용하는 것처럼 파일을 사용할 수 있다.
메모리 매핑 시스템 호출
#include <sys/types.h>
#include <sys/mman.h>
caddr_t mmap(caddr_t addr, size_t • en, int prot, int flag, int fd, off_t off);
fd가 나타내는 파일의 일부 영역(off부터 • en 크기)에 메모리 주소를 부여하고 메모리 맵핑된 영역의 시작 주소(addr)를 반환한다.
• 매개 변수
- addr: 메모리 맵핑에 부여할 메모리 시작 주소,
이 값이 NULL이면 시스템이 적당한 시작 주소를 선택한다.
- • en: 맵핑할 파일 영역의 크기로 메모리 맵핑의 크기와 같다.
- prot: 매핑된 메모리 영역에 대한 보호 정책을 나타낸다.
PROT_READ(읽기), PROT_WRITE(쓰기), PROT_EXEC(실행), PROT_NONE(접근 불가)
- fd: 대상 파일의 파일 디스크립터
- off: 맵핑할 파일 영역의 시작 위치
메모리 매핑을 사용한 cat 명령어 구현:mmap.c
1 #include <stdio.h>
…
8
9 int main(int argc, char *argv[])
10 {
11 struct stat sbuf;
12 char *p;
13 int fd;
14
15 if (argc < 2) {
16 fprintf(stderr, "사용법: %s 파일이름\n", argv[0]);
17 exit(1);
18 }
19
20 fd = open(argv[1], O_RDONLY);
21 if (fd == -1) {
22 perror("open");
23 exit(1);
24 }
26 if (fstat(fd, &sbuf) == -1) {
27 perror("fstat");
28 exit(1);
29 }
30
31 p = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
32 if (p == MAP_FAILED) {
33 perror("mmap");
34 exit(1);
35 }
36
37 for (• ong • = 0; • < sbuf.st_size; • ++)
38 putchar(p[• ]);
39
40 close(fd);
41 munmap(p, sbuf.st_size); // 메모리 매핑 해제
42 return 0;
43 }
핵심개념
• 지역변수와 매개변수에 대한 메모리 공간은 실행시간 스택에 자동적으로 할당되며 동적 변수는 힙에 할당된다.
• 동적 할당을 사용하는 이유는 필요할 때 필요한 만큼만 메모리를 요청해서 사용하여 메모리를 절약하기 위해서이다.
• malloc 함수는 메모리를 할당할 때 사용하고 free는 할당한 메모리를 해제할 때 사용한다.
• malloc() 함수는 메모리 할당에 성공하면 할당된 메모리의 시작주소를 반환하고 실패하면 NULL을 반환한다.
• 공유 메모리는 프로세스간 사이에 메모리 영역을 공유해서 사용할 수 있도록 해준다.
• 메모리 맵핑을 이용하면 파일의 일부 영역에 메모리 주소를 부여할 수 있다.
'Operating System > Linux' 카테고리의 다른 글
Ⅸ 프로세스 제어 (0) | 2024.05.22 |
---|---|
Ⅷ 프로세스 (0) | 2024.05.08 |
Ⅶ 파일 및 레코드 잠금 (0) | 2024.05.08 |
Ⅵ 파일 시스템 (0) | 2024.05.08 |
Ⅴ 파일 입출력, 중간고사 (0) | 2024.04.15 |