혼자 공부하는 C언어 (5)

Erdos·2024년 10월 6일
0

감상

목록 보기
42/43
post-thumbnail

chapter17 사용자 정의 자료형

17-1 구조체

구조체 선언과 멤버 사용

  • 멤버: 구조체 내부에서 포함된 변수들
  • 구조체의 형태를 컴파일러에 미리 알려주어야 한다.
  • 예약어 struct로 선언된다.
#include <stdio.h>

struct student // 구조체 선언
{
    int num;
    double grade;
};

int main(void)
{
    struct student s1;
    
    s1.num = 2;
    s1.grade = 2.7;
    printf("학번: %d\n", s1.num);
    printf("학점: %.1lf\n", s1.grade);
    
    return 0;
}

구조체 선언이 main 함수 앞에 있으면 프로그램 전체에서 사용할 수 있고, 함수 안에 선언하면 그 함수 안에서만 쓸 수 있다. 구조체 선언이 끝나면 그 이후부터 사용자가 정의한 새로운 자료형을 컴파일러가 인식할 수 있다.


  • 바이트 얼라인먼트(byte alignment): 구조체 데이터의 크기가 들쑥날쑥 한 경우 패딩 바이트(padding byte)를 넣어 데이터를 가지런하게 정렬
  • 멤버의 수에 따라 구조체의 크기가 달라질 수 있다.

다양한 구조체 멤버

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct profile
{
    char name[20];
    int age;
    double height;
    char *intro; // 자기 소개를 위한 포인터
};

int main(void)
{
    struct profile yuni;
    
    strcpy(yuni.name, "서하윤");
    yuni.age = 17;
    yuni.height = 164.5;
    
    yuni.intro = (char *)malloc(80);
    printf("자기소개 : ");
    gets(yuni.intro);
    
    printf("이름: %s\n", yuni.name);
    printf("나이: %d\n", yuni.age);
    printf("키 : %.1lf\n", yuni.height);
    printf("자기소개 : %s\n", yuni.intro);
    free(yuni.intro);
    
    return 0;
}

구조체 변수의 초기화와 대입 연산

  • 일반 변수와 같이 선언과 동시에 초기화 할 수 있다.
#include <stdio.h>

struct student
{
    int id;
    char name[20];
    double grade;
};

int main(void)
{
    struct student s1 = {315, "홍길동", 2.4},
                   s2 = {316, "이순신", 3.7},
                   s3 = {317, "세종대왕", 4.4};
    struct student max;

    max = s1;
    if (s2.grade > max.grade) max = s2; // 대입연산으로 복사
    if (s3.grade > max.grade) max = s3;
    
    printf("학번: %d\n", max.id);
    printf("이름: %s\n", max.name);
    printf("학점: %.1lf\n", max.grade);
    
    return 0;
}

구조체 변수를 함수 매개 변수에 사용하기

  • 구조체 변수를 사용해 값을 주고받으면 포인터 없이도 값을 바꿀 수 있다.
#include <stdio.h>

struct vision
{
    double left;
    double right;
};

struct vision exchange(struct vision robot);

int main(void)
{
    struct vision robot; // 구조체 변수 선언
    
    printf("시력 입력: ");
    scanf("%lf%lf", &(robot.left), &(robot.right));
    robot = exchange(robot);
    printf("바뀐 시력: %.1lf %.1lf\n", robot.left, robot.right);
}

struct vision exchange(struct vision robot)
{
    double temp;
    
    temp = robot.left;
    robot.left = robot.right;
    robot.right = temp;
    
    return robot; // 구조체 변수 반환
}
  • 구조체 변수를 사용해 값을 주고받으면 포인터 없이도 값을 바꿀 수 있다.
#include <stdio.h>

struct vision
{
    double left;
    double right;
};

void swap(struct vision *robot);

int main(void)
{
    struct vision robot;
    
    printf("시력 입력: ");
    scanf("%lf%lf", &(robot.left), &(robot.right));
    swap(&robot);  // 포인터로 전달
    printf("바뀐 시력: %.1lf %.1lf\n", robot.left, robot.right);
}

void swap(struct vision *robot)
{
    double temp;
    
    temp = robot->left;
    robot->left = robot->right;
    robot->right = temp;
}

그렇다면, 구조체와 포인터의 장단점

  • 구조체
    • 관련 데이터를 함께 묶어서 관리
    • 코드의 가독성과 유지 보수성 향상
    • 복잡한 데이터 구조 처리
    • 캡슐화된 데이터 처리
    • 단점: 메모리 복사 비용이 많이 발생한다.(메모리 사용, 시간)
  • 포인터
    • 데이터는 메모리에서 수정, 참조만 변경. 그래서 성능면에서 유리하다
    • 구조체가 크다면 포인터 사용 추천

17-2 구조체 활용, 공용체, 열거형

구조체

  • 어떤 대상과 관련된 데이터를 형태가 달라도 하나로 묶어 처리할 수 있다는 장점
  • 데이터 종류가 많을 때 구조체 변수 크기가 커지는 아쉬움(메모리 낭비)
  • 이럴 때는 공용체!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct score
{
    int kor;
    int eng;
    int math;
};

int main(void)
{
    
    struct score yuni = { 90, 80, 70 }; // 구조체 변수 선언과 초기화
    struct score *ps = &yuni; // 구조체 포인터에 주소 저장
    
    printf("국어 : %d\n", (*ps).kor); // 구조체 포인터로 멤버 접근
    printf("영어 : %d\n", ps->eng); // 연산자 -> 사용
    printf("수학 : %d\n",  ps->math);
    return 0;
}

구조체 배열을 처리하는 함수

#include <stdio.h>

struct address
{
    char name[20];
    int age;
    char tel[20];
    char addr[80];
};

void print_list(struct address *lp);

int main(void)
{
    struct address list[3] = {
        {"홍길동", 23, "111-1111", "울릉도 독도"},
        {"이순신", 35, "222-2222", "서울 건천동"},
        {"장보고", 19, "333-3333", "완도 청해진"}
    };
    print_list(list);
    return 0;
}

void print_list(struct address *lp)
{
    int i ;
    for(i = 0; i < 3; i++)
    {
        printf("%10s%5d%15s%20s\n",(lp+i)->name, (lp+i)->age, (lp+i)->tel, (lp+i)->addr);
    }
}
  • 모두 같은 표현, 단 우선 순위 조심하기

자기 참조 구조체

  • 개별적으로 할당된 구조체 변수를 포인터로 연결하면 관련된 데이터를 하나로 묶어 관리할 수 있다.
  • 주로 연결 리스트, 트리, 그래프와 같은 동적 데이터 구조를 구현할 때 많이 사용한다.
#include <stdio.h>

struct list
{
    int num;
    struct list *next;
};

int main(void)
{
    struct list a = {10, 0}, b = {20, 0}, c = {30, 0}; // 구조체 변수 초기화
    struct list *head = &a, *current;
    
    a.next = &b; // a의 포인터 멤버가 b를 가리킴
    b.next = &c;
    
    printf("head->num: %d\n", head->num);
    printf("head->next->num: %d\n", head->next->num);
    
    printf("list all: ");
    current = head;
    while (current != NULL)
    {
        printf("%d ", current->num);
        current = current -> next;
    }
    printf("\n");
    return 0;
}

연결 리스트

  • linked list: 구조체 변수를 포인터로 연결한 것
#include <stdio.h>
#include <stdlib.h>

// 노드 구조체 정의
struct Node {
    int data;             // 데이터 저장
    struct Node *next;    // 다음 노드를 가리키는 포인터 (자기 참조 포인터)
};

// 새로운 노드를 생성하는 함수
struct Node* createNode(int data) {
    struct Node *newNode = (struct Node *)malloc(sizeof(struc
-right:10px;>t Node)); // 메모리 할당
    newNode->data = data; // 데이터 저장
    newNode->next = NULL; // 다음 노드를 NULL로 초기화 (리스트의 끝)
    return newNode;
}

// 리스트의 모든 노드를 출력하는 함수
void printList(struct Node *head) {
    struct Node *current = head;
    while (current != NULL) {
        printf("%d -> ", current->data);
        current = current->next;  // 다음 노드로 이동
    }
    printf("NULL\n");
}

// 리스트에 노드를 추가하는 함수
void appendNode(struct Node **head, int data) {
    struct Node *newNode = createNode(data);
    
    if (*head == NULL) {  // 리스트가 비어 있을 경우
        *head = newNode;
        return;
    }
    
    struct Node *current = *head;
    while (current->next != NULL) {
        current = current->next;  // 마지막 노드까지 이동
    }
    current->next = newNode;  // 마지막 노드의 next에 새 노드 연결
}

int main() {
    struct Node *head = NULL;  // 빈 리스트 생성

    // 리스트에 노드 추가
    appendNode(&head, 10);
    appendNode(&head, 20);
    appendNode(&head, 30);

    // 리스트 출력
    printList(head);

    return 0;
}

공용체

  • 선언 방식은 구조체와 비슷. 하지만, 모든 멤버가 하나의 저장 공간을 같이 사용. (덮어쓰기)
    - 메모리 효율성: 메모리 사용량이 적다
    • 하드웨어 레지스터나 네트워크 패킷의 해석 등에 자주 사용
#include <stdio.h>

union student
{
    int num;
    double grade;
};

int main(void)
{
    union student s1 = { 315 };
    printf("학번: %d\n", s1.num);
    s1.grade = 4.4;
    printf("학점: %.1lf\n", s1.grade);
    printf("학번: %d\n", s1.num);
    return 0;
}

열거형

#include <stdio.h>

enum season {SPRING, SUMMER, FALL, WINTER};

int main(void)
{
    enum season ss;
    char *pc = NULL;
    
    ss = SPRING;
    switch (ss)
    {
    case SPRING:
        pc = "inline"; break;
    case SUMMER:
        pc = "swimming"; break;
    case FALL:
        pc = "trip"; break;
    case WINTER:
        pc = "skiing"; break;
    }
    printf("나의 레저 활동 => %s\n", pc);
    return 0;
}

typedef를 사용한 형 재정의

#include <stdio.h>

struct student
{
    int num;
    double grade;
};

typedef struct student Student;
void print_data(Student *ps);

int main(void)
{
    Student s1 = {315, 4.2};
    print_data(&s1);
    return 0;
}

void print_data(Student *ps)
{
    printf("학번: %d\n", ps->num);
    printf("학점: %.1lf\n", ps->grade);
}

chapter18 파일 입출력

18-1 파일 개방과 입출력

스트림 파일과 파일 포인터

  • 스트림 파일: 프로그램과 입출력 장치 사이의 다리 역할을 하는 논리적인 파일
    • 문자 배열 형태의 버퍼를 가지고 있음
    • 버퍼에서 데이터를 읽거나 쓸 때 그 위치를 알아야 하고 버퍼의 메모리 위치와 크기도 필요함
  • 스트림 파일의 장점
  1. 입출력 효율을 높이고 장치로부터 독립된 프로그래밍이 가능하다
  2. 스트림 파일을 사용하면 프로그램의 장치의 입출력 속도 차이를 줄일 수 있다.

기본적으로 개방되는 표준 입출력 스트림 파일

  • 운영체제는 프로그램을 실행할 때 기본적으로 3개의 스트림 파일을 만든다.

chapter19 전처리와 분할 컴파일

19-1 전처리 지시자

매크로명을 만드는 #define

  • 매크로명으로는 대문자 쓴다
  • 매크로 상수: 상수 대신에 쓰이는 매크로명
  • 매크로명을 정의할 때 치환될 부분이 길어 여러 줄에 써야 한다면 백슬래시로 연결
  • 장점: 자주 사용하는 복잡한 숫자나 문자열 등을 의미 있는 단어로 쉽게 표현할 수 있다
  • 단점: 디버깅, 유지보수가 힘들다

조건부 컴파일 지시자

#if, #else, #elif, #ifdef, #ifndef, #endif
profile
수학을 사랑하는 애독자📚 Stop dreaming. Start living. - 'The Secret Life of Walter Mitty'

0개의 댓글