경일게임아카데미 멀티 디바이스 메타버스 플랫폼 개발자 양성과정 20220504 2022/04/04~2022/12/13

Jinho Lee·2022년 5월 4일
0

경일 메타버스 20220504 5주차 3일 수업내용. myprintf 해설, 표준 라이브러리, 파일 입출력, 동적 할당

0502일자 주소연산 예시 코드

// 1. Type : 데이터를 해석하는 방법
// 2. Memory : 데이터를 저장하는 공간
// 3. Object : 데이터를 저장하는 영역 -> Memory
 
int main()
{
    int a;
    a = 10; // 왜 이게 되는 것일까?
   
    // 연산자는 피연산자를 동반한다.
    // a + b
    // a = b : 할당 연산자는 피연산자가 2개 필요하다.
    // 피연산자에 조건. => 타입.
    // 모든 연산자는 피연산자의 조건(타입)이 있다.
 
    int* p;
    p = &a;
 
    int arr[5];
    arr[2];
    *(arr + 2);
    (arr + 2); // 배열 + 정수 => 포인터 + 정수 => 주소 연산
                // int* + int;
                // int*
    //*(int*) => int
   
    // a[b] == *(a + b);
    // 1. 왜 a + b는 주소 연산일까? => 피연산자의 타입 때문에 주소 연산이란 걸 알게 됨. => a의 타입은 pointer고, b의 타입은 int
    // 2. *연산자는 오직 pointer 타입에만 적용할 수 있음. => (a + b)의 타입은 pointer
    // 3. 역참조한 값의 타입은? pointer가 가리키고 있는 타입.
 
    (p + 2); //
    *(arr + 2); // arr(value) + sizeof(type) * int
                // &arr[0] + sizeof(int) * 2;
                //
 
    int arr2[2][3];
    *(*(arr2 + 2) + 3);
    arr2 + 2; // arr2 : int(*)[3];
            // 주소 연산
            // &arr2[0] + sizeof(int[3]) * 2;
            // 첫 번째 원소로부터 24바이트 뒤에 있는 것을 참조.
            // arr2 + 2 : int(*)[3];
    *(arr2 + 2); // int[3]
    *(int*);
    int;
    arr[2][3];
 
    enum { A, B, C } a = -1;
}

myprintf

  • 어떻게 하면 정수를 올바르게 출력할 수 있을까

    1. 높은 자리 수부터 구해서 차례대로 출력
    2. 일의 자리부터 구해서 반대로 출력 <-
  • 음의 정수 표현 방법

    1. 정수를 2의 보수로 음수 표현

예시 - 메타플밍 최선문 교수님 myprintf

#include <stdio.h>
#include <stdarg.h>
 
/// <summary>
/// printf와 유사하게 동작하나 기능이 제한됨.
/// %d / %c / %s
/// </summary>
/// <param name="format">형식 문자열</param>
/// <param name="">가변 인자값</param>
void myprintf(const char* format, ...);
 
int main(void)
{
    // -14 : 2진수로 표현, 2의 보수 // ~ : NOT
    // 14  : 0000 0000 0000 0000 0000 0000 0000 1110
    // ~14 : 1111 1111 1111 1111 1111 1111 1111 0001
    // +1  : 1111 1111 1111 1111 1111 1111 1111 0010
    myprintf("My Printf : %d, %c, %s\n", -14, 'A', "Hello");
 
    return 0;
}
 
void printNumber(int n)
{
    // 1000 0000 0000 0000 0000 0000 0000
    // 8    0    0    0    0    0    0
    const int isNegative = n & (1 << 31);
    if (isNegative)
    {
        putchar('-');
        n -= 1;
        n = ~n;
    }
 
    char numberString[16] = "";
    int digitCount = 0;
    do
    {
        int digit = n % 10;
 
        numberString[digitCount] = digit + '0';
        ++digitCount;
 
        n /= 10;
    } while (n != 0);
 
    for (int i = digitCount - 1; i >= 0; --i)
    {
        putchar(numberString[i]);
    }
}
 
void printString(const char* str)
{
    while (*str != '\0')
    {
        putchar(*str);
        ++str;
    }
}
 
void myprintf(const char* format, ...)
{
    va_list args;
    va_start(args, format);
 
    while (*format != '\0')
    {
        if (*format == '%')
        {
            ++format;
 
            if (*format == '\0')
            {
                break;
            }
 
            switch (*format)
            {
            case 'd':
                int n = va_arg(args, int);
                printNumber(n);
                break;
            case 'c':
                int ch = va_arg(args, int);
                putchar(ch);
                break;
            case 's':
                const char* str = va_arg(args, const char*);
                printString(str);
                break;
            }
 
            ++format;
 
            continue;
        }
 
        putchar(*format);
        ++format;
    }
 
    va_end(args); 
}

표준 라이브러리

1. <assert.h>

  • assert() : 오입력 등의 오류를 확인. 조건에 대하여, 참인지 거짓인지를 반환.
    디버그를 할 때, 몇 번째 줄에 어떤 문제가 있는지를 보여준다. 디버그 모드에서만 작동한다.
    시스템 상 쓰이면 안되는 코드나, 반드시 만족해야하는 조건에 대하여 쓰인다.
assert(0 == 10); // Assertion failed.

2. <ctype.h>

  • 문자열의 글자가 어떤 것인지 (숫자, 알파벳, 특수 기호 등등) 판별하여 보여준다.

3. <float.h>

  • 부동 소수점의 한계(상수)를 정리해 놓음
#include <assert.h>
#include <stdio.h>
#include <float.h>

// FLT_DIG; // float형 유효숫자
// DBL_DIG; // double형 유효숫자
// LDBL_DIG; // long double형 유효숫자	 

int IsSame(float a, float b)
{
    if (a - b < FLT_EPSILON)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
 
int main(void)
{
    float f = 1.0 - 0.7;
    if (f == 0.3)
    {
        printf("출력 되니?");
    }
 
    if (IsSame(f, 0.3))
    {
        printf("같음");
    }
}
  • 소수점을 비교하려면 FLT_EPSILON 사용.

4. <math.h>

  • 사용자로부터 수를 입력 받고 계산.
  • 부동 소수점 계산도 가능. // 나머지 등
  • 절댓값, 모듈러, 나머지, 사칙연산, 지수, 비교연산, 삼각함수, 삼각함수의 역함수 등등.

5. <stdbool.h>

  • true, false 값을 불리언(bool) 타입으로 넣을 수 있게 해준다.

6. <stdint.h>

  • int의 크기를 표준으로 정의해준다.

7. <time.h>

  • time 타입을 사용할 수 있게 하여, 현재의 시각 등을 함수 등을 통해 사용할 수 있도록 해준다.
  • 그리니치 천문대 기준으로 보통 나온다.

8. <stdlib.h>

  • 표준 라이브러리.
  • 시스템을 다루고 프레임워크를 만들 때 많이 사용하는 기능을 모아 놓은 라이브러리.

9. <stdio.h>

  • 표준 입출력 라이브러리.
  • 파일의 입출력을 할 수 있도록 해준다.
  • 파일의 종류
    1. 텍스트 파일 : 텍스트 에디터로 열 수 있는 파일로, 우리가 읽을 수 있는 문자로 구성
    2. 바이너리 파일 : 그 외의 모든 파일
    • 텍스트 파일에 쓸 수 있는 함수 : fputs() / fputc() / fprintf()
    • 텍스트 파일에서 읽어들일 수 있는 함수 : fgets() / fgetc() / fscanf()
    • 바이너리 파일에 쓸 수 있는 함수 : fwrite()
    • 바이너리 파일에서 읽어들일 수 있는 함수 : fread()
  • 주의 : 파일을 열 때는 항상 예외 처리를 할 것. (오류 발생 시 탈출)
  • 주의 : 프로그램을 끝내기 전에 반드시 파일을 닫을 것.

파일 입출력 예시

#include <stdio.h>

// 파일 입출력
int main(void)
{
	// 1. 파일을 다루기 위한 객체 생성
	FILE* fp = NULL;

	// 2. 파일을 연다.
	// 파일의 종류
	// 1) 텍스트 파일 : 텍스트 에디터로 열 수 있는 파일로, 우리가 읽을 수 있는 문자로 구성
	// 2) 바이너리 파일 : 그 외의 모든 파일
	if (0 != fopen_s(&fp, "Metaverse", "w"))	//	Metaverse라는 이름을 가진 텍스트 파일을 연다.
	{
		printf("오류 발생함.");	// 파일을 열 때는 항상 예외 처리에 주의할 것.

		return 1;
	}

	// 3. 파일을 조작한다.
	// 텍스트 파일에 사용할 수 있는 함수 : fputs() / fputc() / fprintf()
	// 바이너리 파일에 사용할 수 있는 함수 : fwrite()
	fputs("Hello File!", fp);
	fputc('J', fp);
	fprintf(fp, "\nThe name of the queen : %s\n", "퀸균지");

	// 4. 파일을 닫는다.
	fclose(fp);

	if (0 != fopen_s(&fp, "Metaverse", "r"))	//	Metaverse라는 이름을 가진 텍스트 파일을 연다.
	{
		printf("오류 발생함.");	// 파일을 열 때는 항상 예외 처리에 주의할 것.

		return 1;
	}

	// 텍스트 파일에서 읽어들일 수 있는 함수 : fgets() / fgetc() / fscanf()
	// 바이너리 파일에서 읽어들일 수 있는 함수 : fread()

	char ch = fgetc(fp);
	char str[128] = "";
	fgets(str, sizeof(str), fp);	//	개행 문자까지 받아들인다.
	char str2[128] = "";
	fscanf_s(fp, "The name of the queen : %s\n", str2, sizeof(str2));

	printf("읽어들인 문자 : %c\n", ch);
	printf("읽어들인 문자열 : %s\n", str);
	printf("퀸은 누구? : %s\n", str2);

	fclose(fp);

	// ----------------------------
	// 바이너리 파일
	// ----------------------------
	if (0 != fopen_s(&fp, "Metaverse2", "wb"))	//	Metaverse2라는 이름을 가진 바이너리 파일을 연다. // wb 모드
	{
		printf("오류 발생함.");	// 파일을 열 때는 항상 예외 처리에 주의할 것.

		return 1;
	}

	struct Student
	{
		int Age;	// 4바이트
		enum { A, B, O, AB } BloodType;
		char Name[24];
	};

	struct Student s = { 20, A, "최선문"};

	if (1 != fwrite(&s, sizeof(s), 1, fp))
	{
		printf("오류 발생함.");

		fclose(fp);

		return 1;
	}

	fclose(fp);

	if (0 != fopen_s(&fp, "Metaverse2", "rb"))	//	Metaverse2라는 이름을 가진 바이너리 파일을 연다. // rb 모드
	{
		printf("오류 발생함.");	// 파일을 열 때는 항상 예외 처리에 주의할 것.

		fclose(fp);

		return 1;
	}

	struct Student s2 = { 0 };
	if (1 != fread(&s2, sizeof(s2), 1, fp))
	{
		printf("오류 발생함.");

		fclose(fp);

		return 1;
	}

	printf("나이 : %d, 혈액형 : %c, 이름 : %s\n", s2.Age, s2.BloodType, s2.Name);

	fclose(fp);

	return 0;
}

동적 할당

  • void* malloc( size_t size );
  • malloc(필요한 공간의 크기)로 사용. 객체에 힙에서의 메모리 공간필요한 만큼 할당한다.
  • 포인터로 다룬다.
  • malloc(sizeof(데이터 타입))의 형태로 많이 사용한다.
  • 메모리는 자원(Resource)이다.
  • 자원이라는 것은 시스템으로부터 빌려와서 사용이 끝나면 돌려줘야 하는 것이다.
  • 사용이 끝난 메모리를 돌려주지 않으면 메모리 누수(Memory lack)가 발생한다.
  • 그렇기에, free()사용이 끝난 메모리를 힙에 반환해야 한다.

메모리를 수동으로 관리할 때 문제점

  1. 언제 사용이 끝나는지 불명확함.( halt problem, 정지 문제 )
    ⇒ 반환이 안될 수 있다. ⇒ 메모리 누수
  2. 반환이 됐음에도 불구하고 메모리에 다시 접근하는 경우
    유효하지 않은 메모리 주소를 갖고 있는 포인터를 댕글링 포인터(Dangling Pointer)
    “메모리 엑세스 접근 위반”
  3. 반환이 됐음에도 불구하고 또 반환하는 경우
    이중 해제(Double Free)
    • 주의 : 안전한 메모리 관리를 위해, 메모리가 해제 됐다면 꼭 NULL 포인터로 만들어 줄 것.

그 외

typedef : 타입 재정의

  • typedef type identifier;
  • 타입을 재정의하는 것으로 어떤 타입에 별명을 지어주는 것.
    • 디버그 기능 : F11
      함수로 넘어가 함수 단위로 디버그하는 기능

0개의 댓글

관련 채용 정보