수색…


소개

구조는 다양한 유형의 관련 변수 세트를 단일 메모리 단위로 그룹화하는 방법을 제공합니다. 구조체는 전체적으로 하나의 이름이나 포인터로 참조 될 수 있습니다. 구조체 멤버도 개별적으로 액세스 할 수 있습니다. 구조체는 함수에 전달되고 함수에서 반환 될 수 있습니다. 키워드 struct 사용하여 정의됩니다.

간단한 데이터 구조

구조 데이터 유형은 관련 데이터를 패키지화하고 단일 변수처럼 작동하도록하는 데 유용합니다.

int 멤버를 보유하는 간단한 struct 선언 :

struct point 
{
    int x;
    int y; 
};

xypoint 구조체의 멤버 (또는 필드 )라고합니다.

구조체 정의 및 사용 :

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 구조체

structtypedef 를 결합하면 코드가 명확 해집니다. 예 :

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);
    }
}

유연한 배열 멤버

C99

타입 선언

적어도 하나의 부재 를 갖는 구조물은 구조물의 끝에 불특정 길이의 단일 배열 부재를 부가 적으로 포함 할 수있다. 이를 유연한 배열 구성원이라고합니다.

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 */
C99

'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;
}


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow