[VEDA] 6일차

나우히즈·2025년 3월 26일

VEDA

목록 보기
5/16

🔹 struct (구조체)

✅ 개념:

  • 여러 개의 서로 다른 데이터 타입을 묶어 하나의 새로운 타입으로 정의함.
  • 메모리에 각 멤버가 따로 공간을 가짐 (순차적으로).
  • 구조체에 이제껏 일반 자료형 변수명만 썼지만, 함수포인터 변수도 얼마든지 사용이 가능하다.
    - 커널 코드에서 명령어 - 관련함수 세트로 구조체 사용하는 경우가 많다.

✅ 예시:

struct SensorData {
    int temperature;
    float pressure;
    char status;
};

✅ 임베디드 관점:

  • 여러 하드웨어 상태를 논리적으로 묶을 때 자주 사용.
  • 레지스터 맵 구조를 struct로 표현해 메모리 맵에 직접 접근 가능.
  • 구조체 크기가 중요한 경우 #pragma pack 또는 attribute((packed))를 통해 패딩 제거 필요.
struct __attribute__((packed)) RegMap {
    uint8_t control;
    uint16_t status;
    uint8_t data;
};

🔸 union (공용체)

✅ 개념:
• 여러 멤버가 같은 메모리 공간을 공유함.
• 하나의 멤버만 유효하며, 가장 큰 멤버 크기만큼 공간이 할당됨.

✅ 예시:

union DataPacket {
    uint32_t value;
    uint8_t bytes[4];
};

✅ 임베디드 관점:
• 바이트 레벨로 데이터 해석할 때 유용.
• 예를 들어, 32비트 데이터를 바이트 단위로 분해하거나 결합할 때 사용.
• 센서 값 파싱, 통신 프로토콜 구현 등에 필수.

🔻 enum (열거형)

✅ 개념:
• 관련된 상수들에 이름을 붙여 가독성과 유지보수성을 높임.
• 내부적으로는 정수형(int)으로 처리됨.

✅ 예시:

enum SystemState {
    IDLE,
    RUNNING,
    ERROR
};

✅ 임베디드 관점:
• 상태 머신(state machine) 설계에서 상태를 명확하게 표현.
• 플래그, 명령 코드, 모드 구분 등에 사용.
• 컴파일러 최적화 측면에서도 효과적이고, 디버깅 시 출력값이 의미 있는 이름으로 바뀌어 가독성 향상.


18장. 파일 입출력

파일 입출력 : 파일을 읽어오거나, 수행결과를 저장.
현업에서는 보통 데이터베이스에 저장하나, 간단한 내용은 파일입출력 사용할 수 있음.

입출력 프로세스

  1. 프로그램을 실행
  2. 메모리에 프로그램 탑재 (프로세스화)
  3. 실행중인 상태에서 입출력 버퍼가 프로그램 실행 공간에 할당됨.
  4. 키보드 입력으로 입력한 문자(혹은 출력하려는 문자)가 입력 버퍼(혹은 출력버퍼)에 쌓임.
  5. scanf 함수는 입력버퍼로부터 문자를 받아오게 되어있음. 반대로 printf로 출력하려는 내용은 출력버퍼에 쌓이고 모니터로 전달.

운영체제가 프로세스에 입력,출력,에러 버퍼를 만들고 기본적인 키보드-모니터 장치로 연결시켜줌. 입력하고 출력이 버퍼를 통해 진행되어서, 이를 스트리밍이라고 하며 스트림버퍼라고 한다.

키보드에서 입력, 모니터로 출력하는 방법이나 파일에 대한 입출력이 동일하다.
표준 입/출력 대신 하드디스크에 존재하는 파일내용을 입력 출력하는 것 뿐이다.

모니터나 키보드는 자동적으로 운영체제가 할당시켜주나, 파일은 별도로 지정해주는것이 필요하다. 어떤 파일로부터 입력받을지, 출력할지를 지칭해주는게 필요함.

📁 C의 기본 파일 입출력 함수들

🔹 fopen

  • 파일을 열고 FILE* 포인터를 반환합니다.
// ex)
FILE *fp = fopen("data.txt", "r");

모드 예시:

  • "r" : 읽기
  • "w" : 쓰기 (기존 내용 삭제)
  • "a" : 이어쓰기
  • "rb" / "wb" : 이진 모드로 읽기/쓰기(for fread, fwrite)

🔹 fclose

  • 파일을 닫고 버퍼를 비웁니다.
fclose(fp);

🔹 fread / fwrite

  • 이진 파일에서 메모리 블록 단위로 읽거나 씁니다. 바이트 단위로 입출력하는 함수.
  • 메모리에 담긴 바이트값 자체를 파일에 입력하거나 출력하게 해주는 함수이다.
  • 바이트 단위로 입출력하려면, 파일을 열 때 바이너리 모드('b')로 열어야함.
fread(buffer, size, count, fp);
fwrite(buffer, size, count, fp);
fread(buf, sizeof(char), 100, fp); → 1byte씩 반복횟수 100회(최대 100바이트)를 fp로부터 buf로 읽어들임.

🔹 fgets / fputs
• 문자열 단위(\n)로 읽고 씁니다 (텍스트 파일 전용).

fgets(buffer, sizeof(buffer), fp);
fputs("Hello\n", fp);

🔹 fscanf / fprintf

  • 형식 지정된 데이터를 읽고 씁니다 (파일용 scanf/printf).
  • fprintf를 통해 전달한 정수, 실수 등이 문자열로 바꾸어서 저장한다.
  • 바꾸어 저장하는 과정에서 데이터가 변환 -> 저장해야할 양이 많으면 파일 크기가 매우 커짐

편하긴하지만, 오버헤드가 있어서 사용하기 꺼려짐
-> fread/fwrite 를 주로 사용하는 이유.

fprintf(fp, "%d %s", 10, "text");
fscanf(fp, "%d %s", &num, str);

🧭 파일 위치 제어 함수들

🔸 fseek
• 파일의 현재 위치를 임의의 위치로 이동시킵니다.

fseek(fp, offset, origin);
•	origin:
•	SEEK_SET: 파일 시작 위치 기준
•	SEEK_CUR: 현재 위치 기준
•	SEEK_END: 파일 끝 기준

📌 이진 파일 처리나 레코드 접근에 매우 유용함.

🔸 rewind
• 파일 위치를 처음으로 되돌림. fseek(fp, 0, SEEK_SET); 와 같음.

rewind(fp);

🔸 ftell
• 현재 파일 위치를 바이트 단위로 반환.

long pos = ftell(fp);

⚠️ 파일 상태 점검 함수

🔸 feof
• 파일의 끝에 도달했는지 확인.

while (!feof(fp)) {
    fgets(buf, sizeof(buf), fp);
}

📌 주의: feof는 읽기를 시도한 후에만 true가 됩니다. 읽기 전에 사용하면 잘못된 동작할 수 있음.

🧠 임베디드 관점의 팁

  • 일반적으로 MCU 내장 플래시에서는 fopen 등을 직접 쓸 수 없지만, 파일 시스템이 있는 RTOS 환경이나 외장 SD 카드 등을 사용할 때 FatFs 같은 라이브러리로 유사 API 제공.
  • 시뮬레이터 환경에서 파일 I/O는 센서 데이터 모의, 로깅 테스트, 입출력 패턴 검증에 사용됨.
  • fseek / rewind는 고정 크기 레코드를 순회하거나 반복 재생하는 상황에서 매우 유용함.

✅ 예제: 간단한 이진 파일 읽기

FILE *fp = fopen("data.bin", "rb");
if (fp) {
    uint8_t buffer[16];
    while (fread(buffer, 1, sizeof(buffer), fp) > 0) {
        // 처리 코드
    }
    fclose(fp);
}

0개의 댓글