[자료구조 - C언어] 자료구조 [1강-10강] 복습 및 정리
2022. 8. 17. 20:30ㆍCS/자료구조
728x90
//공부 기록용 포스팅입니다. 틀린 부분이 있을 경우 댓글로 알려주시면 감사합니다! 😎
자료구조 1강
- 메모리
- 모든 변수는 주소를 가진다.
- 포인터
- 포인터는 메모리 주소를 값으로 가지는 변수이다.
type-name * variable-name;
- variable-name은 선언된 포인터 변수의 이름
- *는 포인터 변수임을 표시하는 기호
- type-name은 * variable-name이 저장하는 메모리 주소에 저장된 값의 데이터 타입
- & 연산자: 엠퍼센트
- 연산자 &(엠퍼센트)는 변수로부터 그 변수의 주소를 추출하는 연산자
int a = 100; int *ptr = &a; //ptr = 변수 a의 주소 값
- ptr은 주소값을 저장 → *ptr은 ptr(주소값) 가리키는 값
- ptr은 a의 주소 값을 저장 → *ptr은 a의 주소 값이 가리키는 값 100
- 포인터와 배열
int a[10]; //정수형 배열, 배열의 이름은 a, 배열 a의 크기는 10
- 배열의 이름 a는 배열의 시작 주소 a[0]를 저장하는 포인터 변수
- *a와 a[0]은 동일한 의미
- *(a+1)와 a[1]은 동일한 의미 - 메모리 주소는 연산 가능하다.
- 배열의 이름 a는 배열의 시작 주소 a[0]를 저장하는 포인터 변수
- 동적 메모리 할당
- malloc 함수를 사용해서 직접적으로 메모리를 요청해서 할당받아 데이터를 저장해 두는 것
- malloc 함수를 호출 → 동적 메모리 할당을 요청 → 요구하는 크기의 메모리를 할당 → malloc 함수가 메모리의 시작주소를 반환
- 일반적으로는 변수를 선언하고 그 변수에 데이터를 저장
int *p; p = (int*)malloc(sizeof(int) * 10); p[0] = 12; *(p+1) = 16;
- malloc 함수는 메모리 어딘가에 연속된 40byte의 메모리 공간을 확보한 다음 그 메모리 공간의 시작주소(첫 번째 byte)의 주소를 리턴
- malloc으로 할당 받은 메모리는 보통의 배열처럼 사용한다.
- malloc 함수를 사용해서 직접적으로 메모리를 요청해서 할당받아 데이터를 저장해 두는 것
- 배열 키우기(reallocation)
- 할당 받은 배열의 크기 그 자체를 그냥 키우는 것은 가능하지 않다.
int * array = (int *)malloc(4*sizeof(int)); array[0] = 1; array[1] = 2; *(array+2) = 3; //array[4] = 4; 공간부족 int *tmp = (int *)malloc(8*sizeof(int)); for (int i=0; i<4; i++) tmp[i] = array[i]; //free(array); array = tmp;
- 메모리의 다른 곳에 더 큰 배열을 할당 받은 다음(tmp)
- 원래 배열의 데이터(array)를 새로 할당 받은 더 큰 배열(tmp)로 옮기고 새로 할당 받은 공간(array = tmp)을 사용
- array = tmp: array에 tmp를 저장하면 array가 새로 할당받은 메모리 공간의(tmp) 주소를 갖는다.
- 할당 받은 배열의 크기 그 자체를 그냥 키우는 것은 가능하지 않다.
- 가비지(garbage)
- 어떤 프로그램이 메모리를 할당 받았지만, 할당 받은 메모리의 주소를 아무도 가지고 있지 않게 되면 그 메모리 공간을 가비지라고 부른다.
- free() 함수를 이용해 사용하지 않는 메모리 공간을 반환시켜야 한다.
자료구조 2강
- 문자열: char 타입의 배열이 각 칸마다 문자 하나씩 저장되는 것
- 배열의 맨 마지막에는 null character(’\0’)가 저장되어 있어야 문자열의 끝을 표시할 수 있다.
- 문자열 생성의 차이
- str[]은 배열 안의 값은 바꿀 수 있지만(= str[1] = “k”), str이 전혀 다른 배열을 가리키도록 할 수 없다.
- *str은 배열 안의 값은 바꿀 수 없지만, str이 전혀 다른 새로운 문자열을 가리킬 수 있다(= str = tmp;)
- 문자열을 저장할 때
- scanf(”%s”, buffer)
- 배열의 이름이 실제 그 주소를 저장하기 때문에 &연산이 필요 없다.
- scanf(”%s”, buffer)
- 포인터 변수들 간의 치환문
char * words[100]; while(){ words[n] = buffer; } //입력 aaaa, bbbb, cccc //출력 cccc, cccc, cccc while(){ words[n] = strdup(buffer); } //입력 aaaa, bbbb, cccc //출력 aaaa, bbbb, cccc
- 포인터 변수는 어떤 문자 배열의 주소를 저장할 포인터 변수이지 칸 자체가 문자 배열인 것은 아니다
- 하나의 문자열을 받아 복제본을 만들어 복제본의 주소를 리턴할 수 있는 strdup를 사용해야 한다.
- 포인터 변수는 어떤 문자 배열의 주소를 저장할 포인터 변수이지 칸 자체가 문자 배열인 것은 아니다
자료구조 3강
- 문자 입력시
- scanf는 공백 단위로 단어 하나씩만 입력받기 때문에 문장을 읽기에 적절하지 않다.
- gets는 줄바꿈 문자가 나올 때 까지 통째로 읽기 때문에 사이즈를 초과해서 입력을 받을 수 있어서 문제가 발생할 수 있다.
- fgets는 배열의 크기를 매개변수로 받기 때문에 어떤 경우에도 그 사이즈를 초과할 수 없기 때문에 사용하기 적절하다.
fgets(buffer, BUFFER_SIZE, stdin); //fgets(문자열, 첫 번째 매개변수에 대한 크기, 표준 입력 파일 포인터(= 키보드))
자료구조 4강
- c언어에서 메모리 관리
- 전역변수(global variable)
- 함수의 외부에 선언된 변수들, 프로그램이 시작될 때 할당되며 종료될 때 까지 유지
- 메모리 영역 중 data section에 위치
- 지역변수(local variable)
- 함수 냉부에 선언된 변수들, 자신이 속한 함수가 호출될 때 메모리가 할당되며 함수가 return 될 때 소멸 → strdup
void add(){ char buf1[BUFFER_SIZE], buf2[BUFFER_SIZE]; //지역변수 buf1, buf2 scanf("%s", buf1); scanf("%s", buf2); names[n] = strdup(buf1); //strdup와 strcpy numbers[n] = strdup(buf2); }
- 내부 함수가 종료된 후에도 소멸하지 않고 프로그램이 실행되는 중에 계속해서 전역적으로 유지되어야 하는 데이터들은 지역변수의 주소값이 아닌, strdup함수가 malloc으로 heap에 메모리를 할당 받아서 값을 복사한 새로운 문자열의 주소를 저장해야 한다.
- 메모리 영역 중 stack에 위치
- 동적 메모리 할당(dynamic memory allocation)
- 아무 때나 malloc 등의 함수를 호출하여 필요한 크기의 메모리를 할당할 수 있다.
- 명시적으로 free() 함수를 호출하여 반환하지 않는 한 계속 유지
- 메모리 영역 중 heap에 위치
- 내부 함수가 return, 종료되더라도 그 메모리를 계속해서 사용할 수 있다.
- 아무 때나 malloc 등의 함수를 호출하여 필요한 크기의 메모리를 할당할 수 있다.
- 전역변수(global variable)
자료구조 5강
- 파일로 저장하고 로드하기
FILE *fp = fopen(fileName, "r"); fscanf(fp, "%s", buf) != EOF; fclose(fp);
- fopen함수는 file pointer 라는 값을 리턴
- w: 파일에 쓸 때, 파일이 미리 존재하지 않아도 된다.
- r: 파일을 읽을 때, 파일이 미래 존재해야 한다.
- fsacnf 함수는 파일의 끝에 도달하면 EOF 값 리턴
- EOF = End Of File
- fscanf(fp, “%s”, buf): 입력받은 fp를 읽어 buf에 저장
- fclose(fp): 사용이 끝난 파일은 반드시 받아야 한다.
- fp는 fopen에서 리턴받은 파일 포인터
- fopen함수는 file pointer 라는 값을 리턴
- 데이터를 추가할 때
- 정렬된 상태를 유지하는 삽입정렬을 사용(= 한 칸씩 뒤로 이동해서 삽입)
자료구조 6강
- 이중 포인터 타입 변수
char * names[100]; char ** names;
- char * names[100];
- 캐릭터 포인터 타입의 배열의 이름이 names
- 배열의 이름은 배열의 시작주소를 가리키는 포인터 변수
- 캐릭터 포인터 타입 + 배열의 이름(= 포인터 변수)
= char * 타입의 배열의 이름- = char * * names: 이중 포인터 타입 변수
- 캐릭터 포인터 타입 + 배열의 이름(= 포인터 변수)
- 배열의 이름은 배열의 시작주소를 가리키는 포인터 변수
- 캐릭터 포인터 타입의 배열의 이름이 names
- char * names[100];
- tokenizing
- 구분 문자(delimiter)를 이용하여 하나의 긴 문자열을 작은 문자열들로 자르는 일
- strtok을 이용한 문자열 자르기
#include <stdio.h> #include <string.h> int main(void){ char str[] = "now # is the time # to start preparing ### for the exam#"; char delim[] = "#"; char *token; //첫 번째 호출 token = strtok(str, delim); while(token != NULL){ //문자열이 끝나서 더 이상 토큰이 존재하지 않으면 null 리턴 printf("next token is: %s:%d\\n", token, strlen(token)); //이어진 호출들 token = strtok(NULL, delim); } return 0; }
- token = strtok(문자열의 주소, 구분 문자로 사용할 문자 배열)
- 리턴 값은 문자열을 구분자 기준으로 tokenizing 했을 때 첫 번째 toekn의 시작 주소를 리턴
- str = "now # is the time # to start preparing ### for the exam#";
- token = strtok(str, delim); ~ ‘now’의 n의 주소
- 첫 번째 호출에서는 tokenizing 하고자 하는 문자열의 주소를 주어야 하지만, 그다음부터는 NULL 입력
token = strtok(NULL, delim)
- 리턴 값은 문자열을 구분자 기준으로 tokenizing 했을 때 첫 번째 toekn의 시작 주소를 리턴
- strtok는 첫 번째 시작 주소를 찾아 리턴해주는 것에 그치지 않고 첫 번째 토큰이 끝나는 위치에 null character를 삽입한다.
- strtok은 원본 문자열을 변화시킨다.(= ‘\n’을 삽입한다.)
- char * str = “ “; 변경할 수 없는 상수(string literal), strtok 사용 불가
- char str[] = “”; 변경할 수 있는 변수, strtok사용 가능
- token = strtok(문자열의 주소, 구분 문자로 사용할 문자 배열)
자료구조 7강
- 구조체(struct): 항상 같이 붙어 다녀야 하는 데이터들을 하나의 그룹으로 묶어주는 역할
typedef struct person{ char * name; char * number; char * email; char * group; } Person; Person directory[100];
- struct person을 정의하고 typedef를 사용해서 Person으로 renaming
- typedef 선언을 사용하여 C에서 이미 정의된 형식이나 사용자가 선언한 형식에 대한 보다 짧거나 의미 있는 이름을 생성할 수 있다.
- char * name은 문자열이라는 의미
- struct person은 한 사람을 표현하기 위한 구조체로 여러 사람을 표현하기 위해서는 배열을 사용
Person directory[CAPACITY]; //struct 간에도 치환할 수 있다. directory[i+1] = directory[i]; directory[i+1].name = strdup(name); directory[i+1].number = strdup(number); directory[i+1].email = strdup(email); directory[i+1].group = strdup(group);
- struct person을 정의하고 typedef를 사용해서 Person으로 renaming
자료구조 8강
- 구조체에 대한 포인터
- C언어에서 구조체의 배열은 일반적인 방법이 아니다
- 전달, 리턴등 구조체 내부의 모든 멤버들이 복사되기 때문에 구조체의 크기가 커지면 복사되는 데이터양이 많기 때문에 프로그램의 성능에 영향을 끼칠 수 있다.
- 구조체의 배열로 표현하게 되면 구조체의 복사를 피할 수 없다.
- 구조체의 배열 대신 구조체 포인터의 배열
Person * directory[CAPACITY];
- 포인터 변수를 매개변수로 사용해도 call-by-value로 값을 전달하는 것은 동일
- call-by-value: 값에 의한 매개 변수 전달 방식
- 하지만, 구조체의 모든 맴버들이 복사되는 것이 아니라, 주소값만 복사되기 때문에
- 복사되는 값이 주소 1개로 매우 적어진다.
- directory = tmp;
- 와 같은 표현은 객체 자체가 복사되는 것이 아니라 객체의 주소가 복사되는 것
- directory가 새로운 배열(tmp)를 가리키도록 하는 것
- 포인터 변수를 매개변수로 사용해도 call-by-value로 값을 전달하는 것은 동일
- C언어에서 구조체의 배열은 일반적인 방법이 아니다
- 새로운 연산자 ->
- (*p).name과 p->name은 동일한 표현이다.
부경대학교 권오흠 교수님의 [c로 배우는 자료구조 및 여러 가지 예제 실습] 강의 정리입니다. 감사합니다.
728x90
'CS > 자료구조' 카테고리의 다른 글
[자료구조 - C언어] 자료구조 제11강: 연결 리스트 - 개념과 기본 동작들(2) (0) | 2022.09.20 |
---|---|
[자료구조 - C언어] 자료구조 제11강: 연결 리스트 - 개념과 기본 동작들(1) (0) | 2022.09.20 |
[자료구조 - C언어] 자료구조 제10강: 전화번호부 v5.0 (0) | 2022.08.09 |
[자료구조 - C언어] 자료구조 제9강: 전화번호부 v4.0 (0) | 2022.06.02 |
[자료구조 - C언어] 자료구조 제8강: 전화번호부 v3.0 (0) | 2022.05.24 |