관리 메뉴

공부 기록장 💻

[C언어] 구조체 - 구조체 변수 선언, 초기화, 구조체 포인터, 구조체 배열, 함수 본문

# Language & Tools/C

[C언어] 구조체 - 구조체 변수 선언, 초기화, 구조체 포인터, 구조체 배열, 함수

dream_for 2021. 2. 2. 16:14

 

구조체의 개념

 

- 구조체는 여러 자료형의 변수들을 하나로 묶어 새로운 자료형을 만든 것을 의미한다.

- 배열과 비슷한 개념으로 보면 된다. 다만 다양한 자료형을 하나의 단위로 묶을 수 있다는 것이 차이점

 

 

구조체의 정의, 선언, 초기화

 

구조체는 'struct' 키워드를 사용하여 정의할 수 있다.

아래 예시는 학생의 학번, 이름, 학점을 하나의 단위로 묶어 새로운 자료형으로 정의하여

student 라는 이름의 구조체를 선언한 것이다.

 

struct - 키워드

student - 구조체의 이름(태그)

int number, char name[10], grade - 구조체의 멤버

 

#include <stdio.h>

//구조체 선언
struct student {
	//구조체 멤버 3개 정의
	int number;
	char name[10];
	double grade;
};

int main(void) {
	
	struct student s1 = {20210000, "Kim", 4.5}; // 구조체 멤버 변수 선언과 초기화

	return 0;
}

 

주의할 점은, 구조체 선언은 변수 선언이 아니다 라는 점이다.

main() 함수 내에서 s1이라는 변수를 선언해야, 구조체 변수가 선언 된 것이다.

위의 예시에서 구조체 멤버 변수 s1가 선언되고, 중괄호 안에 각 구조체 멤버에 대한 값들이 초기화 되었다.

 

 

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

//구조체 선언 & 구조체 변수 선언
struct student {
	//구조체 멤버 3개 정의
	int number;
	char name[10];
	double grade;
}s1;

int main(void) {
	
	//구조체 멤버 참조
	s1.number = 20210000; 
	strcpy_s(s1.name, 10, "Kim");
	s1.grade = 4.5;
	
	return 0;
}

 

위의 예시에서는 구조체 선언과 구조체 변수의 선언이 동시에 선언되었다. 전체 소스 파일에서 이 변수를 사용할 수 있다.

이후 main() 함수 내에서는 구조체의 각 멤버 변수들이 멤버 연산자(.)를 이용해 값이 대입되었다.

이를 구조체의 멤버를 참조한다고 부른다.

멤버가 문자열인 경우엔 문자열 처리 라이브러리 함수 strcpy_s() 사용해서 문자열을 저장해준다.

 

 

 

직접 입력받아 구조체 멤버에 대입하는 경우

 

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

//구조체 선언
struct student {
	//구조체 멤버
	int number;
	char name[10];
	double grade;
}s1;

int main(void) {
	
	printf("학번: ");
	scanf_s("%d", &s1.number);
	
	printf("이름: ");
	scanf_s("%s", s1.name, 10);

	printf("학점: ");
	scanf_s("%lf", &s1.grade);

	printf("학번: %d\n이름: %s\n학점: %f\n\n", s1.number, s1.name, s1.grade);
	
	return 0;
}

 

 

 

 

구조체 변수의 대입과 비교

 

- 대입 : = 연산자를 이용해 하나의 구조체를 또다른 구조체에 대입할 수 있다.

- 비교 : 구조체 변수와 구조체 변수를 서로 비교하는 것은 불가능하다. 각 구조체 멤버마다 비교 수식을 적어주어야 함.

 

#include <stdio.h>

struct point {
	int x, y;
};

int main(void){
	
	struct point p1 = { 10,20 };
	struct point p2 = { 10,10 };
	struct point p3 = p1; // 구조체 변수 대입

	printf("p3의 좌표 : %d %d\n", p3.x, p3.y);
    
    
   	 // 구조체 변수의 비교

	if (p1.x == p2.x && p1.y == p2.y)
		printf("p1과 p2의 좌표는 같습니다.\n");
	else
		printf("p1과 p2의 좌표는 다릅니다.\n");

	return 0;
}

 

정수형 변수 x, y를 멤버로 갖는 point 구조체를 선언하였다.

구조체 변수 p1과 p2는 각각 초기화를 해주었고, p3에는 p1을 대입하였다.

p1과 p2를 비교할 때에는, 각 멤버 변수들의 값마다 비교 수식을 사용하여준다.

 

 

p3의 각 멤버 변수는 p1의 변수의 값으로 대입된 것을 확인할 수 있고,

p1과 p2의 좌표를 비교한 결과 서로 같지 않은 변수임을 확인할 수 있다.

 

 

 

구조체 내의 구조체 (구조체 중첩)

 

구조체를 다른 구조체 안에 중첩하여 포함시킬 수 있다. 

 

아래는 x, y 좌표를 나타내는 두 변수를 포함한 point 구조체가

각각 사각형의 왼쪽 상단 좌표, 오른쪽 하단 좌표를 의미하며 rect 구조체 안에 중첩되어 있는 예시이다.

각 좌표를 입력받아 면적과 둘레를 구하는 프로그램이다.

 

#include <stdio.h>

struct point {
	int x, y;
};
struct rect {
	struct point p1, p2; // 왼쪽 상단 좌표, 오른쪽 하단 좌표
};
int main(void){
	
	struct rect r; // rect 구조체 변수 r 선언
	int width, height, area, peri; // 사각형의 가로, 세로, 면적, 둘레

	printf("왼쪽 상단의 좌표: ");
	scanf_s("%d %d", &r.p1.x, &r.p1.y);

	printf("오른쪽 하단의 좌표: ");
	scanf_s("%d %d", &r.p2.x, &r.p2.y);

	width = r.p2.x - r.p1.x;
	height = r.p1.y - r.p2.y;
	area = width * height;
	peri = 2 * (width + height);

	printf("면적: %d\n둘레: %d\n", area, peri);

	return 0;
}

 

멤버 참조 연산자를 두 번 사용하여 중첩되어 있는 구조체의 멤버를 참조할 수 있다.

 

 

 

 

구조체 배열

 

선언된 구조체에 대해 여러 변수들의 데이터를 처리해야 하는 경우, 구조체 배열을 사용한다.

 

 

아래는 학번, 이름, 학점을 멤버로 갖는 student 구조체에 대하여 

여러 학생의 데이터를 관리해야 하는 경우 구조체 배열을 선언하여 사용하는 예시이다.

 

#include <stdio.h>

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

int main(void){

	struct student list[3]; // 구조체 배열 선언

	for (int i = 0;i < 3;i++) // 반복문 이용하여 각 구조체 변수의 멤버의 값을 입력받는다.
	{
		printf("학번: ");
		scanf_s("%d", &list[i].number);
		printf("이름: ");
		scanf_s("%s", list[i].name, 10);
		printf("학점: ");
		scanf_s("%lf", &list[i].grade);
	}

	for (int i = 0;i < 3;i++)
		printf("\n이름: %s, 학점: %f \n", list[i].name, list[i].grade);

	return 0;
}

 

구조체 배열을 선언할 때는 구조체 키워드, 태그, 그리고 배열의 이름과 요소 개수가 필요하다.

각 구조체 변수에 대해서는 반복문을 이용하여 데이터 값을 입력받는다.

같은 방법으로 출력하면 된다.

 

 

 

 

 

구조체와 포인터

 

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

 

구조체는 상대적으로 메모리 공간을 많이 차지하기 때문에, 함수 인수로써 전달할 때 포인터에 메모리를 할당하여 사용하는 것이 훨씬 효율적이다.

따라서 구조체를 가리키는 포인터를 선언하여, 구조체의 주소를 저장하여 활용할 수 있다.

 

	struct student s = { 1,"Kim", 4.3 }; // 구조체 배열 선언과 초기화
	struct student* p = &s; // 구조체 포인터에 s의 주소 대입

	printf("이름: %s, 학점: %f \n", p->name, p->grade);
	// printf("이름: %s, 학점: %f \n", (*p).name, (*p).grade);

 

구조체의 포인터를 선언해주고, 구조체 변수의 주소를 추출하여 대입하여 주면 된다.

포인터를 이용해 구조체의 멤버에 접근하려면 간접 참조 연산자를 이용한다. ex) (*p).변수

이를 또다른 표현으로도 사용 가능하다. 간접 멤버 연산자 -> 를 사용해주는 것이다. ex) p->변수

 

 

 

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

 

구조체는 일반적인 자료형에 대한 포인터, 혹은 구조체에 대한 포인터를 멤버로 가질 수도 있다.

 

struct student {
	int number;
	char name[10];
	double grade;
	struct student* link; // 또다른 student 구조체 변수의 주소를 저장하는 멤버
};

 

동적 메모리를 사용하는 연결 리스트에서 사용되는 예이다. (자기 참조 구조체)

자기 자신과 똑같은 다른 student 구조체 변수를 가리키는 포인터를 멤버로 가지고 있는 구조체이다.

 

 

 

구조체와 함수

 

구조체 혹은 구조체 포인터를 함수의 인수로 전달할 수도, 반환값으로 반환할 수도 있다.

구조체를 인수로 넘기는 경우엔, 값에 의한 호출이 일어나 복사본이 전달된다.

구조체가 반환값으로 전달되면, 여러 변수들의 값을 한 번에 전달할 수 있으므로 용이한다.

 

메모리 공간을 효율적으로 사용하기 위해, 구조체의 포인터를 사용할 수 있다.

매개 변수를 구조체 포인터로 하는 함수는 다음과 같다.

함수를 호출 할 땐, 구조체의 주소를 전달해주어야 한다.

 

#include <stdio.h>

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

int equal(struct student* p1, struct student* p2) // 구조체 포인터를 인수로 전달받음
{
	if (p1->number == p2->number)
		return 1;
	else return 0;
}
int main(void){

	struct student s1 = { 1, "Kim", 4.3 };
	struct student s2 = { 2,"Lee", 4.5 };

	if (equal(&s1, &s2) == 1) // 함수 호출 할 때, 구조체의 주소 전달
		printf("같은 학생\n");
	else
		printf("다른 학생\n");

	return 0;
}

 

 

 

typedef : 사용자 정의 자료형

 

복잡한 형식의 구조체를 읽기 쉬운 새로운 자료형으로 정의할 수 있다.

 

#include <stdio.h>

// student 구조체를 typedef를 이용해 새로운 자료형 STU로 정의
typedef struct student {
	int number;
	char name[10];
	double grade;
}STU;

// STU의 포인터 두 개를 인수로 전달 받음
int equal(STU* p1, STU* p2)
{
	if (p1->number == p2->number)
		return 1;
	else return 0;
}

int main(void){

	STU s1 = { 1, "Kim", 4.3 };
	STU s2 = { 2,"Lee", 4.5 };

	if (equal(&s1, &s2) == 1)
		printf("같은 학생\n");
	else
		printf("다른 학생\n");

	return 0;
}
728x90
반응형
Comments