사자자리

[C언어] 6주차 구조체 본문

C언어/C언어 이론

[C언어] 6주차 구조체

renne 2022. 5. 27. 21:17
#include <stdio.h>
#include <string.h>
struct student{		//구조체 정의
    char name[20];	//구조체의 멤버들
    int kor, eng, math;
}s1;			//변수명 정의

int main(){
    strcpy(s1.name, "Sirius");	//점(.)으로 구조체의 멤버에 접근하여 값 할당
    s1.kor = 90;
    s1.eng = 95;
    s1.math = 100;

    struct student s2 = {"Regulus", 85, 90, 100};	//새 구조체 변수 선언, 초기화

    printf("%s의 성적: 국어 %d점, 영어 %d점, 수학 %d점\n", s1.name, s1.kor, s1.eng, s1.math);
    printf("%s의 성적: 국어 %d점, 영어 %d점, 수학 %d점\n", s2.name, s2.kor, s2.eng, s2.math);
    return 0;
}

<실행 결과>
Sirius의 성적: 국어 90점, 영어 95점, 수학 100점
Regulus의 성적: 국어 85점, 영어 90점, 수학 100점

 

구조체

 - struct: data structure의 약어

 - 관련된 변수들을 하나로 묶어서 사용할 수 있다.

 

구조체의 정의

 - 보통 함수 바깥에 정의한다. 함수 안에 정의하면, 해당 함수 안에서만 구조체를 사용할 수 있다.

 - 멤버: 구조체 안에 들어있는 변수

 - 구조체를 정의한다고 해서, 구조체의 멤버가 메모리에 할당되지 않는다.

 - 구조체의 변수를 선언하면, 구조체의 멤버가 메모리에 할당된다.

struct 구조체명{
    데이터형 멤버명;
    데이터형 멤버명;
    ...
}변수명, 변수명... ;	//변수명은 생략 가능, 세미콜론은 필수

//예시
struct student{		//구조체 정의
    char name[5];	//구조체의 멤버들
    int kor, eng, math;
}s1;			//변수명 정의

 

구조체의 크기

 - 모든 멤버들의 크기의 합보다 크거나 같다.

 - 메모리 정렬 때문에 멤버들 사이에 패딩이 들어갈 수도 있기 때문이다.

 - sizeof ( )

 

구조체 변수의 선언

struct	구조체명	변수명;
struct	student	s2;

 

 - 구조체 변수를 선언하면, 구조체 변수가 가진 멤버들이 메모리에 선언된 순서대로 할당된다.

#include <stdio.h>
struct student{		//구조체 정의
    char name[5];
    int kor, eng, math;
};

int main(){	
    struct student s1;	//구조체 선언
}

 

 - 구조체를 정의하면서 변수도 함께 선언할 때, 구조체명을 생략할 수 있다.

 - 그러나 구조체명이 없으므로 나중에 새로운 변수를 선언하지 못한다.

#include <stdio.h>
#include <string.h>
struct{			//구조체명 생략
    char name[10];
    int age;
}s1;

int main(){
    strcpy(s1.name, "Harry");
    s1.age = 11;
    
    struct s2;
    s2.age = 11;	//컴파일 에러
    
    struct s3 = {"Hermione", 11};	//컴파일 에러
}

 

구조체 변수의 초기화

 - { } 안에 멤버들의 초기값을 순서대로 나열한다.

 - 초기값의 개수가 멤버의 개수보다 부족하면, 나머지는 0으로 초기화된다.

 

구조체 간의 초기화 및 대입

 - 같은 구조체의 변수들끼리는 서로 초기화나 대입이 가능하다.

 - 얕은 복사

#include <stdio.h>
struct profile{
    int birthyear;
    int birthday;
};

int main(){
    struct profile p1 = {1959, 1103};
    struct profile p2 = {1961, 0823};
    struct profile p3 = p1;	//구조체 간의 초기화
    struct profile p4;
    p4 = p2;			//구조체 간의 대입
    
    printf("p1의 주소: %p = p3의 주소: %p", p1, p3);
    printf("p2의 주소: %p = p4의 주소: %p", p2, p4);
}

<실행 결과>
p1의 주소: 0000044F000007A7 = p3의 주소: 0000044F000007A7
p2의 주소: 000004CF00000786 = p4의 주소: 000004CF00000786

 

구조체 간의 비교

 - 변수끼리는 직접 비교 연산을 할 수 없다. 변수형이 다를 수 있기 때문이다.

 - 멤버끼리는 직접 비교 연산을 할 수 있다.

//변수 간의 비교: 컴파일 에러
p1 == p2

//멤버 간의 비교: 가능
p1.birthday == p2.birthday

 

구조체 배열

 - 같은 구조체의 변수를 여러 개 사용하려면 구조체 배열을 선언한다.

struct student arr[3];	//크기가 3인 student 구조체 배열을 선언

 

 - 구조체 배열도 인덱스를 이용해서 배열의 원소에 접근한다.

#include <stdio.h>
struct student{
    char name[4];
    int id;
};

int main(){
    struct student arr[3];

    printf("학생의 정보를 입력하세요.\n");
    for (int i = 0; i < 3; i++){
        printf("이름: ");
        scanf("%s", arr[i].name);
        printf("학번: ");
        scanf("%d", arr[i].id);
    }

    for (int i = 0; i < 3; i++){
    	printf("이름: %s, 학번: %d\n", arr[i].name, arr[i].id);
    }

    return 0;
}

<실행 결과>
학생의 정보를 입력하세요.
이름: Mon
학번: 2211111
이름: Tue
학번: 2222222
이름: Wed
학번: 2233333
이름: Mon, 학번: 2211111
이름: Tue, 학번: 2222222
이름: Wed, 학번: 2233333

 

구조체 포인터

 - 구조체 변수의 주소를 저장하는 포인터

 - 구조체 포인터로 구조체의 멤버에 접근할 때,  ->  연산자를 사용한다.

#include <stdio.h>
struct student{
    char name[20];
    int id;
};

int main(){
    struct student sirius = {"Sirius Black", 691103};
    struct student *p = &sirius;			//구조체 포인터 선언, 초기화

    printf("이름: %s, 학번: %d", p->name, p->id);	// -> 연산자 사용
    return 0;
}

<실행 결과>
이름: Sirius Black, 학번: 691103

 

malloc 함수

 - Memory ALLOCation

 - 사용할 메모리 공간을 확보하는 함수

 - stdlib.h 헤더 파일에 선언되어 있다.

 - 동적 메모리 할당: 원하는 시점에 원하는 크기만큼 메모리를 할당할 수 있다.

 

  사용하는 메모리 메모리 해제
일반 변수 스택(stack) 사용한 뒤 따로 처리하지 않아도 된다.
malloc 함수 힙(heap) free 함수로 메모리를 해제해야 한다.

 

메모리 누수(memory leak): 메모리를 할당만 하고 해제하지 않아 메모리 사용량이 계속 증가하는 현상

 

#include <stdio.h>
#include <stdlib.h>
int main(){
    int n = 10;
    int *p1 = &n;
    
    int *p2;
    p2 = malloc(sizeof(int));	//4바이트만큼 동적 메모리 할당
    
    printf("p1이 가리키는 메모리 주소: %p\n", p1);
    printf("p2가 가리키는 메모리 주소: %p\n", p2);
    
    free(p2);	//동적으로 할당한 메모리 해제
    return 0;
}

<실행 결과>
p1이 가리키는 메모리 주소: 000000000062FE0C
p2가 가리키는 메모리 주소: 00000000001713E0

 

비트 필드

 - 지금까지 구조체의 멤버는 각 데이터형의 크기만큼 공간을 차지했다. ex) int: 4 byte

 - 비트 필드를 사용하면 구조체의 멤버를 비트 단위로 저장할 수 있다. (8 bits = 1byte)

 - 주어진 비트로 표현 가능한 범위 밖의 값을 저장하면 오버플로우가 일어난다.

 - 비트 필드의 각 멤버는 최하위 비트부터 차례대로 배치된다.

 - 중간에 일부 비트를 비워둘 수 있다.

struct 구조체명{
    정수자료형 멤버명 : 비트수;
};
#include <stdio.h>
struct time{
    unsigned int hour : 5;
    unsigned int : 3;		// 3 비트는 사용하지 않는다.
    unsigned int min : 6;
    unsigned int sec : 6;
};

int main(){
    struct time t1;
    t1.hour = 23;	// 23:  10111, 비트 5개
    t1.min = 59;	// 59: 111011, 비트 6개
    t1.sec = 59;	// 59: 111011, 비트 6개
    printf("현재 시간: %d시 %d분 %d초", t1.hour, t1.min, t1.sec);
    return 0;
}

<실행 결과>
현재 시간: 23시 59분 59초

 

공용체

 - 여러 멤버들이 메모리를 공유해서 사용한다.

 - 공용체 변수의 멤버들을 모두 같은 주소에 할당된다.

 - 공용체의 크기는 멤버 중 가장 크기가 큰 멤버에 의해 결정된다.

 - 공용체 변수를 초기화할 때는 첫 번째 멤버의 초기값만 지정한다.

#include <stdio.h>
union data{
    unsigned long word;
    unsigned char byte[4];
};

int main(){
    union data d1;
    printf("공용체 data의 크기: %d\n", sizeof(union data));
    d1.word = 0x12345678;
    printf("d1.word = %x\n", d1.word);
    for (int i = 0; i < 4; i++){ 
        printf("d1.byte[%d] = %x\n",i, d1.byte[i]);
    }
    return 0;
}

<실행 결과>
공용체 data의 크기: 4
d1.word = 12345678
d1.byte[0] = 78
d1.byte[1] = 56
d1.byte[2] = 34
d1.byte[3] = 12

 

리틀 엔디언 최하위 바이트부터 메모리에 저장하는 방식 인텔 계열의 CPU
빅 엔디언 최상위 바이트부터 메모리에 저장하는 방식 모토로라 계열의 CPU

 

https://dojang.io/mod/page/view.php?id=455

 

열거체

 - enum: enumeration(열거, 목록)

 - 가독성 향상: 정수형 상수에 이름을 붙여서 코드를 이해하기 쉽게 해준다.

#include <stdio.h>
enum week {sun, mon, tue, wed, thur, fri, sat};	//sun = 0, mon = 1, ... sat = 6으로 정의
enum color {red = 10, green = 20, blue = 30};	//red = 10, green = 20, blue = 30으로 정의

int main(){
    enum week weekend = sat;	//열거형 변수 선언, 값 할당
    enum color ravenclaw = blue;
    printf("%d\n", weekend);
    printf("%d\n", ravenclaw);
    return 0;
}

<실행 결과>
6
30

 

typedef

 - 기존의 데이터형에 새로운 이름을 붙인다.

 - typedef 정의를 한 다음에도 원래의 데이터형을 그대로 사용할 수 있다.

typedef	기존 데이터형	새 이름;
typedef	unsigned int	UNIT;		//기본형
typedef	char*		STR;		//파생형
typedef struct student	POINT;	//사용자 정의형

 

 - typedef와 구조체

#include <stdio.h>
struct student{	//구조체 정의
    int kor, eng, math;
};

typedef struct student POINT;	//typedef 정의

int main(){
    POINT s1 = {100, 90, 80};
    printf("s1의 점수: 국어 %d 영어 %d 수학 %d", s1.kor, s1.eng, s1.math);
    return 0;
}

<실행 결과>
s1의 점수: 국어 100 영어 90 수학 80
#include <stdio.h>
typedef struct student{	//구조체 정의, typedef 정의
    int kor, eng, math;
}POINT;

int main(){
    POINT s1 = {100, 90, 80};
    printf("s1의 점수: 국어 %d 영어 %d 수학 %d", s1.kor, s1.eng, s1.math);
    return 0;
}

<실행 결과>
s1의 점수: 국어 100 영어 90 수학 80

 


 

[코딩도장] 48.6 연습문제: typedef로 좌표 구조체 정의하기

다음 소스 코드를 완성하여 2차원 좌표 x, y를 표현하는 구조체를 정의하고, 10 20이 출력되게 만드세요. 좌표의 자료형은 int입니다.

#include <stdio.h>
typedef ①__________

int main()
{
    Point2D ②__________
    p1.x = 10;
    ③__________
    printf("%d %d\n", p1.x, p1.y);
    return 0;
}

 

#include <stdio.h>
typedef struct grid{
    int x, y;
}Point2D;

int main()
{
    Point2D p1;
    p1.x = 10;
    p1.y = 20;
    printf("%d %d\n", p1.x, p1.y);
    return 0;
}

 

[코딩도장] 49.6 연습문제: 3차원 좌표 구조체 포인터에 메모리 할당하기

3차원 좌표 구조체 _Point3D가 정의되어 있습니다. 다음 소스 코드를 완성하여 10.000000 20.000000 30.000000이 출력되게 만드세요.

#include <stdio.h>
#include <stdlib.h>
typedef struct _Point3D {
    float x;
    float y;
    float z;
} Point3D;

int main()
{
    Point3D *p1 = ①__________
    ②__________

    printf("%f %f %f\n", p1->x, p1->y, p1->z);
    free(p1);
    return 0;
}

 

#include <stdio.h>
#include <stdlib.h>
typedef struct _Point3D {
    float x;
    float y;
    float z;
} Point3D;

int main()
{
    Point3D *p1 = malloc(sizeof(Point3D));
    p1 -> x = 10;
    p1 -> y = 20;
    p1 -> z = 30;

    printf("%f %f %f\n", p1->x, p1->y, p1->z);
    free(p1);
    return 0;
}

'C언어 > C언어 이론' 카테고리의 다른 글

[C언어] 7주차 포인터의 활용  (0) 2022.06.26
[C언어] 6주차 함수의 활용  (0) 2022.06.25
[C언어] 5주차 포인터  (0) 2022.05.20
[C언어] 5주차 배열과 문자열  (0) 2022.05.20
[C언어] 4주차 함수  (0) 2022.05.14
Comments