C Language
구조물
수색…
소개
구조는 다양한 유형의 관련 변수 세트를 단일 메모리 단위로 그룹화하는 방법을 제공합니다. 구조체는 전체적으로 하나의 이름이나 포인터로 참조 될 수 있습니다. 구조체 멤버도 개별적으로 액세스 할 수 있습니다. 구조체는 함수에 전달되고 함수에서 반환 될 수 있습니다. 키워드 struct
사용하여 정의됩니다.
간단한 데이터 구조
구조 데이터 유형은 관련 데이터를 패키지화하고 단일 변수처럼 작동하도록하는 데 유용합니다.
두 int
멤버를 보유하는 간단한 struct
선언 :
struct point
{
int x;
int y;
};
x
와 y
는 point
구조체의 멤버 (또는 필드 )라고합니다.
구조체 정의 및 사용 :
struct point p; // declare p as a point struct
p.x = 5; // assign p member variables
p.y = 3;
구조체는 정의시 초기화 될 수 있습니다. 위 내용은 다음과 같습니다.
struct point p = {5, 3};
구조체는 지정된 초기화 프로그램을 사용하여 초기화 될 수도 있습니다.
필드에 액세스하는 작업은 .
운영자
printf("point is (x = %d, y = %d)", p.x, p.y);
Typedef 구조체
struct
와 typedef
를 결합하면 코드가 명확 해집니다. 예 :
typedef struct
{
int x, y;
} Point;
반대로 :
struct Point
{
int x, y;
};
다음과 같이 선언 될 수 있습니다 :
Point point;
대신에:
struct Point point;
다음을 사용하는 것이 더 좋습니다.
typedef struct Point Point;
struct Point
{
int x, y;
};
의 가능한 정의를 모두 이용해야하는 point
. 이러한 선언은 C ++을 처음 배운 경우 가장 편리합니다. 이름이 모호하지 않으면 struct
키워드를 생략 할 수 있습니다.
구조체의 typedef
이름이 프로그램의 다른 부분의 다른 식별자와 충돌 할 수 있습니다. 어떤 사람들은 이것을 단점이라고 생각하지만, struct
와 다른 식별자를 가진 대부분의 사람들은 꽤 혼란 스럽습니다. 악명 높은 POSIX ' stat
int stat(const char *pathname, struct stat *buf);
struct stat
라는 하나의 인수를 가진 함수 stat
를 볼 수 있습니다.
태그 이름이없는 typedef
구조체는 항상 struct
선언문을 사용하는 코드에서 볼 수 있습니다. 그런 다음 전체 struct
선언을 헤더 파일에 배치해야합니다.
중히 여기다:
#include "bar.h"
struct foo
{
bar *aBar;
};
따라서 태그 이름이없는 typedef
struct
에서는 bar.h
파일에 항상 bar
의 전체 정의가 포함되어야 bar
. 우리가 사용하면
typedef struct bar bar;
bar.h
에서 bar
구조의 세부 정보를 숨길 수 있습니다.
Typedef를 보라.
구조체에 대한 포인터
struct
포함하는 변수가 있으면 도트 연산자 ( .
)를 사용하여 해당 필드에 액세스 할 수 있습니다. 그러나 struct
대한 포인터가 있으면이 작동하지 않습니다. 필드에 액세스하려면 화살표 연산자 ( ->
)를 사용해야합니다. 다음은 struct
대한 포인터를 사용하고 화살표 연산자를 보여주는 스택을 매우 단순하게 구현 한 예입니다 (일부는 "끔찍하고 간단하게"구현할 수 있음).
#include <stdlib.h>
#include <stdio.h>
/* structs */
struct stack
{
struct node *top;
int size;
};
struct node
{
int data;
struct node *next;
};
/* function declarations */
int push(int, struct stack*);
int pop(struct stack*);
void destroy(struct stack*);
int main(void)
{
int result = EXIT_SUCCESS;
size_t i;
/* allocate memory for a struct stack and record its pointer */
struct stack *stack = malloc(sizeof *stack);
if (NULL == stack)
{
perror("malloc() failed");
return EXIT_FAILURE;
}
/* initialize stack */
stack->top = NULL;
stack->size = 0;
/* push 10 ints */
{
int data = 0;
for(i = 0; i < 10; i++)
{
printf("Pushing: %d\n", data);
if (-1 == push(data, stack))
{
perror("push() failed");
result = EXIT_FAILURE;
break;
}
++data;
}
}
if (EXIT_SUCCESS == result)
{
/* pop 5 ints */
for(i = 0; i < 5; i++)
{
printf("Popped: %i\n", pop(stack));
}
}
/* destroy stack */
destroy(stack);
return result;
}
/* Push a value onto the stack. */
/* Returns 0 on success and -1 on failure. */
int push(int data, struct stack *stack)
{
int result = 0;
/* allocate memory for new node */
struct node *new_node = malloc(sizeof *new_node);
if (NULL == new_node)
{
result = -1;
}
else
{
new_node->data = data;
new_node->next = stack->top;
stack->top = new_node;
stack->size++;
}
return result;
}
/* Pop a value off of the stack. */
/* Returns the value popped off the stack */
int pop(struct stack *stack)
{
struct node *top = stack->top;
int data = top->data;
stack->top = top->next;
stack->size--;
free(top);
return data;
}
/* destroy the stack */
void destroy(struct stack *stack)
{
/* free all pointers */
while(stack->top != NULL)
{
pop(stack);
}
}
유연한 배열 멤버
타입 선언
적어도 하나의 부재 를 갖는 구조물은 구조물의 끝에 불특정 길이의 단일 배열 부재를 부가 적으로 포함 할 수있다. 이를 유연한 배열 구성원이라고합니다.
struct ex1
{
size_t foo;
int flex[];
};
struct ex2_header
{
int foo;
char bar;
};
struct ex2
{
struct ex2_header hdr;
int flex[];
};
/* Merged ex2_header and ex2 structures. */
struct ex3
{
int foo;
char bar;
int flex[];
};
크기 및 여백에 미치는 영향
유연한 배열 멤버는 구조체의 크기를 계산할 때 크기가없는 것으로 취급되지만 해당 구조체의 이전 멤버와 패딩 사이에 여전히 여백이있을 수 있습니다.
/* Prints "8,8" on my machine, so there is no padding. */
printf("%zu,%zu\n", sizeof(size_t), sizeof(struct ex1));
/* Also prints "8,8" on my machine, so there is no padding in the ex2 structure itself. */
printf("%zu,%zu\n", sizeof(struct ex2_header), sizeof(struct ex2));
/* Prints "5,8" on my machine, so there are 3 bytes of padding. */
printf("%zu,%zu\n", sizeof(int) + sizeof(char), sizeof(struct ex3));
가변 배열 구성원은 불완전한 배열 유형으로 간주되므로 sizeof
사용하여 크기를 계산할 수 없습니다.
용법
플랙시 블 배열 멤버가 포함 된 구조체 유형을 사용하여 객체를 선언하고 초기화 할 수 있지만 플랙시 블 배열 멤버가없는 것처럼 처리되므로 플랙시 블 배열 멤버를 초기화하지 마십시오. 이 작업을 시도하는 것은 금지되어 있으며 컴파일 오류가 발생합니다.
마찬가지로, 유연한 배열 멤버가 요구하는 객체를 허용하기 위해 구조 끝에 충분한 패딩이 없기 때문에 이러한 방식으로 구조를 선언 할 때 가변 배열 멤버의 모든 요소에 값을 할당하지 마십시오. 그러나 컴파일러가이 작업을 수행하지 못하게 할 수는 없으므로 정의되지 않은 동작이 발생할 수 있습니다.
/* invalid: cannot initialize flexible array member */
struct ex1 e1 = {1, {2, 3}};
/* invalid: hdr={foo=1, bar=2} OK, but cannot initialize flexible array member */
struct ex2 e2 = {{1, 2}, {3}};
/* valid: initialize foo=1, bar=2 members */
struct ex3 e3 = {1, 2};
e1.flex[0] = 3; /* undefined behavior, in my case */
e3.flex[0] = 2; /* undefined behavior again */
e2.flex[0] = e3.flex[0]; /* undefined behavior */
대신 malloc
, calloc
또는 realloc
을 사용하여 여분의 저장 공간이있는 구조체를 할당 한 다음 나중에 해제 할 수 있습니다. 이렇게하면 유연한 배열 구성원을 원하는대로 사용할 수 있습니다.
/* valid: allocate an object of structure type `ex1` along with an array of 2 ints */
struct ex1 *pe1 = malloc(sizeof(*pe1) + 2 * sizeof(pe1->flex[0]));
/* valid: allocate an object of structure type ex2 along with an array of 4 ints */
struct ex2 *pe2 = malloc(sizeof(struct ex2) + sizeof(int[4]));
/* valid: allocate 5 structure type ex3 objects along with an array of 3 ints per object */
struct ex3 *pe3 = malloc(5 * (sizeof(*pe3) + sizeof(int[3])));
pe1->flex[0] = 3; /* valid */
pe3[0]->flex[0] = pe1->flex[0]; /* valid */
'struct hack'
유연한 배열 구성원은 C99 이전에는 존재하지 않았으므로 오류로 처리됩니다. 일반적인 해결 방법은 'struct hack'이라는 기법 인 길이 1의 배열을 선언하는 것입니다.
struct ex1
{
size_t foo;
int flex[1];
};
그러나 실제 플렉시블 배열 멤버와는 달리 구조의 크기에 영향을 미칩니다.
/* Prints "8,4,16" on my machine, signifying that there are 4 bytes of padding. */
printf("%d,%d,%d\n", (int)sizeof(size_t), (int)sizeof(int[1]), (int)sizeof(struct ex1));
flex
멤버를 유연한 배열 멤버로 사용하려면 sizeof(*pe1)
(또는 이와 동등한 sizeof(struct ex1)
)가 offsetof(struct ex1, flex)
로 대체된다는 점을 제외하고는 위와 같이 malloc
을 사용하여 할당합니다. 또는 유형 불가지론 적 표현식 sizeof(*pe1)-sizeof(pe1->flex)
. 또는 원하는 길이가 0보다 큰 경우 구조체 크기에 이미 포함되어 있으므로 "유연한"배열의 원하는 길이에서 1을 뺄 수 있습니다. 다른 논리의 사용 예에도 같은 논리가 적용될 수 있습니다.
적합성
유연한 배열 구성원을 지원하지 않는 컴파일러와의 호환성이 필요한 경우 아래 FLEXMEMB_SIZE
와 같이 정의 된 매크로를 사용할 수 있습니다.
#if __STDC_VERSION__ < 199901L
#define FLEXMEMB_SIZE 1
#else
#define FLEXMEMB_SIZE /* nothing */
#endif
struct ex1
{
size_t foo;
int flex[FLEXMEMB_SIZE];
};
객체를 할당 할 때는 가변 길이 배열 멤버와 컴파일러를 지원하는 컴파일러간에 일관성을 유지할 유일한 표현이기 때문에 offsetof(struct ex1, flex)
형식을 사용하여 구조체 크기를 참조해야합니다 (가변 배열 구성원 제외). 아니:
struct ex1 *pe10 = malloc(offsetof(struct ex1, flex) + n * sizeof(pe10->flex[0]));
또 다른 방법은 전처리기를 사용하여 조건부로 지정된 길이에서 1을 빼는 것입니다. 이 양식에서 불일치와 일반적인 사람의 실수 가능성이 커지면서 논리를 분리 된 기능으로 옮겼습니다.
struct ex1 *ex1_alloc(size_t n)
{
struct ex1 tmp;
#if __STDC_VERSION__ < 199901L
if (n != 0)
n--;
#endif
return malloc(sizeof(tmp) + n * sizeof(tmp.flex[0]));
}
...
/* allocate an ex1 object with "flex" array of length 3 */
struct ex1 *pe1 = ex1_alloc(3);
구조체를 함수에 전달
C에서 모든 인수는 구조체를 포함하여 값에 의해 함수에 전달됩니다. 작은 구조체의 경우 이것은 포인터를 통해 데이터에 액세스하는 데 오버 헤드가 없다는 것을 의미하므로 좋은 방법입니다. 그러나 프로그래머가 인수를 참조로 전달하는 다른 언어에 익숙한 경우 특히 실수로 거대한 구조체를 전달하여 성능이 저하 될 수 있습니다.
struct coordinates
{
int x;
int y;
int z;
};
// Passing and returning a small struct by value, very fast
struct coordinates move(struct coordinates position, struct coordinates movement)
{
position.x += movement.x;
position.y += movement.y;
position.z += movement.z;
return position;
}
// A very big struct
struct lotsOfData
{
int param1;
char param2[80000];
};
// Passing and returning a large struct by value, very slow!
// Given the large size of the struct this could even cause stack overflow
struct lotsOfData doubleParam1(struct lotsOfData value)
{
value.param1 *= 2;
return value;
}
// Passing the large struct by pointer instead, fairly fast
void doubleParam1ByPtr(struct lotsOfData *value)
{
value->param1 *= 2;
}
구조체를 사용한 객체 기반 프로그래밍
구조체는 객체 지향 방식으로 코드를 구현하는 데 사용될 수 있습니다. 구조체는 클래스와 비슷하지만 일반적으로 클래스의 일부가되는 함수가 누락되어 있습니다. 함수 포인터 멤버 변수로 추가 할 수 있습니다. 좌표 예제를 유지하려면 다음을 수행하십시오.
/* coordinates.h */
typedef struct coordinate_s
{
/* Pointers to method functions */
void (*setx)(coordinate *this, int x);
void (*sety)(coordinate *this, int y);
void (*print)(coordinate *this);
/* Data */
int x;
int y;
} coordinate;
/* Constructor */
coordinate *coordinate_create(void);
/* Destructor */
void coordinate_destroy(coordinate *this);
그리고 이제 C 파일을 구현합니다.
/* coordinates.c */
#include "coordinates.h"
#include <stdio.h>
#include <stdlib.h>
/* Constructor */
coordinate *coordinate_create(void)
{
coordinate *c = malloc(sizeof(*c));
if (c != 0)
{
c->setx = &coordinate_setx;
c->sety = &coordinate_sety;
c->print = &coordinate_print;
c->x = 0;
c->y = 0;
}
return c;
}
/* Destructor */
void coordinate_destroy(coordinate *this)
{
if (this != NULL)
{
free(this);
}
}
/* Methods */
static void coordinate_setx(coordinate *this, int x)
{
if (this != NULL)
{
this->x = x;
}
}
static void coordinate_sety(coordinate *this, int y)
{
if (this != NULL)
{
this->y = y;
}
}
static void coordinate_print(coordinate *this)
{
if (this != NULL)
{
printf("Coordinate: (%i, %i)\n", this->x, this->y);
}
else
{
printf("NULL pointer exception!\n");
}
}
좌표 클래스의 사용 예는 다음과 같습니다.
/* main.c */
#include "coordinates.h"
#include <stddef.h>
int main(void)
{
/* Create and initialize pointers to coordinate objects */
coordinate *c1 = coordinate_create();
coordinate *c2 = coordinate_create();
/* Now we can use our objects using our methods and passing the object as parameter */
c1->setx(c1, 1);
c1->sety(c1, 2);
c2->setx(c2, 3);
c2->sety(c2, 4);
c1->print(c1);
c2->print(c2);
/* After using our objects we destroy them using our "destructor" function */
coordinate_destroy(c1);
c1 = NULL;
coordinate_destroy(c2);
c2 = NULL;
return 0;
}