C언어 보충5 - 16, 17장

2018. 2. 3. 20:58

 

16장. 동적할당(동적할당을 위해서는 stdlib.h를 헤더파일로 포함시켜야한다)

 

1. malloc

 - 원형: void *malloc(unsigned int size);

 - 의미: 입력한 size크기만큼 공간을 할당하고 시작주소를 반환

 - 사용

    size에는 '사용하고자하는 자료형의 크기*할당하고 싶은 크기'를 입력

    반환값은 void *형 이기 때문에, 사용하고자하는 자료형에 맞추어 포인터를 형변환하여 사용한다 

    ex)

     - int *pi;

     - pi=(int *)malloc(5*sizeof(int));

     - free(pi);

 - 특징

    malloc이 공간을 할당할수 없을때 널포인터를 반환(따라서 이럴때는 간접참조 연산이 불가능하다) => if(pi==NULL)로 확인가능(487.p 예제 참조)

    동적할당 이후에는 free로 반환을 반드시 해야한다(안한다면 메모리 누수현상(memory lack)이 발생)

    지역변수와 달리 stack이 아닌 heap에 할당된다

 

※ null포인터란?

 - null포인터(0번지, null pointer)는 포인터의 특별한 상태를 나타내기 위해 사용한다. 따라서 간접참조 연산이 불가능하다

 - null pointer는 값이 0이며, 형태는 void *형이다

 - 참조: https://ko.wikipedia.org/wiki/%EB%84%90_%ED%8F%AC%EC%9D%B8%ED%84%B0

 

2. 동적할당을 배열처럼 쓰기

 - pi는 주소(=배열의 시작주소), *pi는 가리키는 대상(즉, 배열[혹은 배열명])을 의미한다

    int *pi;

    pi=(int *)malloc(5*sizeof(int));

    scanf("%d", &pi[i]); => 혹은 scanf("%d", &(*(pi+i)));  혹은 scanf("%d", (pi+i)); 

    printf("%d", pi[i]); =>pi[i]는 *(pi+i)와 같다. 즉 pi[i]를 통해 주소를 표현하려면 &pi[i]밖에 없다

    free(pi);

 

3. calloc

 - 원형: void *calloc(unsigned int cnt, unsigned int size);

 - 의미: 할당할 크기와 개수를 곱해 그 크기만큼 할당하고 0으로 초기화

 - 사용

    int* pi;

    pi=(int *)calloc(5,sizeof(int));

    scanf("%d", &pi[i]);

    free(pi);

 

4. realloc

 - 원형: void *realloc(void *p, unsigned int size); (이전에 사용하던 저장공간의 포인터, 재할당 후 전체 저장공간의 크기)

 - 의미: p가 연결한 영역을 size바이트 크기로 재할당하고 시작주소를 반환(즉, 재할당하겠다)

 - 사용

    int* pi;

    pi=(int *)calloc(5,sizeof(int));

    pi=(int*)realloc(pi, 10*sizeof(int));

    free(pi);

 - 특징

    재할당 과정에서 메모리의 주소가 바뀔수 있다.

    이전에 동적할당된 공간에 저장된 데이터 값들을 그대로 가지고 재할당한다.

    할당불가시 널포인터를 반환한다

 

5. 임의의 크기를 입력받아 그 크기만큼 동적할당 후 저장하기

 -

    char *pi;

    char ary[80];

    gets(ary);

    pi=(char *)malloc(strlen(ary)+1); => strlen은 배열에 저장된 널문자 이전까지의 글자수를 센다 그리고 널문자 추가 위해 +1을 한다

    strcpy(pi,ary);

    printf("%s", pi);

    free(pi);

 

6. 명령행인수와 main함수

 - 명령행에서 프로그램을 실행시킬때는 프로그램의 이름 외에도 프로그램에 필요한 정보를 함께 줄 수 있는데 이를 명령행 인수(command line argument)라고한다

 - os는 명령행인수를 입력받을 때 포인터 배열에 저장하고, 실행되는 프로그램의 main에게 명령행 인수의 개수와 각 명령행 인수가 저장된 포인터 배열의 시작주소 값을 넘긴다

 - 포인터 배열은 다음과 같이 저장된다

   

    => 포인터 배열의 남은 공간을 NULL포인터로 채우는 점을 이용하기도 한다(조건문으로)

 - ex) 파일을 복사할 명령을 실행시킨다고 가정

    c:\> copy src.txt des.txt

     - 순서대로 실행파일명, 복사파일명, 복사받을 파일명이다

     - 명령행 인수는 3개 이다. 즉 실행파일명 또한 명령행 인수로 취급하고 이 3개를 모두 main에 넘긴다

 

7. 명령행 인수를 사용해 문자열을 입력받기

 - ex) 508.p 참조

    int main(int argc, char **argv)

    {

      ...

      char temp[80]; //문자열을 여기에 입력받는다

      char **str;

      str=(char **)malloc( (num+1)*sizeof(char *) ); //num은 입력받은 문자열 개수

      //포인터배열을 동적으로 할당하고 주소를 str에 저장

      gets(temp);

      if(temp[0]=='\0') break; //엔터를 누르면 버퍼에 개행저장, gets는 이를 null로 변환된다.

      //따라서 엔터만 입력하면 반복종료를 의미

      str[i]=(char *)malloc(strlen(temp)+1); //문자열을 저장하기 위한 동적영역할당

      strcpy(str[i], temp);

      i++;

      str[i]=0; //모든 입력이 끝나면 널포인터를 입력하여 끝을 표시

      whlie(str[i]!=0) //위의 끝표시(널포인터)를 조건문으로 이용

      {

        free(str[i]);

        i++;

      }

      free(str); //문자열들을 반환후, 포인터 배열도 반환

      ...

    }

 

 

 

 

17장. 사용자 정의 자료형

 

1. 구조체

 - 정의: 다양한 자료형을 하나로 묶는 복합자료형

 - 구조체 선언

    struct student  //구조체+식별자(이름)=자료형

    {

        int num; //자료형+멤버명=구조체맴버

    }; //선언이니 ;필요(함수선언처럼)

 - 변수선언: struct student s1; //s1이 자료형이다

 - 맴버접근: 구조체 변수명+맴버접근 연산자+맴버명

    s1.num=2;

    printf("%d", s1.num);

 - 구조체 변수의 크기: 바이트 얼라인먼트에 영향을 받는다(523.p참조)

    보통 짝수단위로 cpu가 설꼐되기 때문에 맴버들도 짝수단위로 배치된다

    컴파일러는 cpu가 맴버를 불러올때 여러번 부르지 않도록 정렬한다

    맴버들을 정렬하면서 패딩바이트가 발생하고 그결과 구조체의 크기가 선언한 맴버들의 전체크기보다 커지게 된다

    패팅바이트는 맴버의 순서를 재배치하여 최대한 줄일 수 있다

    #pragma pack(1); 을 선언하면 바이트 얼라인먼트가 1로 설정되어 패딩바이트가 없다.(단, rw시간이 늘어난다)

 - 구조체 변수 초기화(배열 초기화와 비슷하다) //529.p 참조

    ① struct student s1, s2, s3;

    ② struct student s1={1}, s2={2}, s3={3};

    ③ struct student max; max=s1;

 

2. 동적할당 구조체

 -

    struct student  

    {

        char * intro;

    };    

    struct student kim;

    kim.intro=(char *)mallac(80);

    gets(kim.intro);

    free(kim.intro);

 

3. 구초제를 맴버로 사용하는 구조체

 - 맴버가 될 구조체가 먼저 선언되어야한다(즉 구조체 선언순서중요)

    struct student

    {

        int id;

    };

    struct profile

    {

        struct student aa;

        int a;

    };

    struct profile kim;

    kim.aa.id=~~~;

 

4. 비트필드 구조체: 각 맴버에 사용할 수 있는 비트수를 정해놓은 구조체

 - 맴버의 자료형은 (실수가 아닌)정수형만 사용하고, 이자료형은 각 맴버가 가질 수 있는 최대 비트수를 결정한다

    (배열 또한 맴버로 사용불가능하다)

 - 기본자료형의 크기를 넘어서 맴버를 새로 할당해야 한다면 새로운 단위 블록에 할당한다(535.p참조)

 - 변수선언, 초기화, 맴버 접근방식은 일반 구조체와 같으나, 맴버에 직접 값을 입력할 수 없다

    (별도의 변수에 입력후 변수를 맴버에 대입시켜야함)

 - 맴버이름을 생략하여 선언하는 부분은 패딩비트가 된다(536.p참조)

 

5. 구조체 포인터와 ->연산자

 - 구조체변수=여러개의 맴버를 가진 변수

 - 구조체 포인터=구조체 변수를 가리키는 포인터

    ex)

    struct score kim;

    struct score *ps=&kim;

 - 구조체 맴버 접근방법

    (*ps).kor //()를 반드시 붙여야한다

    ps->kor //구조체 포인터 명만 가지고 맴버에 접근할수 있다(즉 kim의 kor맴버에 접근)

 

6. 구조체 배열

 - 여러개의 구조체를 하나의 배열로 묶음

 - 즉, 구조체의 맴버가 배열이 아닌 구조체의 변수가 요소로 구성된 배열을 의미한다

 - ex)

    struct adress list[5]; //list[5]는 변수

    초기화: struct adress list[5]={ {~~}, {~~~}, .... , {~} };

    맴버접근방법: list[3].age //list배열의 4번째요소(4번째 구조체의)의 age맴버에 접근

    접근 응용: list[3].age == (*(list+3)).age == (list+3)->age

 

7. 자기참조 구조체

 - 구조체 안에 같은 구조체를 가지는 포인터맴버가 있는 경우이다.

 - 이러한 자기참조 구조체는 구조체 포인터 맴버를 이용해 다른 변수(같은형식의 구조체)를 가리킬 수 있다.

 - 특징

    관련된 데이터를 하나로 묶어 관리할 수 있다.

    자기참조 구조체와 구조체 포인터로 연결한 것을 linkde list(연결리스트)라고 한다

     - ex)

        struct list

        {

            int num;

            struct list *next;

        }

        struct list a={10, 0}, b={20, 0};

        struct list *head=&a;

        a.next=&b;

 

        => 위의 예제를 그림으로 나타내면 아래와 같다

           

 

        => 여기에 구조체 포인터를 하나 더 사용해서(head처럼) 위 링크드리스트에 자유롭게 접근하는 역할의 포인터로 사용한다

            (head를 이용하면 처음의 위치를 찾기 어렵기 때문에 포인터를 새로 생성하여 사용한다, 551.p참조)

 

8. 공용체

 - 선언은 구조체와 같지만, 공용체의 맴버는 하나의 저장공간을 공유한다.

 - 저장공간의 크기는 공용체 맴버중 가장 큰 자료형을 따른다.

 - 단, 각 맴버의 자료형보다 큰 크기의 데이터를 각 맴버에 저장하면(공용체에 저장공간이 남아있어도) 초과되는 데이터는 잘리게 된다

 - 공용체의 초기화는 첫번째 맴버만 초기화를 하나, 다른 맴버를 초기화할 때는 '.맴버명'을 사용한다

    ex1) 다른맴버로 초기화를 할 때

    union list

    {

        int num;

        double grade;

    }

    union list s1={.grade=3.14};

    ex2) 초기화후, 다른맴버를 사용하고자 할때(=덮어쓰기)

    union list

    {

        int num;

        double grade;

    }

    union list s1={315};

    s1.grade=3.14;

    printf("%lf", s1.grade);

 

9. 열거형

 - 가독성 향상을 위해 사용. 구조체와 선언방식이 비슷하다(554~555.p참조)

 

10. typedef

 - 자료형의 이름을 재정의 해준다

    ① 기본자료형 재정의

     - ex)

        typedef unsigned int nbyte;

        nbyte aa =315;

    ② 구조체 재정의

     - ex)

        struct student

        {

            int num;

        };

        typedef struct student list;

        list s1={315}; //선언과 동시에 초기화

    ③ 형선언 동시에 재정의

     - ex)

        typedef struct

        {

            int num;

        }list;

        list s1={315};

    =>①, ②의 경우 원자료형(struct student)과 재정의된 이름(list) 둘다 사용가능, ③의 경우 재정의된 이름(list)만 사용가능

 

 

 

 

'Study > 프로그래밍_언어' 카테고리의 다른 글

시작  (0) 2020.07.22
C언어 보충6 - 18, 19장, 추가사항  (0) 2018.02.03
C언어 보충4 - 15장  (0) 2018.02.03
C언어 보충3 - 13, 14장  (0) 2018.02.02
C언어 보충2 - 11, 12장  (0) 2017.12.06

+ Recent posts