-목차-
1. 구조체 자료형 정의 및 변수 선언 방법
2. typedef와 구조체 자료형
3. 구조체의 메모리 구조
4. 문자열 배열, 포인터와 구조체 변수에 대한 Assigment 연산
5. 구조체의 배열
1. 구조체 자료형 정의 및 변수 선언 방법
○ 구조체
※ 구조체는 여러 자료형의 연속된 변수들로 구성된 집합체
ex) 학생관리 프로그램의 각 학생에 대한 기록
- 이름, 학번, 나이, 전화번호, 전공 등 학생과 관련한 여러 자료
- 한 학생에 대한 여러 자료들을 개별적으로 다루기보다 하 곳에(하나의 변수로) 모아서 관리하는 것이 편리
※ C의 구조체 구조
- 구조체는 하나 이상의 멤버/필드 변수들로 구성된다
- 각 멤버/필드 변수들은 서로 다른 자료형을 가질 수 있다
※ 구조체는 일종의 자료형(Type) ★★
- 구조체 선언은 새로운 자료형을 정의하는 것으로 변수 선언과는 다르다
- 새로운 자료형의 이름은 "struct name" 이다
○ 구조체 자료형 정의 및 변수 선언 방법
#include <stdio.h>
int main(void) {
// 구조체 자료형의 정의
struct student { // struct : 구조체 선언 시 필요한 키워드, student : 구조체 이름
int id; // 구조체 멤버의 자료형 + 구조체 멤버의 이름
char *pname;
double points;
}; // 마지막에 ; 빼먹지 말기
struct student s1, s2;
s1.id = 1;
s1.pname = "Kim";
s1.points = 9.9;
s2.id = 2;
s2.pname = "Hwang";
s2.points = 9.8;
printf("[%d:%s] = %lf\n", s1.id,
s1.pname, s1.points);
printf("[%d:%s] = %lf\n", s2.id,
s2.pname, s2.points);
return 0;
}
※ 구조체 자료형의 정의
※ 구조체 변수의 선언
- 앞서 정의한 구조체 자료형을 이용해 선언
※ 구조체 변수의 사용 <- 구조체 변수가 지역변수인 경우
- 구조체 변수에 포함된 멤버 변수에 접근하기 위해서는 점 연산자를 사용해야 한다.
구문 형식 : 구조체변수.멤버명
+) 다음 A, B, C 3개의 코드는 결과적으로 동일하다
- A는 구조체 자료형의 정의와 해당 구조체 변수 선언을 분리한 것
- B는 구조체 자료형의 정의와 해당 구조체 변수 선언을 동시에 한 것 (A에 비해 상대적으로 간결함) ★★★
- C는 구조체의 이름을 제거한 것 ( 해당 구조체를 가지는 또 다른 변수를 선언할 때 불편하기에 잘 쓰이지 않음)
2. typedef와 구조체 자료형
▷ Syntax Error가 발생한 이유는 무엇일까?
- 사용할 함수 이전에 자료형을 미리 등록하지 않았기 때문 ★★
▷ 해결책은?
- typedef로서 아래 설명을 참고하자
○ typedef : 자료형 별명 만들기 ★★
※ typedef
- 복잡한 type 이름을 간단히 표시하기 위해 주로 사용
- 선언뿐만 아니라 정의까지 한번에 할 수 있음
- 구문 형식
※ typedef를 통해 해결하기
#include <stdio.h>
typedef struct student { // struct student라는 복잡한 자료형을 STUD라는 간단한 이름으로 표현 가능
int id;
char *pname;
double points;
} STUD;
void stud_printx(STUD s)
{
printf("[%d:%s] = %lf\n", s.id,
s.pname, s.points);
}
int main(void)
{
STUD s1; // s1과 s2는 서로 같은 자료형이다(선언 시 사용한 자료형의 이름만 다른 것)
struct student s2;
s1.id = 1;
s1.pname = "Kim";
s1.points = 9.9;
s2.id = 2;
s2.pname = "Hwang";
s2.points = 9.8;
stud_printx(s1);
stud_printx(s2);
return 0;
}
- function declaration처럼 typedef를 통해 struct name을 미리 선언할 수 있다 (정의까지 한번에 가능)
+) 구조체 변수도 초기화 가능
- 배열의 초기화와 유사
- 멤버/필드 변수의 자료형과 초기화하는 값의 자료형이 일치하여야 함 ★★
#include <stdio.h>
typedef struct student {
int id;
char *pname;
double points;
} STUD;
int main(void)
{
STUD s1 = {1, "Kim", 9.9};
struct student s2 = {2, "Hwang", 9.8};
return 0;
}
3. 구조체의 메모리 구조
○ 구조체의 메모리 구조 ★★
- 구조체의 멤버들이 선언된 순서대로 연속하여 메모리 공간을 차지함
- 각 멤버들은 자신의 자료형에 해당하는 메모리 공간을 차지
ex)
○ 멤버 변수가 동일하지만 순서가 다른 경우
※ 메모리 크기와 멤버 주소 비교
#include <stdio.h>
typedef struct student {
int id;
char *pname;
double points;
} STUD;
typedef struct student2 {
int id;
double points;
char *pname;
} STUD2;
int main(void)
{
STUD s1 = {1,"Kim",9.9};
STUD2 s2 = {2,9.8,"Hwang"};
printf("%d",sizeof(s1.points));
printf("%p, %d\n", &s1, sizeof(s1));
printf("%p, %d\n", &s2, sizeof(s2));
printf("[s1 id, pname, points] = %p, %p, %p\n",
&s1.id, &s1.pname, &s1.points);
printf("[s2 id, pname, points] = %p, %p, %p\n",
&s2.id, &s2.points, &s1.pname);
return 0;
}
- 위 코드의 결과는 다음과 같다
※ 이러한 차이의 원인은 무엇일까?
- word alignment, 특정 자료형의 저장 위치가 정해져있기때문 ★★
- double형 (멤버) 변수의 경우 0x00000?에서 ?이 0이나 8인 주소에만 저장할 수 있다.
- int형 (멤버) 변수는 0, 4, 8, C인 주소에만 저장할 수 있다.
4. 문자열 배열, 포인터와 구조체 변수에 대한 Assignment 연산
#include <stdio.h>
void print_str (char *str)
{
while (*str != '\0')
putchar(*str++);
}
int main(void)
{
char aMsg1[] = "Hello";
char aMsg2[10];
int aN1[] = {1,2,3};
int aN2[3];
aN2 = aN1; // Syntax Error 발생
aMsg2 = aMsg1; // Syntax Error 발생
print_str(aMsg1);
print_str(aMsg2);
return 0;
}
- 위의 코드에서 Syntax Error가 발생한다. 왜냐하면 배열은 배열 상수로서 대입 연산의 좌측에 올 수 없기때문이다.
○ 구조체 변수에 대한 Assignment 연산 ★★
※ 구조체 변수에 대해서는 Assignment 연산을 할 수 있을까?
- 전통(?) C언어에서는 지원하지 않았으나 현재는 가능하며 실제 값들이 모두 복사된다. ( 메모리의 내용이 그대로 복사, 허용되기는 하지만 남용하지 않아야 하며 일반적인 경우 비추천)
- s2 = {2,"Kang",9.8}; 이렇게는 대입 연산 불가능. 구조체 변수1 = 구조체 변수2 이렇게만 대입연산 가능
○ 문자열 배열과 포인터
#include <stdio.h>
void foo(char *pstr) {
printf("4:%d, %d",
sizeof(pstr),
sizeof(*pstr));
}
int main(void)
{
char msg[] = "123456789";
char *pmsg = msg;
int n = 0, *pn = &n;
printf("0:%d\n", sizeof(pn)); // size of a pointer variable
printf("1:%d\n", sizeof(msg)); // size of the array
printf("2:%d\n", sizeof(pmsg)); // size of a pointer variable
printf("3:%d\n", sizeof(*pmsg)); // size of
foo(msg); // foo(pmsg);
return 0;
}
- 위의 코드의 결과는 다음과 같다.
0 : sizeof(pn)은 n에 대한 포인터 변수 pn의 메모리 크기
1 : sizeof(msg)는 배열 msg의 메모리 크기
2 : sizeof(pmsg)는 msg에 대한 포인터 변수 pmsg의 메모리 크기 (포인터 변수의 메모리 크기는 하나로 정해져 있음)
3 : sizeof(*pmsg)는 문자열 msg의 첫번째 문자(pmsg와 메모리 공간을 공유하는 애)의 메모리 크기 ★★★
○ 배열, 포인터의 초기화 vs Assginment(=)
※ 배열 초기화 vs Assignment(=)
#include <stdio.h>
int main(void)
{
char msg1[10] = "123456789"; // 배열 초기화는 assignment 연산이 아님
char msg2[10];
char msg3[10];
int na1[5] = {1,2,3,4,5};
int na2[5];
int na3[5];
msg2 = "987654321"; // 배열에 assignment 연산을 했기에 syntax error
na2 = {5,4,3,2,1}; // 배열에 assignment 연산을 했기에 syntax error
msg3 = msg1; // 배열에 assignment 연산을 했기에 syntax error
na3 = na1; // 배열에 assignment 연산을 했기에 syntax error
return 0;
}
※ 배열의 포인터 변수 초기화 vs Assignment(=)
#include <stdio.h>
int main(void)
{
char msg1[10] = "123456789"; // 배열 초기화는 assignment 연산이 아님
char *pmsg1 = msg1; // 문자열 msg1의 주소를 담는 초기화 과정
char *pmsg2 = "123456789"; // 문자열 상수 123456789(msg1과 다름)의 주소를 담는 초기화 과정
// 참고로 msg1 : 동적 공간 속 stack에 존재하며 바뀔 수 있는 값
// 123456789 : 정적 공간 속 data에 존재하며 바뀔 수 없는 값
char *pmsg3;
char *pmsg4;
pmsg3 = "987654321"; // 정적 공간 속 data 987654321의 주소를 담는 과정. 에러 X.
pmsg4 = msg1; // pmsg4는 배열이 아닌 포인터이기에 assignment연산이 가능
printf("%s %s %s %s\n",
pmsg1, pmsg2, pmsg3, pmsg4);
return 0;
}
○ 구조체 초기화
#include <stdio.h>
typedef struct student1 {
int id;
char *pname;
double points;
} STUD1;
typedef struct student2 {
int id;
char name[128];
double points;
} STUD2;
int main(void) {
STUD1 st11 = {1,"KIM",99.9} , st12, st13; // 구조체 초기화 과정에서는 가능
STUD2 st21 = {2,"Hwang",99.8}, st22, st23;
st12 = {3,"Park",89.9}; // 구조체 변수가 아닌 값을 assignment 연산 불가능
st22 = {4,"Choi",93.1}; // 구조체 변수가 아닌 값을 assignment 연산 불가능
st13.id = 1; // 구조체 변수에 대해 점 연산자로 접근 가능
st13.pname = "Kim";
st13.points = 91.9;
st23.id = 2;
st23.name = "Lee"; // 배열에 assginment하는 것은 불가능
st23.points = 92.1;
return 1;
}
○ 구조체의 배열
※ 구조체에 대한 배열도 가능함
※ 첨자를 이용하여 배열 순회 가능
#include <stdio.h>
typedef struct student {
int id;
char *pname;
double points;
} STUD;
int main(void) {
STUD pnuecs[3] = { {1,"Choi",9.9}, {2,"Park",0.1}, {3,"Kim",9.9}};
int i = 0;
while (i<3) {
printf("[%d:%s] = %lf\n",
// 지역변수로 선언된 배열에 대해 첨자연산으로 순회 가능
pnuecs[i].id, pnuecs[i].pname, pnuecs[i].points);
i++;
}
return 0;
}
+) 구조체 배열의 끝 표시
※ 배열의 끝을 전달하는 방법은 다음과 같음
1. 배열의 크기 값을 함수 인자로 전달
2. 특별한 값을 가진 요소로 배열 끝을 표시 ex) NULL문자, '\0'로 끝을 표시하는 문자열
ex)
#include <stdio.h>
typedef struct student {
int id;
char *pname;
double points;
} STUD;
int main(void) {
STUD pnuecs[] = { {1,"Choi",9.9}, {2,"Park",0.1}, {3,"Kim",9.9},
{4,"Lee",3.0}, {5,"Moon",9.5}, {6,"Kang",7.0},
{-1,NULL,0} };
int i = 0;
while (pnuecs[i].id >=0 ) {
printf("[%d:%s] = %lf\n",
pnuecs[i].id, pnuecs[i].pname, pnuecs[i].points);
i++;
}
return 0;
}
'CS > C, C++' 카테고리의 다른 글
C/C++ 자료형 범위 및 바이트 (0) | 2024.04.19 |
---|---|
[C/C++] 09 - 2 구조체 변수와 함수, 포인터 (0) | 2021.12.15 |
[C/C++] 08 - 2 배열, 문자열과 포인터 (0) | 2021.11.17 |
[C/C++] 08 - 1 포인터 개념 (0) | 2021.11.12 |
[C/C++] 07 - 3 함수와 모듈러 프로그래밍 (0) | 2021.11.02 |