4 분 소요

구조체 선언방식의 차이

struct account와 ‘typdef struct {…} account’는 구조체를 정의하는 두가지 방식이다. 여기까지만 차이가 존재하면 그냥 줄중에 보기 편한거 하나만 고정적으로 사용하면 되는데, 이러면 나중에 main안에서 선언하는 방식도 달라지기 떄문에, 제대로 짚고 넘어가는게 좋을 것 같아 정리한다.

1.struct account:

struct account
{
    char name[12];
    int actnum;
    double balance;
};

이 방식은 구조체를 정의하고 이름이 struct account 인 타입을 만듭니다. 즉, struct키워드를 사용하여 구조체의 명시되어 있습니다.

맨 윗줄에 이렇게 struct라는 키워드를 사용하게 되면 선언할때 이렇게 됩니다.

struct account myAccount;

즉 typdef가 안오면 위와 같이 변수룰 선언해준다 보시면 됩니다.

2.typedef struct {...} accout:

여기서는 특이하게 구조체명을 맨 뒤에 넣어줍니다.

typedef struct
{
    char name[12];
    int actnum;
    double balance;
} account;

이렇게 하면 구조체의 이름을 직접 사용하지 않고 새로운 타입으로 선언할 수 있습니다(무슨소리지??ㄷㄷ??)

account myAccount;

즉, typedef를 사용하면 ‘struct’ 키워드 없이도 구조체 타입을 직접 사용할 수 있게 됩니다.

둘 다 기능적으로 동일하며, 어떤 것을 사용할지는 코딩스타일이나 프로젝트 규칙에 따라 다를 수 있습니다. typedef를 사용하면 코드가 더 간결해지고 편리해질 수 있지만, 코드의 가독성이나 유지보수성을 고려하여 선택해야 합니다.

오픈소스들을 보면 후자의 방식을 많이 사용하는 것 같다.나도 이제부터 저 typedef를 넣는 방식에 익숙해지려고 한다.


직접주소연산자(&)는 문자열을 지정해줄때 이외에는 모두 사용한다.

다음예시는 계좌정보를 입력하여 구조체에 저장하고, 구조체로부터 값을 출력하는 코드이다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

//은행계좌 구조체
typedef struct {
    char name[12];
    int actnum;
    double balance;
}account;

int main() {
    account accounts;

    printf("계좌주인이름: ");
    scanf("%s", accounts.name);
    printf("계좌 번호: ");
    scanf("%d", &accounts.actnum);
    printf("계좌 잔고: ");
    scanf("%lf", &accounts.balance);


    printf("[%s]-[%10d]-[%10.2f]", accounts.name, accounts.actnum, accounts.balance);
    return 0;
}

output>

계좌주인이름: 윤성웅 계좌 번호: 1000021 계좌 잔고: 100000 [윤성웅]-[ 1000021]-[ 100000.00]

다음 의문이 생겼다. “왜 문자열에 입력할때에는 따로 직접주소연산자를 지정해주지 않는가?”

scanf()에서 %s와 %d/%f형식지정자를 사용하는 경우에 있어서 차이는 해당 자료형의 데이터 처리 방식의 차이이다.

1.문자열 입력(%s):

문자열은 문자형배열에 저장된다. 즉 문자열을 입력받으면 자동으로 배열에 표현이 되는데, 이때 배열의 이름 자체가 해당 배열의 주소를 나타내게 된다.

에를 들어 array[2]={0, };이 있을때, array를 출력하면 이 배열의 주소가 출력이 되고, array[0]이나 array[1]처럼 뒤에 대괄호와 인덱스값까지 나와야 비로소 그 배열 속 원소의 값이 추출된다.

그래서 배열 이름만 덩그러니 등장하면 그 배열에 있는 값들이 떠올라야 하는게 아니라, 그 배열의 주소가 떠올라아 한다.

2.정수 입력(%d):

정수입력에 대해서는 이전처럼 변수이름이 왔을때 그 변수에 저장된 값이 떠오르면 된다.

:paperclip: 크기를 출력할때는 %zu를 쓰자!

다음 코드는 구조체의 크기를 계산하는 예시코드이다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

typedef struct {
    char name[12];
    int actnum;
    double balance;
} account;

int main() {
    account me = {"홍길동", 1001, 300000};
    printf("구조체 크기: %zu\n", sizeof(me));
    //12(char) + 4(int) + 8(double) = 24(byte)
    return 0;
}

구조체의 크기는 그 안에 필드의 총합이다(참고로 공용체의 크기는 그 안에 있는 가장 큰 필드의 자료형의 크기이다.)

따라서 24바이트가 출력된다고 예상해볼 수 있다.

구조체 변수의 대입과 동등비교

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

//학생을 위한 구조체
typedef struct
{
    int snum;
    char* dept;
    char name[12];
}student;

int main(void)
{
	
	student hong = { 202200001, "컴퓨터공학과", "홍길동" };
	student na = { 202200002 };
	student you = { 202200003 };

	//학생이름 입력
	scanf("%s", na.name);
	na.dept = "컴퓨터정보공학과";
	you.dept = "기계공학과";
	//memcpy(you.name, "홍길동", 7);
	//strcpy(you.name, "홍길동");
	strcpy_s(you.name, 7, "홍길동");

	printf("[%d, %s, %s]\n", hong.snum, hong.dept, hong.name);
	printf("[%d, %s, %s]\n", na.snum, na.dept, na.name);
	printf("[%d, %s, %s]\n\n", you.snum, you.dept, you.name);

	student one=you;
	if (one.snum == you.snum) 
		printf("학번이 %d로 동일합니다.\n", one.snum);
	
	if (one.snum == you.snum && !strcmp(one.name, you.name) && !strcmp(one.dept, you.dept))
		printf("내용이 같은 구조체입니다.\n");

}

output>

김현식 [202200001, 컴퓨터공학과, 홍길동] [202200002, 컴퓨터정보공학과, 김현식] [202200003, 기계공학과, 홍길동]

학번이 202200003로 동일합니다. 내용이 같은 구조체입니다.

구조체에서의 문자열의 입출력

이번에는 구조체에서의 문자열의 입출력에 대해 좀더 알아 보겠습니다.

학교번호,학교명,학교위치,총장을 받는 School이라는 구조체를 만들어보겠습니다.

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

typedef struct {
	int schoolNum;
	char schoolName[12];
	char schoolLoc[20];
	char president[12];
} School;

int main(void)
{
	School knu = { 101, "", "", "" };
	strcpy(knu.schoolName, "경북대학교");
	strcpy(knu.schoolLoc, "대구");
	strcpy(knu.president, "홍원화");

	School pnu = { 102, "", "", "" };
	strcpy(pnu.schoolName, "부산대학교");
	strcpy(pnu.schoolLoc, "부산");
	strcpy(pnu.president, "차정인");

	printf("학교번호: %d, 학교명: %s, 학교위치: %s, 총장:%s\n", knu.schoolNum, knu.schoolName, knu.schoolLoc, knu.president);
	printf("학교번호: %d, 학교명: %s, 학교위치: %s, 총장:%s\n", pnu.schoolNum, pnu.schoolName, pnu.schoolLoc, pnu.president);
}

여기서 구조체를 선언하는 부분을 보면, 모두 문자열을 입력받기 위해 문자형배열 방식을 사용하였습니다. 그래서 이에 맞춰, strcpy()를 사용해주었습니다.

이번에는 다른방식으로 구조체를 선언해보겠습니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
typedef struct {
	int schoolNum;
	char* schoolName;
	char* schoolLoc;
	char* president;
} School;

int main(void)
{
	School knu = { 101, NULL, NULL, NULL };
	strcpy(knu.schoolName, "경북대학교");
	strcpy(knu.schoolLoc, "대구");
	strcpy(knu.president, "홍원화");

	School pnu = { 102, NULL, NULL, NULL };
	strcpy(pnu.schoolName, "부산대학교");
	strcpy(pnu.schoolLoc, "부산");
	strcpy(pnu.president, "차정인");

	printf("학교번호: %d, 학교명: %s, 학교위치: %s, 총장:%s\n", knu.schoolNum, knu.schoolName, knu.schoolLoc, knu.president);
	printf("학교번호: %d, 학교명: %s, 학교위치: %s, 총장:%s\n", pnu.schoolNum, pnu.schoolName, pnu.schoolLoc, pnu.president);
}

여기서는 문자열 포인터를 선언해주었습니다. 하지만 이럴경우에 기본적으로 포인터가 널로 초기화되어 있어, 특정 값을 가져올수가 없습니다,

그래서 바로 복사하는 방식은 불가능하고, 값이 들어갈 공간을 확보해주고, 거기다가 값을 넣어주어야 합니다.

그러면 키보드에서 입력받은 문자열 값이 입력버퍼에 들어가고 입력버퍼에서 동적할당 된 메모리에 값이 복사되어 들어갑니다.

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

typedef struct {
	int schoolNum;
	char* schoolName;
	char* schoolLoc;
	char* president;
} School;

int main(void)
{
	School knu = { 101, NULL, NULL, NULL };
	knu.schoolName = (char*)malloc(strlen("경북대학교") + 1);
	strcpy(knu.schoolName, "경북대학교");

	knu.schoolLoc = (char*)malloc(strlen("대구") + 1);
	strcpy(knu.schoolLoc, "대구");

	knu.president = (char*)malloc(strlen("홍원화") + 1);
	strcpy(knu.president, "홍원화");


	School pnu = { 102, NULL, NULL, NULL };
	pnu.schoolName = (char*)malloc(strlen("부산대학교") + 1);
	strcpy(pnu.schoolName, "부산대학교");

	pnu.schoolLoc = (char*)malloc(strlen("부산") + 1);
	strcpy(pnu.schoolLoc, "부산");

	pnu.president = (char*)malloc(strlen("차정인") + 1);
	strcpy(pnu.president, "차정인");
	

	printf("학교번호: %d, 학교명: %s, 학교위치: %s, 총장:%s\n", knu.schoolNum, knu.schoolName, knu.schoolLoc, knu.president);
	printf("학교번호: %d, 학교명: %s, 학교위치: %s, 총장:%s\n", pnu.schoolNum, pnu.schoolName, pnu.schoolLoc, pnu.president);

	// 동적으로 할당된 메모리 해제
	free(knu.schoolName);
	free(knu.schoolLoc);
	free(knu.president);

	free(pnu.schoolName);
	free(pnu.schoolLoc);
	free(pnu.president);

	return 0;
}

여기서 주목할 점은 strcpy()를 쓰기전에 사용한 malloc()함수입니다. 이를 통해 동적으로 메모리를 확보해서 사용할수가 있겠습니다.

저 함수를 사용하려면 사전에 #include <stdlib.h>가 필요합니다.

근데 말이죠! 위와 같이 출력하는 것이 목적이라면 다음과 같이 간소화시켜서 구현해줄수도 있습니다.

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

typedef struct {
	int schoolNum;
	char* schoolName;
	char* schoolLoc;
	char* president;
} School;

int main(void)
{
	School knu = { 101, NULL, NULL, NULL };
	knu.schoolName = "경북대학교";
	knu.schoolLoc = "대구";
	knu.president = "홍원화";

	School pnu = { 102, NULL, NULL, NULL };
	pnu.schoolName = "부산대학교";
	pnu.schoolLoc = "부산";
	pnu.president = "차정인";

	printf("학교번호: %d, 학교명: %s, 학교위치: %s, 총장:%s\n", knu.schoolNum, knu.schoolName, knu.schoolLoc, knu.president);
	printf("학교번호: %d, 학교명: %s, 학교위치: %s, 총장:%s\n", pnu.schoolNum, pnu.schoolName, pnu.schoolLoc, pnu.president);

	return 0;
}

구조체에서 포인터 활용하기

다음은 사전에 입력된 도서정보를 출력하는 프로그램입니다.

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

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

//도서정보 구조체(제목, 작가, 년도, 가격)
typedef struct {
    char title[100];
    char author[20];
    date date;
    float price; //dollar
} Book;

//도서정보를 출력하는 함수
void printBookInfo(const Book* book) {
    printf("Title: %s\n", book->title);
    printf("Author: %s\n", book->author);
    printf("Year: %d\n", book->date.year);
    printf("Price: %.1f$ \n", book->price);
}

int main(void)
{
    //Book이라는 구조체를 가져와서 book1,book2라는 변수 만들기
    Book book1 = { "경북대를 향해서", "홍원화",{2002,3 }, 2.5 };

    printBookInfo(&book1);
    return 0;
}

댓글남기기