c언어

[C언어] 사용자 정의형 - 구조체

kidmillionaire1998 2022. 10. 24. 17:14

구조체  

-서로 다른 형의 변수들을 하나로 묶어주는 방법을 제공한다. 

: 이질적인 데이터 집합을 하나의 단위로 취급할 수 있게 한다. 

EX) 성적 처리 프로그램 

학생 이름과 그 학생의 점수는 한 쌍으로 다루는 것이 좋다. 

 

구조체 선언 

struct name_grade {
	char name[10]; 
	int grade; 
};

//struct : 구조체 선언을 위한 키워드 
//name_grade : 구조체 태그 이름 
//name, grade : 구조체 멤버

- 이 선언은 메모리 할당을 받는 변수를 선언한 것이 아니라

struct name_grade 형을 선언한 것이다. 

- struct name_grade는 자료형이 오는 자리에 사용할 수 있다. 

-struct name_grade 형의 크기는 단순히 구성 요소 덧셈이 아니라, sizeof 연산자를 사용해야 알 수 있다. 

 

구조체 (struct name_grade)형 변수 선언 

#include <stdio.h>
struct name_grade {
	char name[10]; 
	int grade; 
};

struct name_grade st_g1, st_g2;

- struct name_grade가 자료형이기 때문에 이 코드는 st_g1과 st_g2라는 변수를 선언하는 선언문 

- 컴파일러는 st_g1과 st_g2 변수에 메모리 할당 

 

-st_g1과 st_g2는 구조체 변수로 일반 변수처럼 다룰 수 없다. 

st_g1 = 10; // 오류

-st_g1과 st_g2의 멤버 변수는 일반 변수처럼 다룰 수 있다. 

st_g1.name // char[10]형 변수, char 형 배열 
st_g1.grade //int 형 변수

#include <stdio.h>
struct name_grade {
	char name[10]; 
	int grade; 
};

struct name_grade st_g1, st_g2;

strcpy(st_g1.name, "이순신");
st_g1.grade = 98; 
	
// st_g1.name = "이순신"; 
// st_g1.name은 char 배열형 이기 때문에, 상수 포인터이므로, 대입을 하면 안되며, strcpy를 이용해야한다.

 

[예제 프로그램] 

#include <stdio.h>

int main(void) {

	struct name_grade {
		char name[10];
		int grade;
	};

	struct name_grade st_g1, st_g2;

	strcpy(st_g1.name, "이순신");
	st_g1.grade = 98; 
	strcpy(st_g2.name, st_g1.name); 
	st_g2.grade = st_g1.grade; 
	printf("%s의 점수는 %d입니다.", st_g2.name, st_g2.grade); 
	return 0;
};

[출력 결과] 

-같은 형의 구조체 변수 간에는 배정이 가능하다. 

st_g1 = st_g2;

 

다양한 구조체 변수 선언 

구조체 형 선언과 동시에 변수 선언 

struct name_grade {
	char name[10]; 
	int grade; 
}st_g3;

struct name_grade st_g4;

 

구조체 태그가 없는 선언 

: 해당 구조체 형은 다시 사용할 수 없다. 

struct{	char name[10]; 
	int grade; 
}st_g5;

struct {
	char name[10];
	int grade;
}st_g6;


//st_g5와 st_g6는 다른 형이다. 
st_g5 = st_g6; //오류

 

typedef 이용 

구조체 형 이름은 보통 길기 때문에, typedef을 많이 사용한다. 

예시 

typedef struct name_grade name_grade; 
name_grade st_g7; 

typedef struct {
	char name[10]; 
	int grade; 
}name_grade;

name_grade st_g8, st_g9;

 

구조체의 초기화 

변수 선언문에서 배열과 유사하게 초기화 

struct name_grade {
	char name[10];
	int grade;
}; 
struct name_grade st_g10= {"둘리",80};

//멤버 순서대로 초기화가 된다. 
// st_g10.name = "둘리" 
// st_g10.grade = 80;

struct name_grade st_g11 = {"둘리"}; 

이 경우에는 

st_g11.name = "둘리"; 

st_g11.grade =0; 

이 실행된다. 

 

연산자를 사용하여 멤버를 지정하여 초기화 할 수 있다. 

struct name_grade {
	char name[10];
	int grade;
}; 
struct name_grade st_g10= {.grade = 80};

 

복합 리터럴 

구조체 멤버의 값을 배정할 때 유용하다. 

캐스트를 사용하여 형을 지정한다. 

1. 선언을 함과 동시에 초기화를 안할 경우 

struct name_grade {
	char name[10];
	int grade;
}; 

struct name_grade st_g1; //초기화를 구조체 변수 선언할 때 시행을 안 한 경우 

//일반적인 경우 : 변수 별로 저장 
strcpy(st_g1.name, "이순신"); 
st_g1.grade = 98; 

//복합 리터럴 => 중괄호 & 캐스트 연산자 통해서 한 번에 저장 가능하다. 
st_g1 = (struct name_grade){ "이순신",98 }; 
st_g1 = (struct name_grade){ .grade = 80, .name = "홍길동" };

 

구조체의 멤버로 구조체가 올 수 있다. 

struct name_grade {
	char name[10];
	int grade; 
};


struct subject {
	char name[10]; 
	struct name_grade student1; 
	struct name_grade student2;
	struct name_grade student3;
	float avg; 
};

struct subject math; 
math.student1.grade = 80;

예제 프로그램 

#include <stdio.h>
struct name_grade {
	char name[10];
	int grade; 
};

struct subject {
	char name[10]; 
	struct name_grade student1; 
	struct name_grade student2;
	struct name_grade student3;
	float avg; 
};

int main(void) {
	struct subject math = { "수학",{"하나",90},{"둘",44},{"셋",76} }; 
	math.avg =
		(math.student1.grade + math.student2.grade + math.student3.grade) / 3.0; 
	printf("%s: %d점\n", math.student1.name, math.student1.grade); 
	printf("%s: %d점\n", math.student2.name, math.student2.grade);
	printf("%s: %d점\n", math.student3.name, math.student3.grade);
	printf("%s 평균은 %.2f점입니다", math.name, math.avg); 
	return 0; 
}

[출력결과] 

구조체 포인터 

#include <stdio.h>
struct name_grade {
	char name[10];
	int grade; 
};

struct name_grade st_g1; 

struct name_grade* st_gp; 
st_gp = &st_g1; 

(*st_gp).grade = 88;

//오류 코드들 
//st_gp.grade = 88; //구조체 포인터는 역연산자 붙여야함
//*st_gp.grade = 88; //역 연산자를 붙일 때는 괄호 붙여야 연산자 우선 순위에 따른 오류 방지
//*st_gp.grade = *(st_gp.grade)

 

멤버 접근 연산자 -> 

구조체 포인터를 통해 멤버를 접근할 때 사용한다. 

#include <stdio.h>
struct name_grade {
	char name[10];
	int grade;
};

struct name_grade st_g1;

struct name_grade* st_gp;

st_gp = &st_g1;

strcpy(st_gp->name, "하니"); 
st_gp->grade = 88;

[예제 프로그램] 

구조체 포인터를 통해 

1. -> 연산자 사용 

2. 역연산자 사용 => (*st_gp).grade  

#include <stdio.h>
typedef struct name_grade {
	char name[10];
	int grade;
}name_grade;

int main(void) {
	name_grade st, * st_gp = &st; 
	st = (name_grade){"이순신",98};
	printf("%s의 점수는 %d점 입니다.\n", st_gp->name, (*st_gp).grade); 
	return 0; 
}

구조체 배열 

일반 배열과 같은 방법으로 선언하고 사용한다. 

 

구조체 배열 초기화 

#include <stdio.h>
struct name_grade {
	char name[10];
	int grade;
};

struct name_grade st_a[5] = { {"하나",77},{"둘",87},{"셋",65},{"넷",90},{"다섯",98}};

struct name_grade st_a[5] = { [1] = {"둘",87} }; 
struct name_grade st_a[5] = {[1].grade=87, [1].name="둘"}

[예제 프로그램] 

#include <stdio.h>
#define N 5 
typedef struct name_grade{
	char name[10];
	int grade;
}name_grade;

int main(void) {

	int sum = 0, i; 
	float avg = 0.0;
	name_grade st_a[N] = { {"하나",77},{"둘",87},{"셋",65},{"넷",90},{"다섯",98} };
	printf(" 이름  점수 \n"); 
	for (i = 0; i < N; i++) {
		printf("%-10s	%3d\n", st_a[i].name, st_a[i].grade); 
		sum += st_a[i].grade; 
	}
	avg = (float)sum / N; 
	printf("성적 평균은 %.2f 점입니다\n.", avg); 
	return 0; 
}

구조체와 함수 

-구조체는 함수의 인자로써 함수에 전달될 수 있고 함수로 부터 리턴될 수도 있다. 

-함수의 인자로서 구조체가 전달될 때 구조체는 값으로 전달된다. 

[예제 프로그램 1] 

구조체 전달, return 값 void 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
typedef struct grade {
	int grade[3];
	char p_f[3]; 
	int sum; 
	float avg; 
}grade; 

void grade_proc(grade st) {
	st.sum = st.grade[0] + st.grade[1] + st.grade[2]; 
	st.avg = st.sum / 3.0; 
	st.p_f[0] = st.grade[0] < 60 ? 'f' : 'p'; 
	st.p_f[1] = st.grade[1] < 60 ? 'f' : 'p'; 
	st.p_f[2] = st.grade[1] < 60 ? 'f' : 'p';
}
int main(void) {
	grade st = { {0},{0},-1,-1.0 }; 

	printf("성적을 입력하세요.:"); 
	scanf("%d%d%d", &st.grade[0], &st.grade[1], &st.grade[2]); 

	grade_proc(st); 

	printf("국어 :%d (%c) \n", st.grade[0], st.p_f[0]); 
	printf("수학 :%d (%c) \n", st.grade[1], st.p_f[1]);
	printf("영어 :% d(%c) \n", st.grade[2], st.p_f[2]);
	printf("총점 : %d\n", st.sum); 
	printf("평균: %.2f", st.avg); 
}

main 함수에서 저장한 st.grade[3]은 출력이 제대로 되지만, 

값만 복사되서 함수에 전달되었고, return 받지 못했기 때문에 제대로 출력이 안된다(초기값) 

 

[예제 프로그램2] 

: return 값 grade형으로 반환 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
typedef struct grade {
	int grade[3];
	char p_f[3]; 
	int sum; 
	float avg; 
}grade; 

grade grade_proc(grade st) {
	st.sum = st.grade[0] + st.grade[1] + st.grade[2]; 
	st.avg = st.sum / 3.0; 
	st.p_f[0] = st.grade[0] < 60 ? 'f' : 'p'; 
	st.p_f[1] = st.grade[1] < 60 ? 'f' : 'p'; 
	st.p_f[2] = st.grade[1] < 60 ? 'f' : 'p';
	return st; 
}
int main(void) {
	grade st = { {0},{0},-1,-1.0 }; 

	printf("성적을 입력하세요.:"); 
	scanf("%d%d%d", &st.grade[0], &st.grade[1], &st.grade[2]); 

	st=grade_proc(st); 

	printf("국어 :%d (%c) \n", st.grade[0], st.p_f[0]); 
	printf("수학 :%d (%c) \n", st.grade[1], st.p_f[1]);
	printf("영어 :% d(%c) \n", st.grade[2], st.p_f[2]);
	printf("총점 : %d\n", st.sum); 
	printf("평균: %.2f", st.avg); 
}

=> 구조체가 많은 멤버를 가지거나 큰 배열을 멤버로 가질 경우, 함수의 인자를 구조체를 전달하는 것은 상대적으로 비효율적이다

: 대부분 응용 프로그램에서 함수의 인자로 구조체의 주소를 사용한다. 

 

[예제 프로그램 3] 

구조체의 주소를 넘겨준다. 

실행 성공 여부에 따라 int 값을 다르게 return 해준다. 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
typedef struct grade {
	int grade[3];
	char p_f[3]; 
	int sum; 
	float avg; 
}grade; 

int grade_proc(grade* st) {
	if (st == NULL) {
		printf("오류");
		return -1;
	}
	st->sum = st->grade[0] + st->grade[1] + st->grade[2]; 
	st->avg = st->sum / 3.0; 
	st->p_f[0] = st->grade[0] < 60 ? 'f' : 'p'; 
	st->p_f[1] = st->grade[1] < 60 ? 'f' : 'p'; 
	st->p_f[2] = st->grade[1] < 60 ? 'f' : 'p';
	return 0; 
}
int main(void) {
	grade st = { {0},{0},-1,-1.0 }; 

	printf("성적을 입력하세요.:"); 
	scanf("%d%d%d", &st.grade[0], &st.grade[1], &st.grade[2]); 

	if (grade_proc(&st))
		return 1; 

	printf("국어 :%d (%c) \n", st.grade[0], st.p_f[0]); 
	printf("수학 :%d (%c) \n", st.grade[1], st.p_f[1]);
	printf("영어 :% d(%c) \n", st.grade[2], st.p_f[2]);
	printf("총점 : %d\n", st.sum); 
	printf("평균: %.2f", st.avg); 
}

'c언어' 카테고리의 다른 글

[C언어] 전처리기  (0) 2022.10.23
[C언어] 비트 수준 접근  (0) 2022.10.23
[C언어] 공용체(union) / 열거형  (0) 2022.10.12
[C언어] 동적 메모리 할당  (0) 2022.09.07
[C언어] 포인터와 배열  (0) 2022.09.07