본문 바로가기

2학년 2학기/c언어56

14-6장 main 함수의 인수 지금까지의 main함수는 인수가 없는 void를 갖고 잇는 함수였다. 이번에는 외부로부터 입력을 받을 수 있는 main()함수 형태를 살펴보자. //[외부로부터 입력을 받는 main()]int main(int argc, char* argv[]){} argc명령어가 가지는 인수들의 개수 argv[]명령어가 가지는 인수들을 문자열 형태로 전달main에 인수가 아래와 같이 전달되었다고 가정해보자. C:\cprogram> mycopy src dst 위에서 인수는 mycopy, src, dst를 말한다. 따라서, argc는 3이고, argv = [mycopy, src, dst]이다. 2024. 12. 3.
14-5장 헤더 파일 중복막기(분할 컴파일) 분할 컴파일이란?분할 컴파일은 하나의 프로그램에 여러 파일을 나누어 작성해서 분할로 컴파일을 진행하고 링크를 통해서 하나의 실행파일로 만드는 것을 의미한다.   이렇게 하면, 유지보수 재사용, 디버깅 하기 쉽다는 장점이 있다.   다만, 분할 컴파일. 즉, 파일을 여러 개 만들어서 메인 파일에 여러 파일을 포함(include)할 때, 중복으로 포함하면 예상치 못한 오류가 발생한다. 따라서, 이를 방지 위한 방법으로 사용자가 정의한 헤더파일에 #ifndef 조건부 컴파일 지시자를 사용하는 방법이 있다.  위의 코드를 간단하게 설명하면, main.c에서는 power.h를 #include해서 메인 파일에 포함시킨다. 중복으로 포함하는 것을 방지하기 위해서 power.h는 내부에 조건부 컴파일 지시자(#ifnd.. 2024. 12. 2.
14-4장 #if #if란?: #ifdef에서는 기호 상수의 존재를 확인했지만, #if에서는 기호 상수의 값을 기반으로 분기처하는 조건문이다. 따라서, 논리, 관계 연산자 사용이 가능하다.  관계 연산자#define DEBUG 1#if(DEBUG == 1) printf("디버그 중");#endif 논리 연산자#define VERSION 3.1#if(VERSION > 3.0) printf("버전 3.0이상입니다.");#endif 무조건 무시: 무조건 무시하는 경우는 조거문 내부의 블럭에 주석이 있고, 그 보다 더 큰 블럭 단위를 주석하고 싶을 때 사용한다. 주석 밖에 주석을 하는 경우 주석이 제대로 설정되지 않을 수 있기 때문이다.#if 0 /* 주석 */#endif 무조건 실행#if(DEBUG == 1) .. 2024. 12. 2.
14-3장 #ifdef #ifdef란?: 특정 기호 상수가  존재할 때, 특정 동작을 수행할지 안할지를 분기해주는 조건문이다.  위의 예시와 같이 DEBUG라는 기호 상수가 존재하면, 출력구문을 실행하고, 기호 상수가 존재하지 않으면(주석 처리), 출력문은 존재하지 않고 값만 반환하게 된다. 따라서, 기호 상수를 통해서 테스트 버전일 때와 릴리즈 버전일 때를 나눠서 동작을 설정할 수 있다.   [리눅스와 윈도우 버전확인 예제]#include #define LINUXint main(void){ int n;#ifdef LINUX printf("리눅스 버전입니다. \n"); printf("정수를 입력하시오:"); scanf("%d", &n);#else printf("윈.. 2024. 12. 2.
14-2장 함수 매크로 함수 매크로: 인자를 받아서 함수처럼 동작하는 매크로이다.   함수 매크로의 예시#define SUM(x, y) ((x) + (y))#define AVERAGE(x, y, z) (( (x) + (y) + (z) ) / 3 )#define MAX(x,y) ( (x) > (y) ) ? (x) : (y)#define MIN(x,y) ( (x)   [사용 방법] int v = SUM(3, 4)// v는 7‼️ 함수 매크로 사용시 주의할 점 ‼️ 1. 인자에 괄호 사용하기#define SQUARE(x) x*xint main(){ int v = SQUARE(3+4) //원하는 결과: 49 //실제 결괴 -> 3 + 4 * 3 + 4 = 3 + (4 * 3) + 4 -> 19} 인자에는 괄호를 꼭 사용해야.. 2024. 12. 2.
14-1장 전처리기와 단순 매크로 전처리기란?: # 기호로 시작하는데, 컴퓨터 처리에 있어서 중심적인 처리를 수행하는 부분을 위해 사전 준비적인 계산을 진행하는 것이다.     전처리기의 종류     단순 매크로: 변수처럼 어떠한 값을 특정 단어로 대체하는 것을 의미한다. 즉, 아래와 같이 3.141592를 PI라는 단어로 치환하는 것이다.    💡  #define과 typedef의 차이점 [define]: unsigned char를 uchar로 치환하는 것// define#define uchar unsigned char  [typedef]: unsigned char를 UCHAR로 치환하는 것// typedeftypeof unsigned char UCHAR   서로 같은 기능을 하지만 차이점도 있다. 1. 처리하는 것이 다르다.: ty.. 2024. 12. 2.
13-12장 다차원 배열의 동적 할당 ⭐⭐⭐ 동적 할당을 통해서 다중 배열을 만들 수 있다. 첫 번째로 배열 동적 할당을 하고, 동적 할당을 배열 요소에 다음 동적 할당을 받는다. 그림으로 표현하면 다음과 같다.    [2차원]#include #include void fillArr(int **arr, int row, int col) { int i, j; for(i = 0; i   [3차원]#include #include // 3차원 배열에 값 채우는 함수void fill3DArray(int ***arr, int depth, int row, int col) { int i, j, k; for (i = 0; i  [출력]Depth 0:0 1 2 3 4 5 6 7 8 9 Depth 1:10 11 12 13 14 15 1.. 2024. 11. 26.
13-11장 Wild pointer Wild pointer란?초기화 되지 않은 포인터를 의미한다. 초기화 되지 않은 포인터를 사용하면 해당 포인터가 가리키는 주소가 어디인지 모르기 때문에, 잘못하면 이상한 메모리를 참조할 수도 있고, 메모리 할당을 못 받을 수도 있다. 따라서, 아래의 해결 방법을 통해서 해당 문제를 방지해야한다. #include #include int main(void) { int * pi; *pi = 12; // pi는 wild pointer이다. pi = malloc(sizeof(int)); // wild pointer의 해결방안 1 - malloc(동적 할당: heap 영역) // 여기서 동작 할당을 하고 NULL을 확인해야함!!! *pi = 12; int a = 12; pi = &a; // wild po.. 2024. 11. 26.
13-10장 Dangling pointer Dangling pointer: 해제(free)된 메모리 공간의 주소를 갖고있는 포인터  [경우 1]#include #include int main(void) { int * p1; int * p2; p1 = (int *)malloc(sizeof(int)); *p1 = 5; p2 = p1; // p2는 p1과 같은 메모리를 가리킨다 (aliasing) free(p1); // 이때 p2는 dangling pointer가 된다. 즉, p2 를 통해서 메모리를 접근하려고 하는 시도는 예측 불가능한 결과를 초래한다. *p1 = 5; // 이미 해제된 p1을 통해서 메모리를 접근하고자 할 때, p1 또한 dangling pointer이다. 간단한 듯 보이지만, 대부분의 경우는 찾아내기 어렵다. return 0.. 2024. 11. 26.
13-9장 메모리 누수(3)_먼저 할당된 메모리 주소를 해제를 한 경우 #include #include typedef struct _mys { char *A; int B;} mys;char* getBuf(void) { return (char *)malloc(100 * sizeof(char));}int main(void) { char * A; char * B; mys * m = (mys *)malloc(sizeof(mys)); m->A = (char *)malloc(100*sizeof(char)); getBuf(); // getBuf에서 할당한 메모리는 찾을 수 없으므로 해제 불가능하다. free(m); // m을 먼저 해제했으므로 m->A에 접근할 수 없으므로 해제 불가능하다. return 0;}   위 사진처럼 먼저 할당 된 메모리를 해제를 하면, 다음 할당된 메모.. 2024. 11. 26.
13-8장 메모리 누수(2)_이미 해제된 동적 메모리 영역을 다시 해제 하려고하는 경우 #include #include int main(void) { char * A; char * B; A = (char *)malloc(100*sizeof(char)); B = (char *)malloc(100*sizeof(char)); B = A; free(A); // A는 해제 가능하다. free(B); // 원래의 B는 해제 불가능할 뿐 아니라, 이미 해제한 A를 다시 해제하려 고 하고 있다 (double free) return 0;}   위의 사진처럼 B에 A를 할당을 하면 동적할당 받은 2000번지의 메모리 영역에는 접근할 수 없게 되고 free도 하지 못하게된다.  또한 A와 B를 free하면 같은 메모리 영역을 두번 free하는 것이기 때문에 오류가 발생한다. 따라서, 아래와 .. 2024. 11. 26.
13-7장 메모리 누수(1)_fread가 실패해서 free를 실행하지 못하는 경우 메모리 누수란?: 메모리를 할당하고, 해제하지 않은 경우  #include #include char* readBuf(File* file, const int size) { int foo = 0; char* buf = (char*) malloc(size * sizeof(char)); if(buf == NULL) { printf("메모리 할당 오류\n") ; exit(1); } if(fread(buf, sizeof(char), size, file) != size) return NULL; // fread가 실패했다면? return buf;}int main(void) { FILE * fp; char * pc; int size = 80; fp = fopen("tmp.txt", "r"); pc = readBuf(.. 2024. 11. 26.
13-6장 동적 할당에서의 대표적인 오류들 2024. 11. 25.
13-5장 realloc(), memset(), calloc() realloc: 이미 할당 받은 메모리 값의 크기를 변경하는 것 realloc(이미 할당 받아서 사용하던 메모리 주소를 담고 있는 포인터, 변경하고 싶은 전체 크기)  예제#include #include #define SIZE 5int main(void) { int i, cur = 0, curMSize = SIZE; int *p; // 초기 메모리 할당 p = (int *)malloc(sizeof(int) * curMSize); if (p == NULL) { printf("메모리 할당 오류\n"); exit(1); } while (1) { cur++; // 메모리 확장이 필요할 경우 if (.. 2024. 11. 25.
13-4장 구조체 동적 생성 이전에 우리는 구조체 여러 개를 사용하기 위해서는 배열을 사용해서 구조체를 생성했었다. 이제는 동적 할당을 배웠으니, 동적할당을 기반으로 구조체를 동적으로 생성해보자.   [구조체 동적 생성]#include#include#includestruct Movie{ char title[100]; double rate;};int main(){ struct Movie* pi; int size = 0; printf("영화의 개수: "); scanf("%d", &size); pi = malloc(size * sizeof(struct Movie)); if (pi == NULL){ printf("메모리 할당 오류\n"); exit(1); } .. 2024. 11. 25.
13-3장 동적 할당 영역을 배열처럼 쓰기 이전에 malloc을 이용해서 Heap 영역에 있는 메모리를 동적 할당 받는 것을 해봤다. 이전에는 4byte(int)만 할당 받았지만, 이번에는 4byte를 5개 받아서 즉 int arr[5]와 같은 효과를 내는 메모리를 동적할당 받아보자. #include #include int main(void){ int* pi; pi = (int*)malloc(5 * sizeof(int)); if (pi == NULL) { printf("메모리 할당 오류\n"); exit(1); } *pi = 1; // pi[0] = 1; *(pi + 1) = 2; // pi[1] = 2; *(pi + 2) = 3; // pi[2] = 3; *(pi + 3.. 2024. 11. 19.
13-2장 malloc()과 free() maclloc(): 동적 메모리를 할당할 때 사용 int *pi; //stack 영역에서 할당pi = (int *)malloc(sizeof(int)) //반환은 void* 타입으로 ❓ 왜 malloc은 void*로 반환을 하는가? 사용자가 어떤 타입의 메모리를 할당하는지 모르기 때문에 메모리의 주소를 반환할 때 특정 타입에 한정해서 반환하지 않고, 범용적으로 사용할 수 있는 포인터 타입인 void*를 이용하는 것이다.  ❓ 왜 malloc 반환 값을 int*로 타입 캐스팅을 하는가? malloc이 범용적인 포인터 타입 void*를 반환하기 때문에 그 타입을 명시적으로 변경해주기 위해서 타입 캐스팅을 한다. 타입 캐스팅을 하면 가독성과 안정성을 높일 수 있다.   값 입력, 연산, printf, scanf.. 2024. 11. 19.
13-1장 동적 메모리 할당란? 메모리를 할당하는 방법에는 정적(static)과 동적(dynamic) 2가지 방법이 있다.  기본적으로 우리가 변수를 통해서 메모리를 할당하는 것 정적 메모리 할당이다. 정적 메모리 할당은 이미 프로그램이 시작되기 전에 결정되는 것이다. 즉, 더 큰 입력은 받아드리지 못하고 더 작은 입력은 메모리 공간이 낭비된다.  따라서, 우리는 동적 메모리 할당을 사용해서 우리가 원하는 메모리 공간을 확보해서 좀더 효율적으로 사용해야한다.  동적 메모리를 할당 받을 때는 Heap 영역에서 할당 받는다. 2024. 11. 19.
12-8장 파일 포인터 파일 포인터: 파일 내에서 읽거나 쓰기를 시작할 위치를 나타낸다.   파일 포인터에는 순차 접근, 임의 접근이 있다.순차 접근: 데이터를 파일의 처음부터 순차적으로 읽거나 기록하는 것이다.임의 접근: 임의 접근 방법은 파일의 어느 위치에서든 읽기와 쓰기가 가능하다.     파일 포인터 함수 - fseek: 파일 포인터의 위치를 변경하는 함수이다. int seek(FILE *fp, long offset, int origin); 위를 쉽게 해석해보면, "파일 포인터를  특정 지점(origin)에서 특정 값(offset)만큼 이동해라"라는 의미이다.  Origin과 관련된 상수  사용 예시fseek(fp, 0L, SEEK_SET);// 파일의 처음으로 이동fseek(fp, 0L, SEEK_END);// 파일의.. 2024. 11. 19.
12-7장 이진 파일의 생성과 fread, fwrite size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream): 메모리에서 지정된 주소(ptr)로부터 데이터를 읽어, 크기(size)와 개수(count)만큼 파일 스트림(열어놓은 파일)에 이진 형식으로 저장한다.    #include int main(void){ FILE* fp = NULL; int a = 10; fp = fopen("binary.bin", "rb"); //바이너리로 오픈 if (fp == NULL) { fprintf(stderr, "원본 파일 apple.txt를 열 수 없습니다.\n"); exit(1); .. 2024. 11. 18.
12-6장 형식화된 입출력(fscanf, fprintf) fscanf: 파일에 있는 데이터를 원하는 형식으로 입력 받는 방법  score.txt파일이 있다고 했을 때 텍스트 파일에서 데이터를 정해진 형식으로 읽어오자. score.txt유재석 80 90 75이광수 59 90 87송지효 86 45 90  #include int main(){ FILE* fp = NULL; cahr name[20]; int kor, eng, mat; double avg; fp = fopen("score.txt", "r"); if (fp == NULL) { fprintf(stderr, "원본 파일 score.txt를 열 수 없습니다.\n"); exit(1); } //파일로부터 입력 받기 fscanf(fp,.. 2024. 11. 18.
12-5장 문자열 입출력 함수(fgets, fputs) fgets(입력할 문자열, 문자열의 크기, 파일 포인터): 특정 파일에서 한 문장을 읽어오는 것 #include #include int main(void) { FILE *fp; char str[100]; // 파일 열기 fp = fopen("file.txt", "r"); if (fp == NULL) { fprintf(stderr, "파일 file.txt를 열 수 없습니다.\n"); exit(0); } // 파일에서 한 줄씩 읽어서 출력 printf("파일 내용을 출력합니다:\n"); while (fgets(str, sizeof(str), fp) != NULL) { // 파일에서 한 줄 읽기 printf("%s", st.. 2024. 11. 18.
12-4장 문자 단위 입력출력 함수(fgetc, fputc) fgetc(fp): fgetc를 사용하면 특정 파일에서 문자 한개를 읽어 올 수 있다.  #include int main(void){ FILE* fp = NULL; int c; fp = fopen("apple.txt", "r"); if (fp == NULL) { fprintf(stderr, "원본 파일 apple.txt를 열 수 없습니다.\n"); exit(1); } c = fgetc(fp) //문자 하나 읽어오기 fclose(fp); return 0;}  ❓ 파일에서 문자를 읽었는데 왜 정수형에 저장을 할까? 파일에서 문자를 읽을 경우,.. 2024. 11. 18.
12-3장 파일열기와 닫기(fopen, fclose) 파일을 쓰기 전에 파일을 저장한 것을 열어야하는데, 단순하게 어떤 파일 열어라고 하면 컴퓨터는 알아듣지 못한다. 따라서 아래의 과정을 통해서 파일을 열고, 파일에 대한 작업이 끝나면 파일을 닫는 것까지 배워보자.    ❓ 만약에 write 모드로 했는데 현재 디렉토리에 해당 파일이 없다면? 현재 디렉토리에 파일이 없다면 해당 파일 명으로 된 새 파일을 자동으로 만들고 그 파일에 텍스트를 작성한다, 2024. 11. 18.
12-2장 파일 파일은 왜 필요할까?RAM(메모리)는 전원이 켜져 있을 때만 데이터를 유지하며, 전원이 꺼지면 그 안의 데이터는 모두 사라진다. 따라서 이러한 문제를 해결하기 위해서 하드 디스크에 저장해서 전원이 꺼지더라도 데이터를 보존해야한다. 그러기 위해서는 파일을 하드디스크에 저장해야하기 때문에 파일이 필요하다,  파일파일은 일련의 연속된 byte이다.모든 파일 데이터들은 결국은 바이트로 바꾸어서 파일에 저장된다.이 byte들을 어떻게 해석하냐는 전적으로 프로그래머의 책임이다.  파일의 유형1. 텍스트 파일정의: 사람이 읽을 수 있는 형식으로 데이터를 저장하는 파일이다. 아스키 코드를 이용해서 저장한다.  구조: 텍스트 파일은 각 줄이 \n으로 구분되어 있으며, 데이터가 문장 형식으로 저장된다. 예시: .txt, .. 2024. 11. 18.
12-1장 스트림, 버퍼, 표준 입출력 스트림 1. 스트림입력과 출력을 바이트들의 흐름으로 생각하는 것이다. 쉽게 말해서, 데이터를 프로그램과 외부 장치(예: 파일, 콘솔) 간에 주고받기 위한 추상화된 데이터 통로이다. 2. 버퍼버퍼는 스트림을 통해 데이터를 주고받을 때 일정량의 데이터를 임시로 저장해 두는 메모리 공간이다. 버퍼 통해 입출력이 필요할 때마다 장치에 직접 액세스하는 대신 일정 크기만큼 모아서 전송함으로써 시스템의 성능을 최적화합니다.  3. 스트림과 버퍼의 관계스트림이 데이터를 전송하기 위한 경로라면, 버퍼는 그 데이터를 임시 저장하는 장소이다. 스트림을 사용해서 데이터를 읽거나 쓸 때, 버퍼를 이용해 전송 속도와 효율성을 높일 수 있다.  즉, 프로그램에서 스트림을 통해 데이터를 주고 받을 때, 이 데이터를 직접 스트림으로 보내기 .. 2024. 11. 18.
11-9장 typedef typedef란?: 기존에 존재하던 자료형을 새롭게 정의할 때 사용한다.typedef를 사용하는 목적은 가독성 때문이다.    위처럼 이전에 정의되어 있던 자료형을 새로운 자료형으로 정의할 수 있다.    [typedef struct - 사용 예시]#include typedef struct point { int x; int y;} POINT;int main() { POINT p1 = { 1, 2 }; printf("p1.x: %d p1.y: %d", p1.x, p1.y); // p1.x: 1 p1.y: 2}   [typedef 무명 struct - 사용 예시]#include typedef struct { int x; int y;} POINT;int main() { .. 2024. 11. 12.
11-8장 열거형 열거형이란?: 정의된 값들만 가질 수 있는 자료형을 만드는 것과 같다. 예를 들어 int는 정수만 받을 수 있고, double을 실수만 받을 수 있는 것처럼말이다. 열거형을 만드는 이유는 가독성을 위해서이다.   #include enum days { MON, TUE, WED, THU, FRI, SAT, SUN };// 포인터들의 배열을 만들고 문자열 상수로 초기화한다. const char* days_name[] = { "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" };int main(void){ enum days d; // enum days로 타입 캐스팅한 d를 1씩 증가 시키면.. 2024. 11. 12.
11-7장 공용체 공용체란?: 같은 메모리 영역을 여러 개의 변수가 공유해서 사용하는 것이다. 즉, int i가 특정 메모리 영역을 사용하면 char c는 해당 영역을 사용할 수 없다. union example { char c; // 같은 기억 공간 공유 int i; // 같은 기억 공간 공유};    #include union example { int i; char c;};int main(void){ union example data; data.c = 'A'; printf("data.c:%c data.i:%i\n", data.c, data.i); data.i = 10000; printf("data.c:%c data.i:%i\n", data.c, data.i); .. 2024. 11. 12.
11-6장 구조체와 포인터 구조체와 포인터: 구조체를 만들고 구조체 포인터 변수를 만들어 구조체의 주소를 할당해주면 구조체처럼 사용할 수 있다. #include #include struct student { int number; char name[10]; double grade;};int main(){ struct student s1 = { 24, "Kim", 4.3 }; struct student* p; p = &s; printf("학번=%d 이름=%s 학점=%f \n", s.number, s.name, s.grade); // 학번=24 이름=Kim 학점=4.3 printf("학번=%d 이름=%s 학점=%f \n", (*p).number, (*p).name, (*p).grade);.. 2024. 11. 11.