728x90

------------------------------------------------------------정리-------------------------------------------------------

구조체를 사용하면, 개별적으로 흩어진 여러 변수들의 집합으로 정보를 저장하지 않고, 관련된 모든 정보를 한 장소에 저장할 수 있다.

 

구조체를 설계할 때, 구조체와 함께 사용할 함수들의 패키지를 함께 개발하는 것이 일반적으로 유용하다. 예를 들어 구조체의 내용을 출력하고 싶을 때마다 printf를 사용하는 것이 아닌 구조체를 전달인자로 사용하는 하나의 디스플레이 함수를 작성하는 것이 좋다.

 

공용체 선언은 구조체와 비슷하다. 그러나 공용체 멤버들은 같은 메모리 공간을 공유한다. 그러나 공용체에는 한번에 한 멤버만 들어갈 수 있다. 공용체를 사용하면, 데이터형은 다양하지만 한번에 하나의 값만 넣을 수 있는 변수를 생성할 수 있다.

 

enum은 정수 기호 상수들을 정의하는 수단을 제공한다. typedef는 기본 데이터형이나 유도 데이터형에 새로운 식별자를 부여한다.

함수를 가리키는 포인터는, 어떤 함수에게 그것이 어떤 함수를 사용해야 하는지 알리는 수단을 제공한다.

 

-----------------------------------------------------------요약-----------------------------------------------------------------

C구조체는, 일반적으로 데이터형이 서로 다른 여러개의 데이터 항목들을 하나의 데이터 객체(object)에 저장하는 수단을 제공한다. 태그(tag)를 사용하여, 특정 구조체 템플릿을 식별하고 그 구조체형의 변수들을 선언할 수 있다. 도트연산자(.)를 사용하여 구조체 템플릿에 있는 레이블을 사용하여 구조체의 개별 멤버들에 접근 할 수 있다.

 

구조체를 가리키는 포인터를 선언하면, 이름과 도트연산자를 사용하는 대신에 포인터와 간접 멤버 연산자(->)를 사용하여 구조체의 개별 멤버에 접근할 수 있다. 구조체의 주소를 얻으려면 &연산자를 사용해야 한다. 배열과 달리, 구조체의 이름은 구조체의 주소 역할을 하지 않는다.

 

구조체 자체를 함수의 전달인자와 리턴값으로 사용할 수 있고, 같은 유형의 다른 구조체에 대입할 수 있다.

 

공용체는 구조체와 동일한 신택스를 사용한다. 그러나 공용체에서 멤버들은 동일한 기억공간을 공유한다. 공용체는 선택 리스트에 들어있는 한가지 데이터형만 저장한다.

 

enum을 통해 한무리의 정수 기호 상수들을 설정하고, 관련 열거형을 정의할 수 있다.

 

typedef기능을 사용하면, C의 표준 데이터형들에 다른 이름이나 약식 표기를 부여할 수 있다.

 

함수의 이름은 함수의 주소역할을 한다. 이러한 함수 주소는 다른 함수에 전달인자로 사용할 수 있다.

 

 

 

 

 

728x90

'Programming > C' 카테고리의 다른 글

C언어 공부 37  (0) 2019.07.08
C언어 공부 36  (0) 2019.07.08
C언어 공부 34  (0) 2019.06.22
C언어 공부 33  (0) 2019.06.22
C언어 공부 32  (0) 2019.06.22
728x90

함수를 가리키는 포인터는 함수 코드가 시작되는 위치의 주소를 가질 수 있다. 예를들어 int형을 가리키는 포인터는, 하나의 int형이 저장된 메모ㅓ리 공간의 주소를 가진다. 어떤 함수의 기계어 버전은 메모리에 적재된 코드들로 이루어지기 때문에, 함수들도 마찬가지로 주소를 가진다.

 

ex)

void ToUpper(char *)

ToUpper()함수의 함수형은 "하나의 char * 전달인자를 사용하고 void형을 리턴하는 함수"이다.

이 함수형을 가리키는 pf라는 이름의 포인터는 다음과 같이 선언한다.

void (*pf)(char *);

 

이 선언에서 첫번째 괄호가 * 연산자를 pf와 결합시키므로, pf는 어떤 함수를 가리키는 포인터가 된다. 이것은 (*pf)를 함수로 만들고, (char *)를 그 함수의 전달인자 리스트로 만들고, void를 그 함수의 리턴형으로 만든다. 연산자 우선순위 규칙 때문에 첫번째 괄호가 반드시 필요하다.

 

괄호가 없을때

void * pf(char *);        // pf는 포인터를 리턴하는 함수이다.

 

함수 이름이 그 함수의 주소이다.

ex)

void ToUpper(char *);

void ToLower(char *);

int round(double);

void (*pf)(char *);

pf = ToUpper;

pf = ToLower;

pf = round;                                 //불가능하다. 함수형이 다르다. round는 int형함수 pf는 void형

pf = ToLower();                            //불가능하다. ToLower()은 함수의 주소가 아니다.

 

pf는 전달인자로 char*를 사용하고 리턴형이 void인 함수만 가리킬 수 있다.

 

함수 포인터를 사용하여 함수에 접근할 수 있다.

ex)

void ToUpper(char *);

void ToLower(char *)

void (*pf)(char *);

char mis[] = "Nina Metier";

pf = ToUpper;

(*pf)(mis);                      //1.ToUpper에 mis를 전달함

pf = ToLower;

pf(mis);                          //2.ToLower에 mis를 전달함

 

1.의 경우 *pf는 ToUpper 함수이다. 따라서 (*pf)(mis)는 ToUpper(mis)와 같다.

2.의 경우 함수의 이름은 주소이기 때문에 사용자는 포인터와 함수 이름을 교환하여 사용할 수 있다. 그러므로 pf(mis)와 ToLower(mis)는 같다.

 

함수 포인터를 사용하여 함수에 접근하는 방법은(*pf)(mis), (pf(mis)) 두가지 방법 모두 가능하다.

 

함수를 함수의 전달인자로 사용

void show(void (*fp)(char *), char * str);

fp는 함수 포인터, str는 데이터 포인터이다.

fp는 char *형 전달인자를 사용하고 리턴형이 void인 어떤 함수를 가리킨다. str은 char형을 가리킨다.

위에 해당하는 함수 호툴로는

show(ToLower, mis);     //show()에서 ToLower함수를 사용

show(pf, mis);            //show()는 pf가 가리키는 함수를 사용

 

show() 함수를 사용하는법

void show(void (*fp)(char *), char * str)

{

         (*fp)(str);

         puts(str);

}

fp 전달인자로 함수를 받아오고 str에 저장된 문자열을 fp의 전달인자로 넣어준다.

 

리턴값이 있는 함수들은 다른함수들에 두가지 방식으로 전달 될 수 있다.

ex) 

function1(sqrt);                       //함수 주소를 전달한다.

function2(sqrt(4.0));                  //함수의 리턴값을 전달한다.

 

function1은 함수 내부에서 sqrt함수를 사용할 것이다.

function2는 sqrt()함수를 호출하여 실행한 리턴값을 function2에 넘겨준다.

 

 

728x90

'Programming > C' 카테고리의 다른 글

C언어 공부 36  (0) 2019.07.08
C언어 공부 35  (0) 2019.06.22
C언어 공부 33  (0) 2019.06.22
C언어 공부 32  (0) 2019.06.22
C언어 공부 31  (0) 2019.06.16
728x90

열거형(enumerated type)

열거형을 사용하여 정수 상수를 나타내는 기호 이름을 선언할 수 있다.

enum 키워드를 사용하여 새로운 데이텋형을 만들고, 그 데이터형이 가질 수 있는 값들을 지정할 수 있다.

 

열거형은 프로그램의 가독성을 높이는데 사용한다. 신택스는 구조체와 비슷하다. 

ex)

enum spectrum {red, orange, yellow, green, blue, violet};

enum spectrum color;

 

red ~ violet 처럼 여러된 상수들은 int형이지만 다른 정수형을 사용해도 상관없다.

red ~ violet은 각각 0~5까지의 값을 가진다.

 

열거된 리스트에 있는 상수들은 기본 설정으로 정수값 0,1,2,3 등으로 대입된다.

 

열거된 상수들을 사용자가 원하는 값으로 초기화 시킬 수 있다.

enum level {low = 100, medium = 500, high = 2000};

한 상수에만 값을 지정하고 그 다음 상수들에게 지정하지 않으면 그 다음 상수들에는 연속적으로 수가 매겨진다.

 

ex)

enum feline { cat, lynx = 10, puma, tiger};

일러한 선언에는 cat은 0, lynx = 10, puma = 11, tiger = 12로 초기화된다.

 

열거형의 목적은 프로그램의 가독성을 높이고 유지하기 쉽게 하는것이다.

 

 

typedef

typedef는 어떤 데이터형에 원하는 이름을 부여할 수 있는 데이터관련 고급 기능이다. #define과 비슷하지만, 세가지 차이점이 있다.

1.#define과 달리, typedef는 값이 아니라 데이터형에만 기호 이름을 부여할 수 있도록 제한된다.

2.typedef 해석은 전처리기가 아니라 컴파일러가 수행한다.

3.위 두개의 범위 내에서, typedef가 #define보다 융통성 있다.

 

typedef를 사용하여 BYTE를 char형 변수인 것 처럼 사용하기.

ex)

typedef char BYTE;

BYTE x,y[10], *z;

 

typedef 명령문이 어디에 있느냐에 따라 달라진다. typedef문이 함수 안에 들어 있으면, 범위가 지역적으로 그 함수에만 한정된다. 정의가 함수 바깥에 있으면, 범위는 전역적으로 모든 함수에 적용된다.

 

typedef char * STRING;

char형 포인터를 가리키는 포인터형을 STRING으로 사용할 수 있다. 즉 typedef를 통해 STRING은 char형을 가리키는 포인터를 의미하는 식별자가 된다.

이후 다음과 같은 선언이 가능하다.

STRING name, sign;

 

위 선언은 다음과 같다.

ex)

char *name, *sign;

 

typedef가 아닌 #define을 사용할 때

#define STRING char *

STRING name, sign;

위 선언은 char *name, sign;이 된다.

이경우에 name만 포인터가 된다.

 

 typedef를 사용하는 한가지 이유는, 자주 쓰이는 데이터형을 위한 편리하고 쉽게 인식할 수 있는 이름을 만드는 것이다.

typedef를 사용하는 두번째 이유는 복잡한 데이터형을 typedef이름으로 간단하게 표현할 수 있기 때문이다.

ex)

typedef char (*FRPTC())[5];

FRPTC를 char형 5개짜리 배열을 가리키는 포인터를 리턴하는 함수의 데이터형으로 만든다.

 

복잡한 선언        //어렵다

int board[8][8]              //int형 배열의 배열

int ** ptr;                     //int형을 가리키는 포인터를 가리키는 포인터

int *risks[10];                //int형을 가리키는 포인터 10개짜리 배열

int (*rusks)[10];            //int 10개짜리 배열을 가리키는 포인터

int * oof[3][4];            //int형을 가리키는 포인터들의 3x4 배열

int (*uuf)[3][4];           //int형 3x4배열을 가리키는 포인터

int (*uof[3])[4];           //int형 4개짜리 배열을 가리키는 포인터 3개짜리 배열

 

1.배열을 나타내는 [ ]과, 함수를 나타내는 ( )는 우선순위가 같다.

이들은 간접 연산자 *보다 우선순위가 높다.

2.[ ]과 ( )는 왼쪽에서 오른쪽으로 결합한다. 

ex) int goods[12][50];

int형 50개짜리 배열을 12개 가지는 배열을 만든다.

3.[ ] 와 ( )는 우선순위가 같다.

ex) int (*rusks)[10];

위는 rusksㄹ를 int형 10개짜리 배열을 가리키는 포인터로 만든다.

 

 

728x90

'Programming > C' 카테고리의 다른 글

C언어 공부 35  (0) 2019.06.22
C언어 공부 34  (0) 2019.06.22
C언어 공부 32  (0) 2019.06.22
C언어 공부 31  (0) 2019.06.16
C언어 공부 30  (0) 2019.06.16
728x90

Anonymous 구조체

anonyamous 구조체는 이름이 명명되지 않은 구조체인 구조체 멤버이다. 즉 중첩된 익명의 구조체 멤버 구조이다.

ex)

struct person

{

          int id;

          struct { char first[20]; char last[20];};

};

구조체 초기화

struct person ted = {8483, {"Ted", "Grass"}};

 

구조체는 하나의 객체에 여러종류의 데이터를 저장할 수 있기 때문에, 데이터 베이스를 구축하는데 쓰이는 중요한 도구이다. 데이터 베이스 파일은 데이터 객체를 원하는 만큼 가질 수 있다. 이러한 데이터 객체 즉, 구조체에 저장되는 정보의 총 집합을 레코드(record)라 부른다. 각각의 개별 항목을 필드(field)라고 부른다.

 

구조체의 저장된 정보 즉 레코드를 가장 확실하게 저장하는 방법은, fprintf()를 사용하는 것이다.

ex)

fprintf(Fstream, "%s %s   "%.2f\n",primer.title,primer.author, primer.value);

 

위의 방법은 많은 수의 멤버를 가지고 있는 구조체라면 사용하기 어렵다.

 

많은 멤버를 가진 구조체를 파일에 저장할 때 사용하기 좋은 방법은 fread() 와 fwrite()를 사용하여 구조체를 크기 단위로 읽고 쓰는것이다.

주의할 점은 fread()와 fwrite()는 바이너리 모드의 읽기와 쓰기이다.

 

fwrite(&primer, sizeof(struct book),1,Fstream);

위의 함수는 primer 시작 주소로 부터 해당 구조체 크기만큼의 모든 바이트를 Fstream에 연결된 파일에 복사하는 것이다. 위 함수 fwrite()와 fread()함수는 한번에 한 필드를 읽고 쓰는것이 아니라 하나의 레코드를 읽고 쓸 수 있다.

 

바이너리 모드에서 데이터를 저장하는데 단점은 서로 다른 시스템마다 다른 바이너리 모드를 사용할 수 있어 이식성이 낮다.

 

큐(queue), 바이너리 트리(binary tree), 힙(heap), 해시 테이블(hash table), 그래프(graph) 등과 같은 데이터 형들은 구조체를 연결하여 만든다. 일반적으로, 각 구조체는 한두개의 데이터 항목과, 같은 유형의 다른 구조체를 가리키는 한두개의 포인터를 가진다. 이 포인터들은 한 구조체를 다른 구조체에 연결하여, 상용자가 구조체들의 집합 전체를 탐색할 수 있는 경로를 제공한다.

 

공용체

공용체(union)는 같은 메모리 공간에 서로 다른 데이터형들을 저장할 수 있게 한다. 공용체의 용도는, 순서가 규칙적이지 않고 미리 알 수도 없는 데이터형들의 혼합을 저장하도록 설계된 테이블이다.

 

공용체는 구조체와 같은 방식으로 설정한다.

ex)

union hold{

        int digit;

        double bigfl;

        char letter;

};

 

위와 같은 형태의 구조체는 int형 값 1개, double 형 값 1개, char형 값 1개 총 3개의 값을 저장할 수 있지만 union 공용체는 하나의 값 int 또는 double 또는 char형 중 하나의 데이터형태의 하나의 값을 저장할 수 있습니다.

 

hold형 공용체 변수 정의 예시

ex)

union hold fit;

union hold save[10];

union hold * pu;

 

공용체의 경우 컴파일러는 크기가 가장 큰 멤버를 저장할 수 있을 만큼의 공간을 할당 합니다. 공용체 hold의 경우 가장 크기가 큰 멤버는 double 형 이므로 8바이트 메모리를 할당받습니다. 두번째 선언인 고용용체 배열은 가장 큰 멤버의 크기 double형의 메모리공간 8byte 10개짜리 메모리공간을 할당받습니다. 세번째는 공용체 주소를 담을 메모리 공간입니다.

 

공용체 사용

ex)

fit.digit = 23;                     //23이 fit에 저장된다.

fit.bigfl = 2.0;                    //2.0이 23을 대체한다. 8바이트 사용

fit.letter = 'h';                   //'h'가 2.0을 대체한다. 1바이트 사용

 

도트(.) 연산자를 통해 각 멤버의 데이터형을 사용할 수 있다. 한번에 하나의 값만 저장된다. 저장할 공간이 충분하더라도 하나의 값만 사용할 수 있다.

 

공용체 포인터와 구조체 포인터를 사용하는 방법은 같다.

pu = &fit;

x = pu -> digit;

728x90

'Programming > C' 카테고리의 다른 글

C언어 공부 34  (0) 2019.06.22
C언어 공부 33  (0) 2019.06.22
C언어 공부 31  (0) 2019.06.16
C언어 공부 30  (0) 2019.06.16
C언어 공부 29  (0) 2019.06.16
728x90

구조체를 함수에서 사용하기

함수의 전달인자로 구조체 자체를 전달할 것인지, 아니면 구조체를 가리키는 포인터를 전달할 것인지 사용자가 선택할 수 있다. 또는 구조체 멤버를 전달인자로 사용할 수 있다.

 

피호출 함수에서 호출함수에 있는 구조체 값에 영향을 주고자 한다면 구조체의 주소나 구조체 멤버의 주소를 전달인자로 넘겨주면 된다. 구조체 주소를 얻기 위해서는 &를 사용해야 한다. 배열 이름과 다르게 구조체 이름은 주소가 아니다.

 

구조체는 같은 데이터형 구조체간에 대입하는 것을 허용한다. 즉 A_data 와 B_data가 같은 데이터형 구조체라면 다음과 같이 대입할 수 있다.

ex) A_data = B_data;

이것은 A_data의 각 멤버에 B_data의 각 멤버의 값이 대입되게 한다.

또한 같은 데이터형의 구조체라면, 한 구조체를 다른 구조체로 초기화 할 수 있다.

struct names right_field = {"Ruthine","George"};

struct names captain = right_field;

 

구조체를 함수의 전달인자로 사용할 수 있고 함수의 리턴값으로도 리턴할 수 있다. 구조체를 함수의 전달인자로 사용하면, 함수에 구조체 정보를 전달할 수 있다. 구조체를 함수의 리턴값으로 사용하면, 피호출 함수로부터 호출 함수로 구조체 정보를 전달할 수 있다.

 

구조체 포인터를 전달인자로 사용하는 것은 주소 하나만 전달하기 때문에 빠르다. 그러나 피호출 함수에서 수행하는 특정 동작에 따라 원본 구조체에 들어있는 값에 영향을 줄 수 있다. 이러한 문제는 함수 전달인자에 const를 넣어 해결 할 수 있다.

 

구조체 안에 문자열을 저장해야 한다면, 문자 배열 멤버를 사용하라. 구조체 안에 char형 포인터는 값 저장을 엉뚱한 곳에 저장하려고 시도할 수 있어 프로그램이 먹통이 될 수 있다.

 

구조체안에 문자열 포인터를 사용하는 바람직한 방법은 malloc()을 사용하여 메모리를 할당하고, 그 주소를 포인터에 보관하는 방법이다.

 

복합 리터럴은 이름이 없는 오브젝트 즉 명명되지 않은 데이터 객체이다. 이러한 복합 리터럴은 함수의 전달인자로 사용되는 구조체나 다른 구조체에 대입되는 구조체를 생성하는데 사용할 수 있다. 복합 리터럴을 사용하여 구조체를 만들 때는 초기화 값 리스트에 적용되는 것과 동일한 신택스 규칙이 복합 리터럴에 적용 되어야 한다.

 

플렉서블 배열 멤버(flexible array member)

구조체의 마지막 멤버가 특별한 속성을 가지는 배열을 가질 수 있다. 이러한 배열 멤버를 플렉서블 배열 멤버라고 한다. 이 배열의 첫번째 특별한 속성은, 플렉서블 배열 멤버가 지금 당장은 존재하지 않는 배열이라는 것이다.

두번째 특별한 속성은, 플렉서블 배열 멤버를 마치 그것이 존재하는 것처럼 그리고 필요한 만큼 원소를 가지고 있는 것처럼, 적당한 코드와 함께, 사용할 수 있다는 것이다.

 

함수의 전달인자로 구조체를 활용할 때는 구조체 멤버를 전달, 구조체 주소로 전달, 구조체 자체를 전달 할 수 있다.

 

플렉서블 배열 멤버를 선언하는 규칙

- 플렉서블 배열 멤버는 구조체의 마지막 멤버가 되어야 한다.

- 다른 멤버가 최소한 하나 있어야 한다.

- 플렉서블 배열은, 각괄호 안을 비워 놓는다는 것을 제외하고, 보통의 배열처럼 선언한다.

ex)

struct flex

{

     int count;

     double average;

     double scores[];

};

 

struct flex 형 변수를 선언하더라도, scores를 사용할 수 없다. scores를 위한 메모리가 할당되지 않았기 때문이다.

이러한 플렉서블 배열 멤버가 있는 구조체는 변수 선언을 의도한 것이 아니라 struct flex형을 가리키는 포인터를 선언하고, struct flex형의 정규 내용들을 저장할 공간과 플렉서블 배열 멤버를 위해 원하는 만큼의 공간을 할당 받기위해 malloc()을 사용하려고 의도하는 것이다.

 

예를 들어 double 형 값 5개 짜리 배열을 scores로 나타내기 위해서

ex)

struct flex *pf;

pf = malloc(sizeof(struct flex)+5*sizeof(double));

로 나타낼 수 있다.

malloc()할당을 통해 count, average, double형 값 5개짜리 배열을 저장할 수 있는 메모리 덩어리가 확보된다. 이후 각 멤버에 접근할 때는 포인터 pf를 사용하여 접근하면 된다.

pf->count = 5;

pf->scores[2] = 18.5;

 

felxible 배열 멤버를 가지고 있는 구조체를 다룰 때는 구조체 대입을 하면 안된다.

ex)

struct flex *pf1, *pf2;

*pf2 = *pf1;

위와 같이 하면 안된다. 위와 같은 대입은 flexible 멤버를 제외한 멤버들만 복사할 것이다. 값을 대입하고 싶을 때는 대입연산자가 아닌 memcpy()를 사용하라.

 

fexible 멤버가 있는 구조체는 구조체 값을 전달하는 함수와 함께 사용하면 안된다. 값을 전달인자로 전달하는 것은 대입과 같다.

flexible 멤버가 있는 구조체를 다른 구조체의 멤버로 사용할 수 없다.

728x90

'Programming > C' 카테고리의 다른 글

C언어 공부 33  (0) 2019.06.22
C언어 공부 32  (0) 2019.06.22
C언어 공부 30  (0) 2019.06.16
C언어 공부 29  (0) 2019.06.16
C언어 공부 28  (0) 2019.06.12
728x90

구조체

 

구조체는 특별 배열이다. 하나의 배열에 int형 float형 char형 등등의 데이터가 구분되어 유지된다.

 

구조체 선언

struct book{

       char    title[MAXTITL];

       char    author[MAXAUTL];

       float    value;

};

 

위 선언은 문자 배열 2개와 float 형 변수 1개로 구성된 구조체이다.

위 선언은 실제 데이터 객체를 생성하지 않고, 구조체 구성만 나타낸다. 컴파일러에게 데이터 표현 방법을 알려주지만, 데이터를 위한 기억공간을 할당하지 않는다.

 

구조체 변수(struct variable)

struct book library;

위 문장을 만났을 때 컴파일러는 변수 library를 생성한다. library는 MAXTITL개의 char형 배열과 MAXAUTL개의 char형 배열 , 하나의 float형 변수를 위한 메모리를 할당 받는다. 이것은 library라는 이름으로 한 덩어리를 이룬다.

 

구조체 개별 멤버에 접근하기 위해서 구조체 멤버연산자인 도트(.)를 사용한다. 각 변수에 접근하여 각 변수 데이터형과 동일한 방식으로 사용할 수 있다.

 

구조체 배열 선언

struct book library[MAXBKS];

MAXBKS 개의 원소를 가지는 배열 library를 선언한다. 각각의 원소들은 book형 구조체이다.

 

구조체 배열에서 멤버 식별하기

library[0].value

library[5].title

위와 같이 멤버에 접근 할 수 있다.

 

각원소 하나하나가 구조체 이기 때문에 위와 같이 원소를 통해 해당 구조체를 가리키고 구조체 연산자 도트(.)와 해당멤버의 이름으로 접근하면 된다.

 

library[2].title[4]

위의 값은 library배열의 3번쨰 원소의 title멤버의 5번째 원소를 가리킨다. 즉 하나의 값이다. 구조체도 배열이다. 그렇기 때문에 위와 같이 접근이 가능하다. 도트 연산자 왼쪽 인덱스는 구조체 배열에 적용된다.

->

library

library[2]

library[2].title

library[2].title[4]

위와 같은 순서로 접근한다.

 

구조체 포인터

구조체를 특별배열이라고 했지만 배열의 경우와 다르게 구조체의 이름은 구조체의 주소가 아니다.

struct guy * him;

위처럼 구조체 포인터를 선언해 줄 수 있다.

이후에 구조체 포인터에 포인터 주소를 넣어주면 된다.

him = &barney;

 

구조체 배열을 구조체 포인터에 넣어줄때는

him = &fellow[0];

위처럼 하면 된다. him이 fellow[0]을 가리키고 him+1이 fellow[1]을 가리키게 된다. 구조체 배열 원소 하나의 크기만큼 증가한다.

 

구조체 포인텅서 멤버로 접근하는 방법

1.새로운연산자 -> 를 사용한다.

위의 예시를 빌리면

him->income은 him = &barney의 경우 barney.incom이다.

him == fellow[0]의 경우 him->income은 fellow[0].income이다. 요약하면, 구조체 포인터 뒤에 ->연산자가 오는 것은 구조체 이름에 도트(.)를 쓰는 것과 같다.

 

2.&와 *을 사용한다.

him == &fellow[0]이면, *him == fellow[0]이다. 이것은 &와 *가 서로 상반되는 연산자이기 때문에 가능하다. 이를 통해 멤버로 접근할 수 있다.

fellow[0].income == (*him).income

도트 연산자(.)가 *연산자보다 우선순위가 높아 괄호가 필요하다. him이 barney구조체의 포인터라면

barney.incom == (*him).income == him->income

위 방법들을 통해 멤버에 접근할 수 있다.

 

728x90

'Programming > C' 카테고리의 다른 글

C언어 공부 32  (0) 2019.06.22
C언어 공부 31  (0) 2019.06.16
C언어 공부 29  (0) 2019.06.16
C언어 공부 28  (0) 2019.06.12
C언어 공부 27  (0) 2019.06.12
728x90

가장 정확하고 일관되게 수를 저장하는 방법은, 프로그램이 사용하는 비트 패턴과 동일한 비트 패턴을 사용하는 것이다. 그러므로, double형 값은 double 형 크기 하나의 단위로만 저장되어야 한다. 프로그램에서 사용하는 표현과 동일한 표현으로 데이터를 파일에 저장할 때, 우리는 그 데이터가 바이너리 형태(binary form)로 저장된다고 말한다.

fread()와 fwrite()함수가 위와 같은 바이너리 형태의 읽고 쓰는 것을 제공한다.

 

실제로는, 모든 데이터가 바이너리 형태로 저장된다. 문자들도 그 문자 코드의 바이너리 표현을 사용하여 저장된다. 그러나 파일에 있는 모든 데이터가 문자 코드로 해석된다면, 우리는 그 파일이 텍스트 데이터를 가지고 있다고 말한다. 데이터의 일부 또는 전체가 바이너리 형태의 수치 데이터로 해석된다면, 우리는 그 파일이 바이너리 데이터를 가지고 있다고 말한다.

 

size_t fwrite()함수

fwrite()함수는 다음과 같은 프로토 타입을 가진다.

size_t fwrite(const void * restrict ptr, size_t size, size_t nmemb, FILE * restrict fp);

fwrite()함수는 바이너리 데이터를 파일에 저장한다. size_t 형은 C의 표준 데이터형을 사용하여 정의된다. 이 데이터형은 sizeof 연산자에 의해 리턴되는데, 일반적으로 unsigned int형이다. 포인터 ptr는 저장할 데이터 덩어리의 주소이다. 또한 size는 저장할 그 데이터 덩어리들의 크기(바이트 단위)를 나타낸다. nmemb는 저장할 데이터 덩어리들의 수를 나타낸다. fp는 그 데이터가 저장될 파일을 나타낸다.

ex)

char buffer[256];

fwrite(buffer,256,1,fp);

위 호출은 크기가 256인 데이터 덩어리 하나를 buffer로 부터 파일에 저장한다.

 

10개의 double형 값으로 이루어진 배열을 저장하려면, 다음과 같이 할 수 있다.

ex) 

double earnings[10];

fwrite(earnings,size of(double), 10, fp);

이 호출은 10개의 데이터 덩어리를 earnings 배열로부터 파일로 저장한다. 이때 각각의 데이터 덩어리는 double형 크기를 가진다.

 

fwrite()함수는 성공적으로 저장한 항목의 수를 리턴한다. 일반적으로 이 값은 nmemb와 같다.

 

size_t fread()함수

fread()함수는 다음과 같은 프로토 타입을 가진다.

size_t fread(void * restrict ptr, size_t size, size_t nmemb, FILE * restrict fp);

fread()함수는, fwrite()함수가 사용하는 것과 동일한 집합의 전달인자들을 사용한다. 이번에는 ptr이 파일로 부터 읽은 데이터를 저장할 메모리 공간의 주소다. fp는 데이터를 읽을 파일을 나타낸다. fwrite()를 사용하여 파일에 저장된 데이터를 다시 읽기 위해 이 함수를 사용할 수 있다.

 

앞의 예에서 저장된 10개의 double 형 값으로 이루어진 배열을 다시 읽으려면 다음과 같이 호출할 수 있다.

double earnings[10];

fread(earnings,sizeof(double),10,FILE * fp);

이 호출은 double형 크기의 10개의 값을 읽어 earnings 배열에 복사한다.

 

fread() 함수는 성공적으로 읽은 항목의 수를 리턴한다. 일반적으로 이 값은 nmemb와 같다.

 

int feof(FILE * fp)와 int ferror(FILE * fp)함수

 

표준 입력 함수들이 EOF를 리턴할 때, 일반적으로 그것은 파일의 끝에 도달했다는 것을 의미한다. 그러나 이것은 읽기 에러가 발생했다는 것을 의미할 수도 있다. feof()와 ferror()함수를 사용하면 이 두가지 가능성을 구별 할 수 있다.

feof()함수는 마지막 입력 호출에서 파일의 끝을 만나면 0이 아닌 값을 리턴한다. 그렇지 않으면 0을 리턴한다. 

ferror()함수는 읽기 에러나 쓰기 에러가 발생하면 0이 아닌 값을 리턴한다.

 

바이너리 모드의 파일 입출력을 해야한다면 fread()와 fwrite()함수를 바이너리 모드로 사용하라.

텍스트 모드의 텍스트 입출력을 해야한다면 getc(), putc(), fscanf(), fprintf()같은 함수들을 텍스트 모드로 사용하라.

 

어떤 파일에 접근하려면 파일 포인터(FILE *)를 생성하고 이 포인터를 특정 파일 이름과 연결하라. 이후 파일을 다룰 때 파일 이름이 아니라 파일 포인터를 사용하면 된다.

 

'파일의 끝'에 대한 검사는 읽기 시도 직후에 이루어져야 한다. 이유는 C의 입력 함수들은 파일의 끝을 읽고 나서야 그것이 파일의 끝에 도달했다는 것을 인식하기 때문이다.

ex)

int ch;

FILE * fp;

fp = fopen("abc.txt", "r");

ch = getc(fp);

while(ch != EOF)

{

   putchar(ch);

   ch = getc(fp);

}

 

,

int ch;

FILE * fp;

fp = fopen("abc.txt","r");

while((ch = getc(fp)) != EOF)

{

       putchar(ch);

}

 

C프로그램은 입력을 바이트들의 스트림으로 인식한다. 파일, 입력장치, 다른 프로그램의 출력이 스트림의 소스가 될 수 있다.

C프로그램은 출력을 바이트 스트림으로 인식한다. 파일, 비디오, 디스플레이 등이 출력스트림의 타깃이 될 수 있다.

 

fread()와 fwrite() 함수는 바이너리 모드에서 사용된다. 텍스트 모드에선 getc(), putc(), fprintf(), fscanf(), fgets(), fputs() 등을 사용하라.

728x90

'Programming > C' 카테고리의 다른 글

C언어 공부 31  (0) 2019.06.16
C언어 공부 30  (0) 2019.06.16
C언어 공부 28  (0) 2019.06.12
C언어 공부 27  (0) 2019.06.12
C언어 공부 26  (0) 2019.06.11
728x90

파일 입력은 stdio.h에 선언되어 있는 fscanf(), getc(), fgets()와 같은 입력 함수들 중 하나를 호출하는 것이다. 이 함수들 중 하나를 호출하면, 한덩어리의 데이터가 파일로부터 버퍼가 복사된다. 일반적으로 512바이트이거나 그것의 배수인 4,096바이트 또는 16,384바이트 이다. 최초의 함수 호출은, 버퍼를 채울 뿐 아니라 fp가 가리키는 구조체에 있는 값들을 설정한다. 특별히, 스트림에서의 현재 위치, 버퍼 안으로 복사된 바이트 수가 설정된다. 일반적으로 현재 위치는 바이트 0부터 시작한다.

 

데이터 구조체와 버퍼가 초기화된 후, 입력 함수는 요청된 데이터를 버퍼로부터 읽는다. 이 과정에서 파일 위치 표시자는 마지막으로 읽은 문자 바로 다음 문자를 가리키도록 설정된다. stdio.h계열의 모든 입력 함수들이 같은 버퍼를 사용하기 때문에, 그들 중 어느 한 함수의 호출은 직전에 이루어진 어느 함수 호출이 행동을 끝낸 위치에서부터 시작한다.

 

버퍼에 있는 모든 문자들을 읽었다는 것을 알게 되었을 때, 입력 함수는 버퍼 크기만큼의 그 다음  데이터 덩어리를 파일로부터 버퍼로 복사하도록 요청한다. 이와 같은 방법으로, 입력 함수들은 파일의 끝까지 파일의 모든 내용을 읽을 수 있다. 마지막 버퍼만큼의 데이터 덩어리에 있는 마지막 문자를 읽은 후에, 입력 함수는 파일 끝지시자를 참으로 설정한다. 그러고 나면 입력 함수의 다음번 호출은 EOF를 리턴한다.

 

출력 함수도 버퍼에 저장한다. 버퍼가 가득 차을 때, 데이터는 파일로 복사된다.

 

int fflush()함수

fflush()함수는 다음과 같은 프로토 타입을 가진다. 

int fflush(FILE * fp);

fflush()함수를 호출하는 것은, 출력 버퍼에 아직 남아있는 데이터를 fp가 가리키는 출력 파일로 보낸다. 이 과정을 버퍼 비우기(flusing buffer)라 한다. fp가 널 포인터면, 모든 출력 버퍼들을 비운다. fflush()를 입력 스트림에 사용하는 것의 영향은 정의되지 않는다.

 

int setvbuf() 함수

setvbuf() 함수는 다음과 같은 프로토타입을 가진다.

int setvbuf(FILE * restrict fp, char * restrict buf, int mode, size_t size);

setvbuf()함수는 표준 입출력 함수들이 사용할 또하나의 버퍼를 설정한다. 이 함수는, 파일을 열고 난 후 그 스트림에 어떤 다른 조작이 가해지기 전에 호출된다. 포인터 fp는 스트림을 나타낸다. buf는 사용할 버퍼를 가리킨다. buf의 값으로 NULL을 사용하지 않는다면, 사용자가 버퍼를 만들어야 한다. 예를 들어, 1024개의 char형으로 이루어진 배열을 선언하고, 그 배열의 주소를 전달할 수 있다. 그러나 buf의 값으로 NULL을 사용한다면, 함수가 직접 버퍼를 할당한다. size 변수는 배열의 크기가 얼마인지 setvbuf()에게 알려준다. mode는 _IOFBF(완전 버퍼링; 버퍼가 가득 찼을때 버퍼를 비운다.) _IOLBF(라인 버퍼링; 버퍼가 가득 찼거나 개행이 쓰여졌을 때 버퍼를 비운다.) _IONBF(비 버퍼링; 버퍼를 사용하지 않는다.)중 어느 하나를 선택한다. 이 함수는 성공하면 0을 리턴하고, 실패하면 0이 아닌값을 리턴한다.

 

크기가 각각 3000바이트인 저장된 데이터 객체들을 다루는 프로그램이 있다고 가정하자. setvbuf()를 사용하면, 버퍼 크기가 데이터 객체의 크기와 일치하는 버퍼를 만들 수 있다.

728x90

'Programming > C' 카테고리의 다른 글

C언어 공부 30  (0) 2019.06.16
C언어 공부 29  (0) 2019.06.16
C언어 공부 27  (0) 2019.06.12
C언어 공부 26  (0) 2019.06.11
C언어 공부 25  (0) 2019.06.11

+ Recent posts