Programing

요소가 0 인 배열이 필요합니까?

crosscheck 2020. 7. 20. 07:37
반응형

요소가 0 인 배열이 필요합니까?


리눅스 커널 코드에서 나는 이해할 수없는 다음을 발견했습니다.

 struct bts_action {
         u16 type;
         u16 size;
         u8 data[0];
 } __attribute__ ((packed));

코드는 다음과 같습니다. http://lxr.free-electrons.com/source/include/linux/ti_wilink_st.h

요소가 0 인 데이터 배열의 필요성과 목적은 무엇입니까?


이것은 malloc( kmalloc이 경우) 두 번 호출하지 않고도 가변 크기의 데이터를 갖는 방법 입니다. 다음과 같이 사용하십시오.

struct bts_action *var = kmalloc(sizeof(*var) + extra, GFP_KERNEL);

이것은 표준이 아니었고 해킹으로 간주되었지만 (Aniket이 말했듯이) C99 에서 표준화되었습니다 . 현재 표준 형식은 다음과 같습니다.

struct bts_action {
     u16 type;
     u16 size;
     u8 data[];
} __attribute__ ((packed)); /* Note: the __attribute__ is irrelevant here */

data필드의 크기는 언급하지 않았습니다 . 이 특수 변수는 구조체의 끝에 만 올 수 있습니다.


C99에서이 문제는 6.7.2.1.16 (강조 광산)에 설명되어 있습니다.

특별한 경우로, 명명 된 멤버가 둘 이상인 구조의 마지막 요소는 불완전한 배열 유형을 가질 수 있습니다. 이것을 유연한 배열 멤버라고합니다. 대부분의 경우 가변 배열 멤버는 무시됩니다. 특히, 구조의 크기는가요 성 어레이 부재가 생략되는 것보다 더 많은 후미 패딩을 가질 수 있다는 것을 제외하고는가요 성 어레이 부재가 생략 된 것과 같다. 그러나. (또는->) 연산자에는 유연한 배열 멤버가있는 구조 인 왼쪽 피연산자 및 해당 멤버의 오른쪽 피연산자 이름이 있으며, 해당 멤버가 요소 유형이 동일한 가장 긴 배열로 교체 된 것처럼 동작합니다. ) 액세스하는 객체보다 구조를 크게 만들지 않습니다. 교체 어레이와 상이하더라도 어레이의 오프셋은 플렉시블 어레이 부재의 오프셋으로 유지되어야한다. 이 배열에 요소가 없으면

또는 다른 말로하면,

struct something
{
    /* other variables */
    char data[];
}

struct something *var = malloc(sizeof(*var) + extra);

var->data색인으로 액세스 할 수 있습니다 [0, extra). sizeof(struct something)에만 다른 변수의 크기 회계를 줄 것이다는, 즉 제공 data공의 크기.


표준이 실제로 malloc그러한 구조 (6.7.2.1.17)의 예를 어떻게 제공하는지 주목하는 것도 흥미로울 수 있습니다 .

struct s { int n; double d[]; };

int m = /* some value */;
struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));

동일한 위치에서 표준에 의한 또 다른 흥미로운 메모는 (강조 광산)입니다.

malloc에 ​​대한 호출이 성공한다고 가정하면 p가 가리키는 오브젝트는 p가 다음과 같이 선언 된 것처럼 대부분의 목적으로 작동합니다.

struct { int n; double d[m]; } *p;

(이 동등성이 깨지는 상황이 있습니다. 특히 구성원 d의 오프셋이 같지 않을 수도 있습니다 ).


이것은 실제로 GCC ( C90 )에 입니다.

struct hack 이라고도합니다 .

다음에 저는 이렇게 말할 것입니다.

struct bts_action *bts = malloc(sizeof(struct bts_action) + sizeof(char)*100);

그것은 말하는 것과 같습니다.

struct bts_action{
    u16 type;
    u16 size;
    u8 data[100];
};

그리고 그러한 구조체 객체를 얼마든지 만들 수 있습니다.


아이디어는 구조체의 끝에 가변 크기 배열을 허용하는 것입니다. 아마도 bts_action고정 크기 헤더 ( typesize필드)와 가변 크기 data멤버 가있는 일부 데이터 패킷 일 것입니다 . 이를 길이가 0 인 배열로 선언하면 다른 배열과 마찬가지로 색인을 생성 할 수 있습니다. 그런 다음 bts_action1024 바이트 data크기 구조체를 다음과 같이 할당합니다 .

size_t size = 1024;
struct bts_action* action = (struct bts_action*)malloc(sizeof(struct bts_action) + size);

참조 : http://c2.com/cgi/wiki?StructHack


코드가 유효하지 않은 C입니다 ( 참조 ). Linux 커널은 명백한 이유 때문에 이식성과 관련이 거의 없으므로 많은 비표준 코드를 사용합니다.

그들이하고있는 것은 배열 크기가 0 인 GCC 비표준 확장입니다. 표준 호환 프로그램은 u8 data[]; and it would have meant the very same thing. The authors of the Linux kernel apparently love to make things needlessly complicated and non-standard, if an option to do so reveals itself.

In older C standards, ending a struct with an empty array was known as "the struct hack". Others have already explained its purpose in other answers. The struct hack, in the C90 standard, was undefined behavior and could cause crashes, mainly since a C compiler is free to add any number of padding bytes at the end of the struct. Such padding bytes may collide with the data you tried to "hack" in at the end of the struct.

GCC early on made a non-standard extension to change this from undefined to well-defined behavior. The C99 standard then adapted this concept and any modern C program can therefore use this feature without risk. It is known as flexible array member in C99/C11.


Another not so often seen usage of zero length array is to get a named label inside a struct.

Suppose you have some large struct definitions (spans multiple cache lines) that you want to make sure they are aligned to cache line boundary both in the beginning and in the middle where it crosses the boundary.

struct example_large_s
{
    u32 first; // align to CL
    u32 data;
    ....
    u64 *second;  // align to second CL after the first one
    ....
};

In code you can declare them using GCC extensions like:

__attribute__((aligned(CACHE_LINE_BYTES)))

But you still want to make sure this is enforced in runtime.

ASSERT (offsetof (example_large_s, first) == 0);
ASSERT (offsetof (example_large_s, second) == CACHE_LINE_BYTES);

This would work for a single struct, but it would be hard to cover many structs, each has different member name to be aligned. You would most likely get code like below where you have to find names of the first member of each struct:

assert (offsetof (one_struct,     <name_of_first_member>) == 0);
assert (offsetof (one_struct,     <name_of_second_member>) == CACHE_LINE_BYTES);
assert (offsetof (another_struct, <name_of_first_member>) == 0);
assert (offsetof (another_struct, <name_of_second_member>) == CACHE_LINE_BYTES);

Instead of going this way, you can declare a zero length array in the struct acting as a named label with a consistent name but does not consume any space.

#define CACHE_LINE_ALIGN_MARK(mark) u8 mark[0] __attribute__((aligned(CACHE_LINE_BYTES)))
struct example_large_s
{
    CACHE_LINE_ALIGN_MARK (cacheline0);
    u32 first; // align to CL
    u32 data;
    ....
    CACHE_LINE_ALIGN_MARK (cacheline1);
    u64 *second;  // align to second CL after the first one
    ....
};

Then the runtime assertion code would be much easier to maintain:

assert (offsetof (one_struct,     cacheline0) == 0);
assert (offsetof (one_struct,     cacheline1) == CACHE_LINE_BYTES);
assert (offsetof (another_struct, cacheline0) == 0);
assert (offsetof (another_struct, cacheline1) == CACHE_LINE_BYTES);

참고URL : https://stackoverflow.com/questions/14643406/whats-the-need-of-array-with-zero-elements

반응형