구조체(structure)esslab.hanyang.ac.kr/uploads/data_structure_2019_1... · 2019-03-22 ·...

40
구조체(Structure) C-언어의 활용을 위한 주요 기법 (4) Dong Kyue Kim Hanyang University [email protected]

Upload: others

Post on 13-Aug-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

  • 구조체(Structure)

    C-언어의 활용을 위한 주요 기법 (4)

    Dong Kyue Kim

    Hanyang University

    [email protected]

  • 구조체 (Structure)

  • 3

    구조체란?

    • 구조체

    – 하나 이상의 변수를 그룹 지어서 새로운 자료 형으로 정의

    – 배열은 여러 개의 같은 자료 형을 하나로 묶는 것이라면

    – 구조체는 서로 다른 자료 형의 변수를 하나로 묶는 것

    – 포인터 변수나 배열, 구조체도 그룹에 속할 수 있다.

    배 열

    10 20 30

    구 조 체

    103.14

    “Hello”

  • 4

    구조체

    • 구조체가 필요한 이유

    – 대량의 동일한 자료 유형의 데이터를 위하여 배열을 사용

    – 서로 다른 종류의 데이터를 하나로 묶기 위해서 필요함

    – 학생들의 데이터(학번, 이름, 생년월일, 키, 몸무게 등의 정보)를 다룰 때

    – 위의 데이터들이 서로 묶여 있지 않으면 다루기가 불편

    – 한 학생에 대한 여러 가지 서로 다른 형태의 데이터 유형인 정보를 하나로묶는 것이 편리

    int number; // 학번char name; // 이름double grade; // 성적

  • struct a_struct{char a;int b;double c;int k[10];

    };

    구조체 선언(정의)

    • 구조체 선언(정의)

    – 구조체는 여러 가지 기본 자료 유형을 묶은 하나의 자료 유형이 되는데 이를 가리켜 사용자 정의 자료 유형이라 한다.

    – 사용자가 정의한 구조체 자료 유형의 이름을 태그(tag)라 한다.

    – 태그는 변수 명이 아닌 자료 유형의 이름이다.

    – 구조체 멤버: 구조체를 이루는 변수들

    – 구조체 선언은 변수의 선언이 아니다

    5

    구조체 멤버

    태그(tag)

    a_struct라는 이름의 구조체를 정의하고 있다.

    struct 태그 { 자료형 멤버1;자료형 멤버2;...

    };

  • #include

    struct a_struct {char a;int b;double c;int k[10];

    };

    int main(void){

    struct a_struct a1, a2;

    return 0;}

    #include

    struct a_struct {char a;int b;double c;int k[10];

    }a1, a2, a3;

    int main(void){

    /*본문*/return 0;

    }

    구조체 변수의 선언

    • 구조체 변수의 선언

    – 구조체의 정의와 구조체의 변수의 선언을 같이하거나 정의를 먼저 한 후에 변수를선언하는 두 가지 방법이 있다.

    – 정의를 먼저 한 후 변수를 선언 할 때는, 구조체임을 밝히는 struct를 써주고 사용자가 정의한 구조체 자료 유형의 이름을 적고 원하는 변수 명으로 선언한다

    6

    정의

    변수 선언

  • 구조체 변수 - 초기화

    • 구조체 변수의 초기화

    – 구조체를 초기화하는 것은 배열을 초기화 하는 것과 같다.

    – 아래의 예는 선언과 동시에 초기화를 한 것이다.

    – 선언을 한 후에 초기화를 할 수도 있다.(다음 장의 ppt)

    7

    #include

    struct person{

    char name[20];char phone[20];int age;

    };

    int main(){

    struct person p_1 = {"Esslab", "02-2220-xxxx", 5};printf ("%s %s %d\n", p_1.name, p_1.phone, p_1 age);return 0;

    }

    Esslab 02-2220-xxxx 5

  • #include

    struct a_struct{

    char a;int b;double c;int k[2];

    };int main(){

    struct a_stuct a1;a1.a = 't';a1.b = 100;a1.c = 10.11;a1.k[0] = 10;a1.k[1] = 20;

    printf("%c %d %.3lf\n", a1.a, a1.b, a1.c);printf("%d %d\n", a1.k[0], a1.k[1]);return 0;

    }

    구조체 변수 - 접근

    • 구조체 변수로의 접근

    – 구조체 변수 내의 멤버에 접근 하려면 멤버 연산자 . 을 사용하면 된다.

    – 아래의 예시는 선언을 한 후에 구조체 멤버에 각각 접근을 하여 초기화

    8

    a1 구조체의 멤버 a를 의미한다.

    t 100 10.11010 20

  • int main(){

    struct str_size p_1;printf(“%d \n”, sizeof(p_1));return 0;

    }

    • 구조체의 크기

    – 다음 구조체의 크기를 예측하여 보자.

    – sizeof를 사용해서 측정하여 보자.

    struct str_size{

    char a[10];int b[4];char c;int d;double e;char f;

    };

    구조체의 크기 (1/4)

    9

    10byte16byte1byte4byte8byte1byte

    40byte

    예측과는 다르게 56byte가 나온다.

    56

  • struct sample{

    int id;char name;int age;

    };

    구조체의 크기 (2/4)

    • 구조체의 크기가 예측 값과 측정 값이 다른 이유

    – 컴파일러가 컴퓨터가 연산하기 편하고 값을 빨리 읽어 들일 수 있도록 구조체를 구성하기 때문이다.

    – 구조체 맴버중에 가장 큰 타입을 기준으로 정렬하게 된다.

    – 32비트 시스템일 경우 한 단위가 32비트 보다 작을 경우 쉬프트 연산으로인한 오버헤드가 크다. 차라리 몇 바이트 낭비하고 속도를 향상시키는 것이좋다.

    10

    id

    name age

    id

    name

    age

    예상

    실제

    int형이 구조체 안에서 가장 큰 타입이므로 4byte

    기준으로 정렬 된다.

    이 예제는 32비트 컴퓨터일 경우의 예이다.

  • 구조체의 크기 (3/4)

    • 구조체 크기 예제 (32비트 컴퓨터에서 Visual 2010으로 하였을 경우)

    11

    a

    b

    c

    d

    e

    f

    구조체의 끝은 여기다.따라서 총 56바이트가 된다.

    64비트 컴퓨터일 경우 한 단위를 8byte로 하기 때문에 위 그

    림과 다른 결과가 나온다.

    struct str_size{

    char a[10];int b[4];char c;int d;double e;char f;

    };

  • 구조체의 크기 (4/4)

    • 구조체 멤버 순서에 따른 구조체의 크기

    – 구조체 멤버의 순서를 잘 정렬하면 낭비되는 메모리를 줄일 수 있다.

    12

    a

    b

    c d

    e

    f

    앞의 예제와 같은 맴버를 가진 구조체이지만 구조체의 용량이

    56byte에서 40byte로 줄었다.

    struct str_size{

    char a[10];char c;char f;int d;int b[4];double e;

    };

  • 구조체와 배열

  • 구조체 배열

    • 구조체 배열

    – 기본 자료형의 배열과 같이 구조체를 여러 개 모은 것이다.

    – 구조체 배열의 선언은 기본 자료형의 배열을 선언하는 방식과 동일하다.

    14

    struct person{char name[20];char phone[20];int age;

    };

    int main(){

    struct person sArray[5];return 0;

    }

  • 구조체 배열의 구조

    • 구조체 배열의 구조

    15

    sArray[0].name sArray[0].phone sArray[0].age

    sArray[1].name sArray[1].phone sArray[1].age

    sArray[2].name sArray[2].phone sArray[2].age

    sArray[3].name sArray[3].phone sArray[3].age

    sArray[4].name sArray[4].phone sArray[4].age

    sArray[0]

    sArray[1]

    sArray[2]

    sArray[3]

    sArray[4]

  • 구조체 배열의 초기화

    • 구조체 배열의 초기화

    – 2차원 배열 초기화 방법과 같다.

    16

    struct person{char name[20];char phone[20];int age;

    };

    int main(){

    struct person sArray[3] = {{“Lee”, “2220-0001”, 21},{“Kim”, “2220-0002”, 22},{“Im”, “2220-0003”, 23}

    };return 0;

    }

  • 구조체와 포인터

  • 구조체와 포인터

    • 구조체와 포인터

    – 구조체에서 포인터가 사용되는 경우에는 크게 두 가지가 있다.

    • 구조체 포인터를 선언하여 구조체 변수를 가리키는 경우.

    • 구조체의 멤버로 포인터 변수가 선언되는 경우.

    18

  • #include struct person{

    char name[20];int age;

    };int main(){

    struct person man = {"Kim", 27};struct person *pMan;pMan = &man;

    //구조체 변수를 이용한 출력printf ("name : %s\n", man.name);printf ("age : %d\n", man.age);

    //구조체 포인터를 이용한 출력printf ("name : %s\n", (*pMan).name);printf ("age : %d\n", (*pMan).age);return 0;

    }

    구조체 포인터

    • 구조체 포인터

    – 구조체 포인터를 선언하여 구조체 변수를 가리킬 수 있다.

    19

    name : Kimage : 27name : Kimage : 27

  • int main(){

    struct person man = {"Kim", 27};struct person *pMan;pMan = &man;

    //간접 멤버 접근 연산자를 사용하여 출력printf ("name : %s\n", pman->name);printf ("age : %d\n", pman->age);return 0;

    }

    -> 연산자

    • 간접 멤버 접근 연산자 ( -> )

    – 구조체 포인터를 사용하여 출력할 경우 참조 연산자( * )가 멤버 연산자( . )보다우선 순위가 낮아서 괄호를 해주지 않으면 엉뚱한 값이 출력된다.

    – 간접 멤버 접근 연산자( -> )는 두 개의 연산자( * , . )가 합쳐진 의미로 구조체포인터를 사용하는데 상당히 편리해진다.

    20

    *pMan.name *(pMan.name)= 엉뚱한 값

    (*pMan).name≠ 제대로 된 값

    (*pMan).name = pMan->name

    name : Kimage : 27

  • 구조체 멤버인 포인터 변수

    • 포인터 변수

    – 구조체의 멤버로 포인터 변수가 선언되는 경우

    – 기본 자료형의 포인터가 될 수도 있고 더블 포인터나, 배열 포인터, 구조체 포인터등 도 될 수도 있다.

    21

    #include

    struct person{

    char name[20];int *age;

    };

    int main(){

    int pAge = 27;struct person man = {"Kim", &pAge};

    printf("name : %s\n", man.name);printf("age : %d\n", *(man.age));return 0;

    }

    구조체 멤버로 기본 자료형의포인터 변수를 넣은 경우.

    name : Kimage : 27

  • #include

    struct location{

    char addr[30];char tel[20];

    };struct person{

    char name[20];int age;struct location *loc;

    }int main(){

    struct person man = {"Kim", 27};struct location s_loc = {"Seoul", "2220-0000"};man.loc = &s_loc;

    printf("name : %s\n", man.name);printf("age : %d\n", man.age);printf("loc_addr : %s\n", man.loc->addr);printf("loc_tel : %s\n", man.tel);return 0;

    }

    • 구조체 포인터를 구조체 멤버로 갖는 경우

    구조체 멤버인 구조체 포인터

    22

    구조체 멤버로 구조체의 포인터 변수를 넣은 경우.

    loc

    man

    age

    name

    tel

    addr

    s_loc

    name : Kimage : 27loc_addr : Seoulloc_tel : 2220-0000

  • 구조체 변수의 전달과 리턴

  • #include struct simple{

    int data1;int data2;

    };

    int main(){

    struct simple ts1 = {1,2};struct simple ts2;ts2 = ts1;printf("ts2.data1 : %d\n", ts2.data1);printf("ts2.data2 : %d\n", ts2.data2);return 0;

    }

    구조체 변수 연산

    • 구조체 변수의 연산

    – 다양한 연산이 가능한 기본 자료형 변수에 비해 구조체 변수는 제한된 연산만 허용

    – 허용되는 연산으로는 대입연산, sizeof연산, 포인터 관련 연산(. -> *)이있다.

    24

    대입 연산 시 양쪽의 구조체는 같은 자료형이어야 한다.

    ts2.data1 : 1ts2.data2 : 2

  • #include struct simple{

    int data1;int data2;

    };struct simple add_1 (struct simple kst);int main(){

    struct simple ts = {1,2};ts = add_1(ts);printf("ts.data1 : %d\n", ts.data1);printf("ts.data1 : %d\n", ts.data1);return 0;

    }struct simple add_1 (struct simple kst){

    (kst.data1)++;(kst.data2)++;return kst;

    }

    25

    구조체 변수의 전달과 리턴

    • 구조체 변수의 전달과 리턴

    – 함수 호출 시 구조체 변수를 인자로 전달하거나 리턴 하는 과정에서 일어나는 모든 일은 기본 자료형 변수와 동일하다.

    ts.data1 : 2ts.data2 : 3

  • #include struct simple{

    int data1;int data2;

    };struct simple* add_1 (struct simple* kst);int main(){

    struct simple ts1 = {1,2};struct simple ts2;ts2 = *(add_1(&ts1));printf("ts1.data1 : %d\n", ts1.data1);printf("ts1.data2 : %d\n", ts1.data2);printf("ts2.data1 : %d\n", ts2.data1);printf("ts2.data2 : %d\n", ts2.data2);return 0;

    }struct simple* add_1 (struct simple* kst){

    kst->data1++;kst->data2++;return kst;

    }

    구조체 변수의 전달과 리턴 시 문제점

    • 구조체 변수의 전달과 리턴 시 문제점

    – 구조체 변수의 크기가 클 경우 함수의 인자를 전달하고 리턴 할 때마다 큰용량이 복사되므로 함수 자체의 용량도 커지고 속도도 느려진다.

    – 위 문제점을 해결하기 위해서 주소 값만 넘겨서 연산하는 것이 좋음

    26

    ts1.data1 : 2ts1.data2 : 3ts2.data1 : 2ts2.data2 : 3

  • typedef

  • typedef

    • typedef

    – 키워드 typedef는 이미 존재하는 자료형에 새로운 이름을 붙이기 위한 용도로 사용된다.

    – 이름이 긴 자료형을 간단하게 만들어 준다.

    28

    INT라는 이름을 자료형 int에게 이름 지어 준다는 뜻.

    #include typedef int INT;typedef int* P_INT;

    int main(){

    INT a = 10;// int aP_INT pa = &a; // int *paprintf ("%d %d\n", a, *pa);return 0;

    }

    typedef int INT;

  • typedef와 구조체 변수의 선언

    • 구조체 정의와 분리된 typedef 선언

    29

    #include

    struct Data{

    int data1;int data2;

    };

    typedef struct Data sData;

    int main(){

    sData d = {1, 2};printf ("d.data1 : %d\n", d.data1);printf ("d.data2 : %d\n", d.data2);return 0;

    }

    자료형 struct Data 에게sData라는 이름을 지어준다.이 이후 sData로 선언되는 것은 struct Data로 선언되는 것과 같다.

    d.data1 : 1d.data2 : 2

  • typedef와 구조체 변수의 선언

    • 구조체 정의와 typedef 선언 동시에 하기

    30

    #include

    typedef struct Data{

    int data1;int data2;

    }sData;int main(){

    sData d = {1, 2};printf ("d.data1 : %d\n", d.data1);printf ("d.data2 : %d\n", d.data2);return 0;

    }

    #include

    typedef struct{

    int data1;int data2;

    }sData;int main(){

    sData d = {1, 2};printf ("d.data1 : %d\n", d.data1);printf ("d.data2 : %d\n", d.data2);return 0;

    }

    구조체 정의와 동시에선언 할 때는 구조체의원 이름을 붙이지 않아도 상관없다.

    d.data1 : 1d.data2 : 2

  • typedef와 구조체 변수의 선언

    • typedef로 만든 새로운 자료형 이름을 다시 typedef로 선언 가능하다.

    31

    #include

    typedef struct Data{

    int data1;int data2;

    }sData;

    typedef sData * pData;

    int main(){

    pData d;sData s = {1, 2};d = &s;printf ("d.data1 : %d\n", d->data1);printf ("d.data2 : %d\n", d->data2);return 0;

    }

    d.data1 : 1d.data2 : 2

  • 문자열

  • 문자열

    • 문자열

    – “”안에 들어 있는 문장을 문자열 상수라고 한다.

    – 단일 문자의 경우 char 형 변수에 저장을 하면 되지만 문자열의 경우char형 배열을 이용해야 저장이 가능하다.

    – 문자열은 %s를 서식문자로 사용한다.

    33

    #include

    int main(void){

    char str1[5] = "Good";char str2[] = "morning";

    printf("%s \n", str1);printf("%s %s \n", str1, str2);return 0;

    }

    GoodGood morning

  • Null 문자

    • 문자열의 길이와 배열의 크기

    – “”로 묶인 문자열은 끝에 null문자 라는 것이 자동적으로 삽입되기 때문에 5글자 짜리 문자열은 실제 6자리이기 때문에 저장하려는 배열의 크기를 6개로 해야한다.

    • Null 문자

    – 문자열의 끝을 나타내는 문자.

    – ‘\0’라고 표시한다.

    – 문자열이 저장된 배열에서 각 문자의 위치를 바꿀 때 null문자는 항상 맨 뒤에 있도록 해야한다.

    34

    str [0]

    H e l l o \0

    str [1] str [2] str [3] str [4] str [5]

    char str[6] = “Hello”;

  • Null 문자의 필요성

    • Null 문자의 필요성

    – NULL 문자로 문자의 끝을 표시하지 않으면 문자의 끝을 판단하기 어려움

    – 배열의 크기를 크게 지정하고 문자열을 조금 저장하였을 경우 저장한 문자열의 끝이 어디인지 저장해 두지 않으면 컴퓨터는 어디가 끝인지 모르니 배열의 끝까지 출력

    – 배열의 저장 안된 부분의 쓰레기 값도 출력하게 되어서 문자열이 이상하게나오게 된다.

    35

    int main (void){

    char str [10];str [0] = ‘E’;str [1] = ‘S’;str [2] = ‘S’;

    printf(“%s \n”, str);return 0;

    }

    문자열의 끝인 Null을 입력하지 않았다.

    화면 출력 결과: ESS聯!^3%걀龜

    Memory에 있던 쓰레기값이 출력됨

  • 문자열과 포인터

  • • 문자열 표현 방식

    – 문자열을 표현하는 방식은 배열로 표현하는 방법과 포인터와 문자열 상수로표현하는 방법이 있다.

    문자열 변수

    배열 str1

    문자열 표현 방식

    37

    a b c d \0

    ABCD\0

    포인터 문자열 상수

    str2

    char str1[5] = “abcd”;char *str2 = “ABCD”;

  • char *str2 = “ABCD”;

    문자열 상수

    • 포인터와 문자열 상수

    – 문자열 상수는 메모리공간에 할당되면 주소를 반환한다.

    – 반환된 주소 값을 포인터가 갖게 된다.

    38

    1. 메모리 공간에 할당.

    12345678

    2. 주소값이 반환되어포인터 str2 초기화.

  • int main(){

    char str1[5] = "abcd";char *str2 = "ABCD";

    str1[0] = 'x';str2[0] = 'x';

    return 0;}

    문자열 표현 방식의 차이

    • 문자열 표현 방식의 차이

    – 배열을 이용한 표현은 문자열 변수. 변경이 가능

    – 문자열 상수는 변경이 불가능

    39

    배열 str1은 문자열 변수이므로 변경이 가능하다.

    포인터 str2가 가리키고 있는것은 문자열 상수이므로 변경이 불가능하다 . 따라서 이 부분에서 에러가 발생한다.

  • 문자열 배열

    • 문자열 배열

    – char형 포인터 배열은 여러 개의 문자열을 저장할 수 있는 특징 때문에 문자열 배열이라고 표현한다.

    40

    “Esslab”

    “Pointer”

    “C_Programing”

    arr[0]

    arr[1]

    arr[2]

    char *arr[3] = {"Esslab","C_Programming","Pointer“

    };