본문 바로가기
코스웨어/16년 스마트컨트롤러

2016-08-28_조재찬_스터디일지_C언어-파일위치 지시자 / 메모리 관리와 동적할당

by 알 수 없는 사용자 2016. 8. 29.
728x90
반응형

파일 위치 지시자

FILE 구조체의 멤버 중 하나

Read/Write에 대한 위치 정보를 갖고 있다. (어디까지 읽었는지 또는 어디부터 이어서 쓸건지)


파일 입출력 함수가 호출될때 

파일 위치 지시자의 참조 위치가 달라짐



파일 위치 지시자의 이동 fseek


#include <stdio.h>

int fseek(FILE * stream, long offset, int wherefrom);

// 성공시 0, 실패시 0이 아닌 값 반환


어디서부터(wherefrom) 

몇칸 오른쪽(+) 또는 왼쪽(-)으로 이동할건지에 대한 인자(offset)를 전달받음


SEEK_SET  - 파일 맨앞에서부터 이동시작

SEEK_CUR - current : 현재 위치에서부터 이동시작


SEEK_END - 파일 맨끝에서부터 이동시작

  주의 할점은 END는 EOF를 뜻하는 것



현재 파일 위치 지사자의 위치를 보여주는 함수 ftell


#include <stdio.h>

long ftell(FILE * stream);

// 파일 위치 지시자의 위치 정보 반환, 오류시 -1 반환


읽기/쓰기 위치는 0부터 시작


#include <stdio.h>

int main(void)
{
	/* 파일생성 */
	FILE * fp = fopen("text.txt", "wt");
	fputs("123456789", fp);
	fclose(fp);

	/* 파일개방 */
	fp = fopen("text.txt", "rt");

	/* SEEK_END test */
	printf("\n현재위치 : %d\n", ftell(fp) + 1); // 읽기/쓰기 위치는 0부터 시작이지만 시작점을 1로 가정한다.
	fseek(fp, -2, SEEK_END);    // END는 EOF임에 주의!
	putchar(fgetc(fp));    // 8을 읽고난뒤 다음 문자를 가리킴!
	printf("\n현재위치 : %d\n", ftell(fp) + 1); 

	/* SEEK_SET test */
	fseek(fp, 2, SEEK_SET);
	putchar(fgetc(fp));    // 3을 읽고난뒤 다음 문자를 가리킴!
	printf("\n현재위치 : %d\n", ftell(fp) + 1); 

	/* SEEK_CUR test */
	fseek(fp, 2, SEEK_CUR);    // 4에서 오른쪽 2칸 이동
	putchar(fgetc(fp));    // 6이 출력


	fclose(fp);
	return 0;
}

Output : 

현재위치 : 1

8

현재위치 : 9

3

현재위치 : 4

6



ftell을 이용한 위치정보 저장, 활용예제


#include <stdio.h>

int main(void)
{
	long fpos;
	int i;

	/* 파일생성 */
	FILE * fp=fopen("text.txt", "wt");
	fputs("1234-", fp);
	fclose(fp);

	/* 파일개방 */
	fp=fopen("text.txt", "rt");

	for(i=0; i<4; i++)
	{
		putchar(fgetc(fp));    // 문자 하나 읽어 출력후 지시자는 다음 바이트를 가리킴    
		fpos=ftell(fp);    // 현재위치 정보를 fpos 변수에 담음
		fseek(fp, -1, SEEK_END);    // - 로 위치 이동
		putchar(fgetc(fp));
		fseek(fp, fpos, SEEK_SET);    // fpos에 담긴 위치로 이동
	}
	fclose(fp);
	return 0;
}




메모리의 구성


유사한 성향의 데이터를 묶어 관리할 수있도록 다음과 같이 메모리 공간이 나뉘어진다.

code / data / heap / stack


층층이 나눠진 서랍장 수납공간과 같이 

효율적인 관리뿐만 아니라 메모리 접근 속도도 향상되게 된다.


 

메모리의 stack에는 일반적으로 값이 밑에서부터 위로 만들어진다.


넣어진 종이컵을 꺼내려면 나중에 넣은 종이컵부터 꺼내게 되는 것과 같다.


함수의 호출순서가 main->fct1->fct2라면

stack의 반환은 역순으로 이루어진다. (fct->fct1->main)


메인함수 종료시 스택영역 소멸

프로그램 종료시 데이터 영역에 할당된 것도 소멸


지역변수 - 함수를 빠져나갈때 할당된 메모리가 소멸


#include <stdio.h>

char * ReadUserName(void)
{
	char name[30];
	printf("What's your name? ");
	fgets(name, sizeof(name), stdin);
	return name;
}

int main(void)
{
	char * name1;
	char * name2;
	name1=ReadUserName();
	printf("name1: %s \n", name1);
	name2=ReadUserName();
	printf("name2: %s \n", name2);
	return 0;
}

컴파일시 warning메시지 출력

"warning C4172: 지역 변수 또는 임시: name의 주소를 반환하고 있습니다."

또한 쓰레기값이 출력되게 된다.


위 예제에서 ReadUserName 함수가 호출되어 할당된 메모리 공간은, 함수를 빠져나간후 반환과 동시에 메모리에서 소멸된다.

따라서 name 지역변수는 시간이 지나면 다른 데이터에 의해 덮어질, 의미를 갖지 않는 데이터가 된다.



전역변수 - 값을 덮어써버림

#include <stdio.h>

char name[30];

char * ReadUserName(void)
{
	printf("What's your name? ");
	gets(name);
	return name;
}

int main(void)
{
	char * name1;
	char * name2;
	name1=ReadUserName();
	printf("name1: %s \n", name1);
	name2=ReadUserName();
	printf("name2: %s \n", name2);

	printf("name1: %s \n", name1);
	printf("name2: %s \n", name2);
	return 0;
}

Output : 


What's your name? 재찬

name1: 재찬

What's your name? 찬찬

name2: 찬찬

name1: 찬찬

name2: 찬찬


name1이든 name2든 전역변수 name을 똑같이 가리키기 때문에 값을 덮어써버리게 된다.

이와 같이 지역변수로도 전역변수로도 풀지 못하는 상황을 해결하기 위한 것이 바로 malloc 함수이다.



malloc - Heap영역에 메모리 공간 할당을 위한 함수

#include <stdlib.h> // 또는 <malloc.h>

void * malloc(size_t size); // heap 영역에 생성하고자하는 바이트수만큼 메모리 공간 할당, 첫번째 메모리 주소 반환


heap 영역 : 프로그래머가 원하는 시점에 메모리 공간에 할당 및 소멸을 하기 위한 영역


malloc 함수는 인자로 숫자만 하나 전달받기 때문에

메모리의 포인터 형을 결정짓지 못한다.


void형은 type이 없기에 어떤 값이든 받을 수 있지만, void * 형 변수에 대한 포인터 연산을 할 수 없다.


따라서 다음과 같이 형변환을 거치는 호출형태를 취한다.

int * ptr1 = (int * )malloc(sizeof(int)*7); // 4byte 7개 확보

double ptr2 = (double *)malloc(sizeof(double)*9);


malloc함수는 메모리 할당 실패시 NULL을 반환

heap영역으로의 접근은 pointer를 통해서만 이루어진다.


free - Heap영역의 메모리 공간 해제

void free(void * ptr); // 할당된 메모리 공간 해제


free함수를 호출하지 않아도 프로그램 종료시엔 할당된 자원이 모두 반환된다.

하지만 실제 구현되는 프로그램은 오랜 시간 running되는 경우가 많으므로, heap 영역의 리소스 관리를 위해 free함수 사용을 습관화하는 것이 좋다.


#include <stdio.h> #include <stdlib.h> //malloc.h을 써도 되지만 일반적으로 stdlib.h을 include char * ReadUserName(void) { char * name = (char *)malloc(sizeof(char) * 30); //malloc 함수를 통해 heap에 할당 printf("니 이름이 뭐니? "); gets(name); return name; } int main(void) { char * name1; char * name2; name1 = ReadUserName(); printf("첫째 이름: %s \n", name1); name2 = ReadUserName(); printf("둘째 이름: %s \n", name2); printf("첫째 이름 재출력: %s \n", name1); // 소멸되지않았음을 확인하기 위함 printf("둘째 이름 재출력: %s \n", name2); free(name1); // malloc함수를 통해 할당한 메모리 공간 해제 free(name2); return 0; }

Output : 

니 이름이 뭐니? 조재찬

첫째 이름: 조재찬

니 이름이 뭐니? 조동찬

둘째 이름: 조동찬

첫째 이름 재출력: 조재찬

둘째 이름 재출력: 조동찬



calloc

#include <stdlib.h>

void * calloc(size_t elt_count, size_t elt_size);


malloc 함수와의 차이점은 elt_count x elt_size 크기만큼의 바이트를 동적할당한다는 것이다.

그리고 malloc 함수는 쓰레기값으로 초기화되는것과 달리 calloc는 모든 비트를 0으로 초기화한다.


realloc

#include <stdlib.h>

void * reallo(void * ptr, size_t size);


ptr이 가리키는 heap의 메모리 공간을 size_t size만큼 늘린다.


만약 malloc함수를 통해 기존에 할당된 메모리 공간에 이어서 확장할 여력이 되면 malloc과 realloc이 가리키는 주소는 같다.


기존의 malloc함수를 통해 할당되었던 메모리 공간을 확장할 여력이 안되면 새로운 메모리 공간을 할당하게 된다. 

이 때 malloc 함수를 통해 할당된 메모리를 복사하게 되며 기존의 메모리 공간은 지워진다.

따라서 realloc이 반환하는 주소값이 malloc 함수와는 달라지게 된다.


malloc과 같이 calloc,realloc도 free함수 호출을 통해 할당된 메모리 공간을 해제한다. (한번만 호출하면 된다)

728x90