본문 바로가기
C

struct (구조체)

by yororing 2024. 6. 21.

00 개요

  • 목적: C 언어에서 사용되는 구조체 이해하기
자료형
기초 자료형 
Primary
파생 자료형
Derived
사용자-정의 자료형
User-Defined
int / unsigned int
short / unsigned short
long / unsigned long
char / unsigned char
float
double
long double
void
...
function (함수)
array (배열)
pointer (포인터)
reference (참조)
class
struct (구조체)
union (공용체)
typedef
enum (열거형)
  • 동일한 종류의 데이터를 하나로 묶을 경우 배열을 사용했지만 (int numbers[5] = { 2, 5, 3, 7, 1 };), 다른 종류의 데이터(int, double, char, etc.)를 하나로 묶을 경우 구조체 사용

01 구조체란

1. 정의

  • 여러 개의 변수(다른 데이터 종류 가능)들을 묶어서 새로운 자료형을 만들 수 있는 자료형
  • 배열과 차이: 배열은 자료형이 같은 자료들을 하나로 묶는 반면 구조체는 자료형이 달라도 묶을 수 있음

2. 문법

  • NOTE: 관례상 구조체이름(tag) 앞에 _(underslash) 또는 tag_ 또는 tag를 붙여줌

1) 구조체 선언

struct student {
    int num;
    char name[10];
    double grade;
};

  •  용어
    • struct = student의 keyword
    • student = tag
    • { 내용 } = 내용은 struct member
  • 구조체를 선언했다고 해서 변수 선언이 되지는 않음
  • 구조체 선언구조체 안에 어떤 변수가 들어간다만 정의하는 틀 → 데이터를 저장할 수 있는 상태는 아님
  • 구조체를 함수 밖에서 생성 시 전체 함수에서 사용할 수 있는 구조체를 선언함; 구조체를 함수 안에서 생성 시 그 함수 안에서만 사용하는 구조체를 선언함

2) 구조체 변수 선언

  • 구조체 변수 선언 방법은 함수 안에서 선언함
  • 여러 개 변수를 선언하려면 ',' (콤마 연산자)로 분리하여 선언 가능
struct student {
    int num;
    char name[10];
    double grade;
};

int main(void) {
    struct student s1;    // 구조체 변수 (s1) 선언
    struct student s2;    // 구조체 변수 (s2) 선언
    struct student s3,s4,s5;    // 구조체 변수 (s3, s4, s5) 선언
}

 

4) 구조체 정의 및 선언 (동시 실행)

  • 구조체를 정의만 하는 것과 차이: 끝에 구조체명 (s1) 추가
struct student {
    int num;
    char name[10];
    double grade;
} s1;	// s1 추가

5) 구조체 변수 초기화

  • 구조체가 앞서 선언되어있을 경우만 사용 
  • 구조체 멤버가 선언된 순서대로 넣고 각 멤버의 자료형에 맞게 넣어야 됨
  • NOTE: 중간에 어느 한 멤버 생략 불가능
struct student s1 = {24, "Kim", 4.3};

6) 구조체 변수 선언 및 초기화 (동시 실행)

  • '{ }' (중괄호) 안에 '.' (멤버 연산자)와 멤버이름을 적고 값 할당
  • 이 방법 사용 시 원하는 멤버 변수만 초기화 가능
  • 멤버 변수가 정의된 순서와 초기화 순서는 상관 없으며, 초기화하지 않은 멤버 변수는 0으로 초기화됨
struct 구조체이름 구조체변수이름 = { .멤버이름1 = 값1, .멤버이름2 = 값2 ... }

struct student s1 = { .num = 21012345, .name = "김세종", .grade = 3.5 };

7) 구조체 참조

  • 구조체변수.구조체멤버 사용하여 참조 가능
  • NOTE: char name[10]은 문자 배열이라서 strcpy_s() 함수 사용 필수
s1.grade = 3.0;
strcpy_s(s1.name, 20, "Kim");
// strcpy(s1.name, "Kim");  // strcpy()는 strcpy_s()의 옛 버전
  • s1: 구조체 변수
  • grade, name: 구조체 멤버

8) 이름 없는 구조체

  • 이름 없어도 되나 이름이 없을 경우 변수 생성 불가, 필요한 구조체 변수를 정의와 함께 선언 필수
struct {
    int num;
    char name[10];
    double grade;
} s1, s2, s3;
  • 여기서 s4를 함수 안에서 초기화하려면 위와 같은 구조체를 한 번 더 선언해야 함

9) 구조체를 멤버로 가지는 구조체

struct date{
    int year;
    int month;
    int day;
};

struct student{
    int num;
    char name[10];
    struct date dob; // date of birth
    double grade;
};
  • struct date dob는 구조체 안에 구조체가 들어가 있는 구조
  • 이렇게 선언된 구조체를 초기화 하려면 다음과 같이 초기화 가능
s1.dob.year = 1983;
s1.dob.month = 03;
s1.dob.day = 29;
  •  s1에 있는 dob 안에 있는 year, month, day를 초기화 할 때 이렇게 씀 

02 구조체 활용

1. 구조체와 배열

1) 구조체 안에 배열 생성

  • 구조체 안에 배열 선언 가능
  • 구조체를 여러 개 이어 만든 배열 생성 가능
struct  구조체이름 변수이름[크기];
struct student list[100]; // 구조체 배열 100개 생성
  • 예시
#include <stdio.h>
#include <string.h>

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

int main(void) {
    struct student arr[3]; // 크기가 3인 구조체 배열 생성
    int i;

    // 인덱스로 요소에 접근 후 .으로 멤버에 접근
    arr[0].num = 21012345;
    strcpy_s(arr[0].name, 20, "Kim");
    arr[0].grade = 3.5;

    arr[1].num = 21012346;
    strcpy_s(arr[1].name, 20, "Park");
    arr[1].grade = 4.0;

    arr[2].num = 21012347;
    strcpy_s(arr[2].name, 20, "Lee");
    arr[2].grade = 2.7;

    for (i = 0; i < 3; i++)
    {
        printf("학번: %d\n", arr[i].num);
        printf("이름: %s\n", arr[i].name);
        printf("학점: %.2f\n", arr[i].grade);
        printf("\n");
    }
    return 0;
}
  • output

2) 배열 안에 있는 구조체 초기화

list[2].num = 24;
strcpy(list[2].name = "홍길동"); // 문자열 저장은 항상 strcpy()를 이용
list[2].grade = 4.3;

3) 구조체 배열 초기화

struct 구조체이름 변수이름[크기] = {
    {값1, 값2, 값3}
    {값4, 값5, 값6}
    {값7, 값8, 값9}
};

// 예시
struct student list[3] = {
    {1, "Park", 3.42}
    {2, "Kim", 4.31}
    {3, "Lee", 2.98}
};

4) 구조체 배열 선언 및 초기화 (동시 실행)

struct 구조체이름 변수이름[크기] = {
    {.멤버이름1 = 값1, .멤버이름2 = 값2 ...},
    {.멤버이름1 = 값3, .멤버이름2 = 값4 ...},
    {.멤버이름1 = 값5, .멤버이름2 = 값6 ...},
    ...
};

// 예시
struct student arr[3] = {
    { .num = 21012345, .name = "Kwon", .grade = 3.5 },
    { .num = 21012346, .name = "Hwang", .grade = 2.5 },
    { .num = 21012347, .name = "Hong", .grade =  4.5 }
};

 

2. 구조체와 포인터

  • 포인터: 메모리의 주소값을 저장하는 변수
  • 구조체 포인터: 구조체의 메모리 주소값을 저장하는 변수 (구조체를 가리키는 포인터)
  • 구조체 포인터 변수도 일반 포인터 변수와 동일하게 변수이름 앞에 *를 붙여 선언
struct 구조체이름 * 구조체포인터;

1) 구조체 포인터 사용하여 구조체 멤버에 접근 방법 2가지

  1. * (참조 연산자) 사용
  2. -> (화살펴 연산자) 사용

1-1) * 사용

  • NOTE: * (참조 연산자)는 . (멤버 연산자)보다 연산자 우선순위가 낮으므로 반듣시 ( ) (괄호)를 사용함
(*구조체포인터).멤버변수이름
  • 예시
#include <stdio.h>

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

main(){
    struct student s1 = {21012345, "Ahn", 4.0};
    struct student * p = &s1;
    
    printf("학번: %d\n", (*p).num);
    printf("이름: %s\n", (*p).name);
    printf("학점: %.2f\n", (*p).grade);
}
  • output

1-2) -> 사용

구조체포인터 -> 멤버변수이름
  • 예시
#include <stdio.h>

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

main() {
    struct student s1 = {501, "Shin", 3.5};
    struct student * p = &s1;
    
    printf("학번: %d\n", p -> num);
    printf("이름: %s\n", p -> name);
    printf("학점: %.2f\n", p -> grade);
}
  • output

 

2) 구조체에 쓰이는 포인터 2가지

  1. 구조체를 가리키는 포인터 (i.e., 구조체 포인터)
  2. 포인터를 멤버로 가지는 구조체

2-1) 구조체를 가리키는 포인터

  • 변수에도 포인터 생성 가능하듯이 구조체에도 포인터 생성 가능
#include <stdio.h>

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

main() {
	struct student s = { 24, "Kim", 4.3 };
	struct student* p;
	p = &s;
	printf("학번 = %d 이름 = %s 학점 = %f \n", (*p).num, (*p).name, (*p).grade);
	// 포인터를 통해 구조체에 접근함. (*p)가 구조체가 됨
}

 

  • output

  • *p가 구조체 전체를 가리키고 '.' (점) 뒤에 멤버를 붙여 사용
  • 구조체 변수와 구조체 포인터의 조합:
구조체 수식 의미
(*p).number 포인터 p가 가리키는 구조체의 멤버 number
p->number (*p).number와 동일
*p.number 연산자의 우선순위에 의해 *(p.number)로 해석함
"구조체 p의 멤버 number가 가르키는 것"으로 해석함
이 때 number는 반드시 포인터여야 함, 아니면 error 발생
*p->number 연산자의 우선순위에 의해 *(p->number)로 해석함
"p가 가르키는 구조체의 멤버 number가 가르키는내용"으로 해석함
이 때 number는 반드시 포인터여야 함, 아니면 error 발생

2-2) 포인터를 멤버로 가지는 구조체

  • 구조체 안에서도 포인터 선언 가능
struct date{
    int month;
    int day;
    int year;
};

struct student{
    int num;
    char name[20];
    double grade;
    struct date *dob; // 구조체 안에 포인터가 들어감
};

int main(void){
    struct date d = {3, 20, 1990};
    struct student s = {20190001, "Kim", 4.3};
    
    s.dob = &d; // 포인터 dob에 구조체 d의 주소를 저장
}
  • s.dob -> day 입력 시 dob에 있는 주소를 가지고 date 안에 있는 day값을 출력함

참조

  1. (자료형) https://www.geeksforgeeks.org/data-types-in-c/
  2. https://rumiamochi.tistory.com/22
  3. https://sejong-kr.libguides.com/c.php?g=942235&p=6822369 
  4.  
  5.