경일 메타버스 20220504 5주차 3일 수업내용. myprintf 해설, 표준 라이브러리, 파일 입출력, 동적 할당
// 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;
}
어떻게 하면 정수를 올바르게 출력할 수 있을까
음의 정수 표현 방법
#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);
}
assert(0 == 10); // Assertion failed.
#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("같음");
}
}
#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;
}
- 주의 : 안전한 메모리 관리를 위해, 메모리가 해제 됐다면 꼭 NULL 포인터로 만들어 줄 것.
- 디버그 기능 : F11
함수로 넘어가 함수 단위로 디버그하는 기능