웹 풀사이클 데브코스 TIL [Day 46] - 사용자 정의 타입, 동적 메모리 할당 및 해제

JaeKyung Hwang·2024년 1월 25일
0
post-thumbnail

2024.01.25(목)

🏗️구조체

하나 이상의 서로 다른 종류의 변수들을 묶어서 새로운 데이터 타입을 정의하는 것

  • 연관된 변수들을 하나로 묶어서 관리함으로써 데이터 관리에 유용
  • 데이터 양(변수의 개수)이 많아지면 구조체가 유리
  • 순서대로 저장됨
    • 배치 예시 (32bit CPU 기준 → 4byte 단위로)
      struct Man {
      		int  age;
      		char gender;
      		int  id;
      		char name[11];
      		short int score;
      };
      
      struct Man man = { 19, 'M', 0x12345678, "Hong길동" , 100};

🏛️struct 키워드

  • 구조체 정의
    struct 구조체명
    {
        멤버1자료형 멤버1변수명;    // <-- 구조체 멤버
        멤버2자료형 멤버2변수명;    // <-- 구조체 멤버};
    struct student
    {
        char name[10];
        int age;
    		int height;
    };
  • 구조체 선언
    struct 구조체명 구조체변수명;
    struct student st1;
  • 구조체 정의와 선언 동시에
    struct 구조체명
    {
        멤버1자료형 멤버1변수명;
        멤버2자료형 멤버2변수명;} 구조체변수명;
    struct student
    {
        char name[10];
        int age;
    		int height;
    } st1;

🥡typedef 키워드

  • 구조체 변수를 선언하거나 사용할 때 매번struct를 사용하여 구조체임을 명시해야 하나 typedef 키워드를 사용하여 구조체에 새로운 이름을 선언하면 매번 struct 키워드를 사용하지 않아도 됨
  • 새로운 이름 선언
    typedef struct 구조체명 구조체별칭;
    typedef struct student STUDENT;
  • 구조체 정의와 typedef 선언을 동시에
    typedef struct (구조체명)
    {
        멤버1자료형 멤버1변수명;
        멤버2자료형 멤버2변수명;} 구조체별칭;
    typedef struct    // 구조체명 생략 가능
    {
        char name[10];
        int age;
    		int height;
    } STUDENT;
    • 구조체명이 앞에 없으면 자기 자신의 포인터를 선언할 수 없음 → 따라서 자기 자신의 포인터를 선언할 경우는 구조체명을 앞에 넣어야 한다. (구조체명은 같아도 되고 달라도 됨)
      • 구조체명이 다른 경우
        typedef struct _Man {
        		struct _Man *next;
        		struct _Man *prev;
        	
        	  char name[50];
        	  int  age;
        	  char gender;
        	  char tel[50];
        } Man, *PMan;
      • 구조체명이 같은 경우
        typedef struct Man {
        	  struct Man *next;
        	  struct Man *prev;
        	
        	  char name[50];
        	  int  age;
        	  char gender;
        	  char tel[50];
        } Man;
  • 구조체 변수 초기화 (순서 상관 X)
    구조체변수명 = {.멤버1변수명 = 초깃값, .멤버1변수명 = 초깃값,};
    st1 = {.name = "조미연", .age = 26, .height = 161};
  • 구조체 멤버 접근: 직접 접근 연산자 .
    구조체변수명.멤버변수명
    st1.name, st1.age, st1.height
    • 구조체 포인터에 접근하는 방법
      STUDENT* ptr_st1 = &st1;    // 구조체 포인터 ptr_st1
      1. 참조 연산자(*) 사용

        (*구조체포인터).멤버변수명
        printf("%s", (*ptr_st1).name);    // 조미연
      2. 화살표 연산자(->) 사용

        ✔️일반적으로 많이 사용됨

        구조체포인터 -> 멤버변수명
        printf("%s", ptr_st1 -> name);    // 조미연

🤝공용체

구조체와 달리 모든 멤버 변수가 하나의 메모리 공간을 공유

  • 구조체와 공용체 비교
    • 구조체
      struct stTemp {
      		char a;
      		int  b;
      		double c;
      } st;
    • 공용체
      union unTemp {
      		char a;
      		int  b;
      		double c;
      } un;

  • 공용체는 순서가 규칙적이지 않고, 미리 알 수 없는 다양한 타입의 데이터를 저장할 수 있도록 설계된 타입
  • 크기가 가장 큰 멤버 변수의 크기로 메모리를 할당받음
  • union 키워드를 사용하며 사용방법은 구조체와 동일

🏷️열거형

데이터들을 열거한 집합

  • enumeration의 약자로 enum(이넘)이라고 읽음
  • 컴파일러는 열거형 멤버들을 정수형 상수로 취급
  • enum 키워드 사용
    • 열거형 정의
      enum 열거형명 {1 = 초깃값,2,3
      };
      enum DayOfWeek {
          Sunday = 0,
          Monday,
          Tuesday,
          Wednesday,
          Thursday,
          Friday,
          Saturday
      };
      • 상숫값을 따로 명시하지 않으면 0부터 시작되며, 그 다음 멤버의 값은 바로 앞 멤버의 값보다 1만큼 증가되며 정의됨
      • 보통 열거형을 정의할 때 처음에 오는 값에만 초깃값을 할당하지만 모든 값에 정수를 할당할 수도 있음
    • 열거형 선언
      enum 열거형명 변수명;
  • 마찬가지로 typedef 키워드 사용 가능
    typedef enum (열거형명) {1 = 초깃값,2,3
    } 열거형별칭;
    
    열거형별칭 열거형변수명;
    typedef enum _DayOfWeek {
        Sunday = 0,
        Monday,
        Tuesday,
        Wednesday,
        Thursday,
        Friday,
        Saturday
    } DayOfWeek;
    
    DayOfWeek week;

📥동적 메모리 할당

  • 메모리 구조
    메모리설명
    코드 영역실행할 명령어들이 저장되고 CPU가 명령어들을 하나씩 가져가 처리
    데이터 영역전역변수, static 변수가 저장됨 → 프로그램 시작과 동시에 할당, 종료되면 메모리 해제
    힙 영역 (런타임에 크기 결정)동적 할당 영역 → 메모리 할당(malloc)과 해제(free)의 반복
    스택 영역 (컴파일 타임에 크기 결정)LIFO 방식, 지역변수, 매개변수, 포인터 등이 저장됨
  • 동적 메모리 → 힙 영역 사용
  • 메모리 할당과 해제에 사용하는 함수는 stdlib.h 헤더 파일에 정의되어 있기 때문에 코드 상단에 #include <stdlib.h> 추가
  • 메모리 할당: malloc 함수 사용
    void* malloc(size_t size);
    • size: 바이트 단위
    • void*: 타입이 지정되지 않은 포인터 → 사용자가 type casting해서 사용
    • 메모리 할당이 되면 메모리의 주소값을 반환 / 메모리 부족 시 NULL 포인터 반환
    • 데이터 크기에 맞춰서 할당해줘야 하므로 (데이터타입*)malloc(sizeof(데이터타입)*할당크기); 형식으로 할당
  • 메모리 해제: free 함수 사용
    void malloc(void* _Block);
    • 반환값 없음
    • malloc으로 동적 할당한 메모리 해제
  • 사용 예시
    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        int num;
        int* student;
        
        printf("학생수를 입력하세요: ");
        scanf("%d", &num);
        
        student = (int*)malloc(sizeof(int) * num);
        
        if (student == NULL) {
            printf("메모리가 할당되지 않았습니다.\n");
            return 0;
        }
        
        printf("할당된 메모리 크기는 %dbyte입니다.\n", sizeof(int) * num);
        
        free(student);
        
        return 0;
    }
    • Heap 메모리에 할당한 메모리는 Stack 메모리의 포인터 변수를 통해서만 사용 가능

    • 앞으로 객체 기반에서 new 연산자를 이용한 객체 생성의 메모리 구조는 모두 위 형태 기반

      🌟 반드시 메모리 할당 후 사용이 끝나면 해제해야 함!
      그렇지 않으면 student 변수는 사라져도 Heap에 할당된 메모리는 그대로 남아 메모리 누수 발생💦

      • 메모리 누수(memory leak)
        컴퓨터 프로그램이 필요하지 않은 메모리를 계속 점유하고 있는 현상
      • 자바스크립트의 경우 garbage collector가 사용하지 않는 메모리를 알아서 처리해주기 때문에 걱정할 필요는 없음😋

📝평가 문제

  • 강사님이 오늘 강의에서 내준 평가 문제이다.
  • online c compiler를 사용했다.
  1. 우리가 우체국에서 물건을 어디론가 보낼 때, 물건의 종류, 무게, 높이 등의 정보를 입력한다. 우리는 이러한 정보들을 갖고 있는 물건에 대한 구조체를 만들어보자. 구조체 이름은 object라고 하고, 구조체 멤버로는 이름(name), 높이(height), 무게(weight)를 갖는다. 구조체 변수를 하나 선언하고, 구조체 변수를 통해 각각의 멤버에 값을 대입한다. 대입하는 값은 사용자로부터 입력 받으며, 입력이 끝나면 대입한 멤버의 값을 화면에 출력하도록 프로그램을 작성하라.

    /******************************************************************************
    
    출력 예)
    물건의 이름: 책
    물건의 높이(cm): 30
    물건의 무게(kg): 2
    
    보낼 물건의 정보: 책, 30cm, 2kg
    
    *******************************************************************************/
    
    #include <stdio.h>
    
    struct object {
        char name[50];
        int height;
        int weight;
    };
    
    int main() {
        struct object user_object;
    
        printf("물건의 이름: ");
        scanf("%s", user_object.name);  // 배열 이름 자체가 주소 (포인터 상수)
    
        printf("물건의 높이(cm): ");
        scanf("%d", &user_object.height);
    
        printf("물건의 무게(kg): ");
        scanf("%d", &user_object.weight);
    
        printf("\n보낼 물건의 정보: %s, %dcm, %dkg\n", user_object.name, user_object.height, user_object.weight);
    	
        return 0;
    }
    #include <stdio.h>
    	
    // typedef 사용
    typedef struct {
        char name[50];
        int height;
        int weight;
    } Object;
    
    int main() {
        Object user_object;
    
        printf("물건의 이름: ");
        scanf("%s", user_object.name);  // 배열 이름 자체가 주소 (포인터 상수)
    
        printf("물건의 높이(cm): ");
        scanf("%d", &user_object.height);
    
        printf("물건의 무게(kg): ");
        scanf("%d", &user_object.weight);
    
        printf("\n보낼 물건의 정보: %s, %dcm, %dkg\n", user_object.name, user_object.height, user_object.weight);
    	
        return 0;
    }
profile
이것저것 관심 많은 개발자👩‍💻

0개의 댓글