새소식

프로그래밍/C

구조체

  • -

1. 구조체란?

하나 이상의 변수를 묶어서 새로운 자료형을 정의하는 도구이다. 

 

구조체를 언제 사용할까??

 

char name1[20];
int age1;

char name2[20];
int age2;

char name3[20];
int age3;

....

 

학생 10명의 이름과 나이를 저장하려면 name1,name2처럼 변수 이름을 바꿔서 계속 추가해야 한다. 

이는 상당히 복잡하고 비효율적인데 이럴 때 구조체를 사용하면 훨씬 간단하고 효율적으로 처리할 수 있다.

 

 

struct 구조체 이름{
    자료형 멤버 이름;
};

 

기본 형태는 위와 같으며 구조체는 struct 키워드로 정의한다.

 

 

struct Person {
    char name[20];
    int age;
};

 

위의 예시를 구조체를 이용해 변경하면 위와 같으며, Person은 구조체 이름이고 name, age는 구조체 멤버이다.

Person 구조체를 사용하여 변수를 만들어내면 인적 정보를 쉽게 추가할 수 있고 구조체 자체도 배열로 만들면 사람 수가

많아도 인적 정보를 체계적으로 관리할 수 있다.

 

 

struct Person {
    char name[20];
    int age;  
};

int main()
{
    struct Pesron p1;	// struct 구조체이름 변수이름
    
    strcpy(p1.name, "홍길동");
    p1.age = 30;
    ....
}

 

main함수에서 구조체를 사용하기 위해서는 struct 구조체이름 변수이름 형식으로 선언하면 된다.

구조체 멤버에 접근하기 위해서는 . (점)을 사용하면 된다.

 

 

#define _CRT_SECURE_NO_WARNINGS    // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>    // strcpy 함수가 선언된 헤더 파일

struct Person {   // 구조체 정의
    char name[20];        // 구조체 멤버 1
    int age;              // 구조체 멤버 2
};

int main()
{
    struct Person p1;     // 구조체 변수 선언

    // 점으로 구조체 멤버에 접근하여 값 할당
    strcpy(p1.name, "홍길동");
    p1.age = 30;

    // 점으로 구조체 멤버에 접근하여 값 출력
    printf("이름: %s\n", p1.name);       // 이름: 홍길동
    printf("나이: %d\n", p1.age);        // 나이: 30

    return 0;
}

 

코드를 실행하면 위 그림과 같이 출력된다. 

 


2. typedef로 struct 키워드 없이 구조체 선언하기

구조체를 선언할 때 마다 struct 키워드를 붙이는 것은 귀찮은 작업이다. typedef를 통해 구조체를 정의하면서 별칭(alias)를 지정하면 struct 키워드를 붙이지 않아도 된다.

 

#define _CRT_SECURE_NO_WARNINGS    // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>    // strcpy 함수가 선언된 헤더 파일

typedef struct _Person {   // 구조체 이름은 _Pesron
    char name[20];        // 구조체 멤버 1
    int age;              // 구조체 멤버 2
} Person;                 // typedef를 사용해 구조체 별칭을 Person으로 정의

int main()
{
    Person p1;     // 구조체 별칭 Person으로 변수 선언

    // 점으로 구조체 멤버에 접근하여 값 할당
    strcpy(p1.name, "홍길동");
    p1.age = 30;

    // 점으로 구조체 멤버에 접근하여 값 출력
    printf("이름: %s\n", p1.name);       // 이름: 홍길동
    printf("나이: %d\n", p1.age);        // 나이: 30

    return 0;
}

 

위 코드가 typedef를 통해 구조체 별칭을 만든 예시이다. 구조체 이름(_Pesron)과 별칭(Person)을 구분하기 위해 _를 사용했다. 이전과 달리 변수 p1을 선언할 때 struct 키워드를 붙이지 않아도 된다.

 

 


3. 구조체 포인터 사용하기

구조체는 멤버 변수가 여러 개 들어있어서 크기가 큰 편이다. 그래서 구조체 변수를 일일이 선언해서 사용하는 것 보다 포인터에 메모리를 할당해서 사용하는게 효율적이다.

 

struct 구조체이름 * 포인터이름 = malloc(sizeof(struct 구조체이름));

 

다른 자료형과 마찬가지로 구조체도 포인터를 선언할 수 있으며 구조체 포인터에는 malloc 함수를 사용하여 동적 메모리를 할당할 수 있다.

 

#define _CRT_SECURE_NO_WARNINGS    // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>    // strcpy 함수가 선언된 헤더 파일
#include <stdlib.h>    // malloc, free 함수가 선언된 헤더 파일

struct Person {    // 구조체 정의
    char name[20];        // 구조체 멤버 1
    int age;              // 구조체 멤버 2
};

int main()
{
    struct Person *p1 = malloc(sizeof(struct Person));    // 구조체 포인터 선언, 메모리 할당

    // 화살표 연산자로 구조체 멤버에 접근하여 값 할당
    strcpy(p1->name, "홍길동");
    p1->age = 30;

    // 화살표 연산자로 구조체 멤버에 접근하여 값 출력
    printf("이름: %s\n", p1->name);       // 홍길동
    printf("나이: %d\n", p1->age);        // 30

    free(p1);    // 동적 메모리 해제

    return 0;
}

 

먼저 struct Person *p1과 같이 struct 키워드와 구조체 이름을 사용하여 구조체 포인터를 선언한다.

그 후, malloc 함수로 메모리를 할당할 때 크기를 알아야 하므로 sizeof(struct Person)과 같이 구조체 크기를 구하여 넣어준다.

 

 

    strcpy(p1->name, "홍길동");
    p1->age = 30;

 

지금까지는 . (점)을 사용해서 멤버에 접근했지만 구조체 포인터의 멤버에 접근할 때는 -> (화살표 연산자)를 사용한다.  

p1->name 등의 문자열 멤버는 = (할당 연산자)로 지정할 수 없으므로 strcpy 함수를 사용한다.

 

 

    free(p1);    // 동적 메모리 해제

 

마지막으로 free 함수를 사용하여 할당한 메모리를 해제한다. 구조체에 메모리를 할당할 때는 malloc -> 사용 -> free 패턴이라는 것을 기억하면 된다.

 


4. 참고

 

4.1) 구조체 포인터에서 . (점)으로 멤버에 접근하기

구조체 포인터에서 멤버에 접근하려면 -> (화살표 연산자)를 사용해야 한다고 했다. 그러나 괄호와 역참조를 사용하면

. (점)으로 멤버에 접근할 수 있다.

 

p1->age;		// 화살표 연산자로 멤버에 접근
(*p1).age;		// 구좇 포인터를 역참조한 뒤 . (점)으로 멤버에 접근

 

 

4.2) 구조체의 멤버가 포인터일 때 역참조하기

구조체의 멤버가 포인터 일 때 역참조를 하려면 맨 앞에 *를 붙이면 된다. 이때 구조체 변수 앞에 *가 붙어있더라도 멤버의 역참조이지 구조체 변수의 역참조가 아니다.

 

*구조체변수.멤버

*구조체포인터->멤버

 

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

struct Data {
    char c1;
    int *numPtr;    // 포인터
};

int main()
{
    int num1 = 10;
    struct Data d1;    // 구조체 변수
    struct Data *d2 = malloc(sizeof(struct Data));    // 구조체 포인터에 메모리 할당

    d1.numPtr = &num1;
    d2->numPtr = &num1;

    printf("%d\n", *d1.numPtr);     // 10: 구조체의 멤버를 역참조
    printf("%d\n", *d2->numPtr);    // 10: 구조체 포인터의 멤버를 역참조

    d2->c1 = 'a';
    printf("%c\n", (*d2).c1);      //  a: 구조체 포인터를 역참조하여 c1에 접근
                                   // d2->c1과 같음
    printf("%d\n", *(*d2).numPtr); // 10: 구조체 포인터를 역참조하여 numPtr에 접근한 뒤 다시 역참조
                                   // *d2->numPtr과 같음

    free(d2);

    return 0;
}

 

구조체 변수 d1의 멤버 numPtr을 역참조 하는 방법과 구조체 포인터 d2의 멤버 numPtr을 역참조하는 방법을 표현하면 위 그림과 같다.

 

 

(*구조체포인터).멤버

*(*구조체포인터).멤버

역참조한 것을 괄호로 묶으면 구조체 변수를 역참조한 뒤 멤버에 접근한다는 뜻이 된다. *(*d2).numPtr처럼 구조체 포인터를 역참조하여 numPtr에 접근한 뒤 다시 역참조할 수 있다.

 

    d2->c1 = 'a';
    printf("%c\n", (*d2).c1);      //  a: 구조체 포인터를 역참조하여 c1에 접근
                                   // d2->c1과 같음
    printf("%d\n", *(*d2).numPtr); // 10: 구조체 포인터를 역참조하여 numPtr에 접근한 뒤 다시 역참조
                                   // *d2->numPtr과 같음

(*d2).c1은 d2->c1과 같고, *(*d2).numPtr은 *d2->numPtr과 같다. 즉, 구조체 포인터를 역참조한 뒤 괄호로 묶으면 -> 연산자에서 . 연산자를 사용하게 되므로 포인터가 일반 변수로 바뀐다는 뜻이다. (역참조의 원리와 동일)

 

 


5. 참고문헌

1) https://dojang.io/mod/page/view.php?id=418

2) https://m.blog.naver.com/PostView.nhn?blogId=skout123&logNo=50132236162&proxyReferer=https:%2F%2Fwww.google.com%2F

3) https://neuro.tistory.com/16

'프로그래밍 > C' 카테고리의 다른 글

Call by Value, Call by Reference 비교  (0) 2020.04.23
포인터를 쓰는 이유  (0) 2020.03.27
포인터란?  (0) 2020.03.27
아스키코드란?  (0) 2020.03.26
자료형이란?  (0) 2020.03.26
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.