티스토리 뷰

Chapter 13.

 

구조체란?

 

다양한 기본 자료형을 가진 요소들을 모아 새로운 자료형으로 만든 것이다.

 

구조체와 배열의 공통점은 여러 개가 모여 새로운 하나가 되었다는 점이다.

 

그러나 배열은 '같은 것'이 여러 개 모여 한 덩어리를 이룬 것이고, 구조체는 서로 다른(같을 수도 있음) 것들이 여러 개 모여 저혀 새로운 한덩어리를 이룬 것이라는 점에서 다르다.

 

배열은 배열을 이루고 있는 각각을 요소라고 부르고, 구조체는 멤버라고 부른다.

 

 

13.1 구조체

구조체가 식판이면 식판의 한 요소를 담당하는 그릇 하나하나는 그 식판의 멤버라고 생각하면 된다.

 

구조체나 공용체 같은 것을 사용자 정의 자료형이라고 한다.

 

 

13.1.1 구조체 선언 및 정의

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

//구조체 선언
struct USEDATA{
    int nAge;
    char szName[32];
    char szPhone[32];
};

int main(void){
    //USEDATA 구조체 변수 user 선언 및 정의
    struct USEDATA user = {0, "", ""};
    
    //구조체 멤버 접근 및 값 채우기
    user.nAge = 10;
    strcpy(user.szName, "Nemo");
    strcpy(user.szPhone, "010-1234-1234");
    
    
    //구조체 멤버 접근 및 출력
    printf("%d살, %s, %s\n", user.nAge, user.szName, user.szPhone);
    return 0;
}

※구조체를 이루고 있는 멤버들의 순서에 맞춰서 초깃값을 기술해야 한다는 점에 주의해야 한다.

 

18행의 경우 구조체 멤버 접근연산의 결과가 간접지정처럼 l-value가 될 수 있지만, 19/20행의 경우 구조체의 멤버가 배열이면 l-value가 될 수 없습니다.

 

typedef를 이용한 형 재선언

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

//구조체 선언 및 형 재선언
typedef struct USERDATA{
    int nAge;
    char szName[32];
    char szPhone[32];
} USERDATA;

int main(void){
    //형 재선언 덕분에 변수를 선언할 때 'struct'를 생략할 수 있다.
    USERDATA user = {0, "", ""};
    
    //구조체 멤버 접근 및 값 채우기
    user.nAge = 10;
    strcpy(user.szName, "Nemo");
    strcpy(user.szPhone, "010-1234-1234");
    
    //USERDATA 구조체 배열 선언 및 정의
    USERDATA userList[4] = {
        {10, "김어준", "1234"},
        {11, "아무개", "2345"},
        {16, "김나은", "3456"},
        {18, "백지헌", "4567"}
    };
    
    
    //구조체 멤버 접근 및 출력
    printf("%d살, %s, %s\n", user.nAge, user.szName, user.szPhone);
    
    for(int i =0; i < 4; i++)
        printf("%d살 \t%s\t%s\n", userList[i].nAge, userList[i].szName, userList[i].szPhone);
    return 0;
}

 

13.1.2 구조체 동적 할당

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

typedef struct USERDATA{
    int nAge;
    char szName[32];
    char szPhone[32];
} USERDATA;

int main(void){
    //USERDATA 구조체에 대한 포인터 변수 선언 및 정의
    USERDATA *pUser = NULL;
    
    //USERDATA 구조체가 저장될 수 있을 수 있는 크기의 메모리 동적 할당
    pUser = (USERDATA*)malloc(sizeof(USERDATA));
    
    //포인터 이므로 '.'이 아니라 '->'연산자로 멤버에 접근한다!
    pUser->nAge = 10;
    strcpy(pUser->szName, "Hoon");
    strcpy(pUser->szPhone, "9876");
    
    printf("%d살 \t%s\t%s\n", pUser->nAge, pUser->szName, pUser->szPhone);
    return 0;
}

pUser->nAge == (*pUser).nAge

 

하지만, *pUser.nAge라고 표기하면 간접지정 연산자보다 구조체 멤버 접근 연산자가 우선하므로, 결과적으로 'char형 l-value'가 된다.

 

13.1.3 반환자료, 매개변수 구조체

구조체도 함수의 반환 자료형이나 매개변수가 될 수 있다. 그리고 구조체 변수는 배열의 이름(주소상수)과 달리 l-value가 될 수 있다.

 

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

typedef struct USERDATA{
    int nAge;
    char szName[32];
    char szPhone[32];
} USERDATA;

//구조체 인스턴스가 아니라
//구조체에 대한 '포인터'를 매개변수로 받는다. 따라서 효율적이다.
void GetUserData(USERDATA *pUser){
    scanf("%d%*c", &pUser->nAge);
    gets_s(pUser->szName, sizeof(pUser->szName));
    gets_s(pUser->szPhone, sizeof(pUser->szPhone));
}

int main(void){
    USERDATA user = {0};
    
    //Call by reference로 변경
    GetUserData(&user);
    printf("%d살 \t%s\t%s\n", user.nAge, user.szName, user.szPhone);
    return 0;
}

 

자기 참조 구조체

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

typedef struct USERDATA{
    char szName[32];
    char szPhone[32];
    //USERDATA 구조체를 가리킬 수 있는 포인터를 멤버로 선언!
    struct USERDATA *pNext;
} USERDATA;



int main(void){
    //두 개의 USERDATA 구조체 인스턴스 선언 및 정의
    USERDATA user = {"김어준", "1234", NULL};
    USERDATA newUser = {"정봉주", "2345", NULL};
    
    //pNext 멤버를 이용해 두 인스턴스를 연결한다.
    user.pNext = &newUser;
    
    printf("%s\t%s\n", user.szName, user.szPhone);
    //pNext 멤버를 이용해 구조상 다음 인스턴스에 접근한다.
    printf("%s\t%s\n", user.pNext->szName, user.pNext->szPhone);
    return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

typedef struct USERDATA{
    char szName[32];
    char szPhone[32];
    //USERDATA 구조체를 가리킬 수 있는 포인터를 멤버로 선언!
    struct USERDATA *pNext;
} USERDATA;

int main(void){
    //배열로 USERDATA 구조체 인스턴스 넷을 선언 및 정의
    USERDATA userList[4] = {
        {"김어준", "1234", NULL},
        {"고기는", "2345", NULL},
        {"살리는", "3456", NULL},
        {"인턴은", "4567", NULL}
    };
    //연결 리스트의 첫 번째 인스턴스의 주소를 저장할 포인터
    USERDATA *pUser = NULL;
    
    //pNext 멤버를 배열의 순서상 다음 구조체 인스턴스의 주소로 정의한다.
    userList[0].pNext = &userList[1];
    userList[1].pNext = &userList[2];
    userList[2].pNext = &userList[3];
    //마지막 인스턴스는 뒤에 아무것도 없으므로 NULL로 초기화 한다.
    userList[3].pNext = NULL;
    
    //연결된 리스트의 첫 번째 인스턴스를 가리키도록 포인터를 정의한다.
    pUser = &userList[0];
    while (pUser != NULL){
        //포인터가 가리키는 인스턴스의 멤버를 출력한다.
        printf("%s, %s\n", pUser->szName, pUser->szPhone);
        
        //현재 가리키고 있는 인스턴스의 다음 인스턴스를 가리키도록
        //포인터를 다음으로 '이동' 시킨다!
        pUser = pUser->pNext;
    }
    return 0;
}

 

13.1.5 구조체 멤버 맞춤

#pragma pack 전처리기를 이용하면 특정 구조체만 멤버를 1바이트로 맞출 수 있습니다.

 

#pragma pack(push, 1)

 

...

 

#pragma pack(pop)

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함