source

요소가 전혀 없는 어레이의 필요성은 무엇입니까?

goodcode 2022. 7. 28. 23:51
반응형

요소가 전혀 없는 어레이의 필요성은 무엇입니까?

Linux 커널 코드에서 다음과 같이 이해할 수 없는 것을 발견했습니다.

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

코드는 http://lxr.free-electrons.com/source/include/linux/ti_wilink_st.h 입니다.

요소가 전혀 없는 데이터 배열의 필요성과 목적은 무엇입니까?

이는 다양한 크기의 데이터를 보유할 수 있는 방법이며,malloc(kmalloc(이 경우는)를 2회 실시합니다.다음과 같이 사용할 수 있습니다.

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

이것은 이전에는 표준이 아니었고 해킹으로 간주되었지만(애니켓이 말했듯이), 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(강조 지뢰)에 설명되어 있습니다.

특별한 경우로서 복수의 이름 있는 멤버가 있는 구조의 마지막 요소는 불완전한 배열 타입을 가질 수 있습니다.이것은 플렉시블 배열 멤버라고 불립니다.대부분의 경우 유연한 어레이 멤버는 무시됩니다.특히 구조의 크기는 생략이 의미하는 것보다 더 많은 후행 패딩이 있을 수 있다는 점을 제외하고 플렉시블 배열 멤버를 생략한 것과 같습니다.하면서.(또는 ->.)연산자, 그것이 작용하는 유연한 배열 멤버이자 오른쪽 피연산자 이름을 지어 멤버가 있는 구조체(에 대한 포인터)은 왼쪽 피연산자가 만약 그 부재 구조는 개체에 액세스 할 지는 것보다 더 크게 하지 않을 것 가장 긴 배열( 같은 요소 형식과);촉의 오프셋으로 바뀌었다.ray는 교체 어레이와 다른 경우에도 플렉시블 어레이 부재의 y를 유지해야 한다.이 배열에 요소가 없는 경우 하나의 요소가 있는 것처럼 동작하지만 해당 요소에 액세스하거나 포인터를 생성하려고 하면 동작은 정의되지 않습니다.

즉, 다음과 같은 경우:

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

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

액세스 할 수 있습니다.var->data에 인덱스를 넣고[0, extra)주의해 주세요.sizeof(struct something)는 다른 변수의 크기만 고려합니다. 즉,data사이즈는 0 입니다.


또한 이 표준이 실제로 어떻게 다음과 같은 예를 제시하는지 주목하는 것도 흥미로울 수 있다.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의 오프셋은 동일하지 않을 수 있습니다).

코드가 유효하지 않습니다(이것 참조).Linux 커널은 명백한 이유로 휴대성과는 전혀 관계가 없기 때문에 많은 비표준 코드를 사용합니다.

어레이 사이즈가 0인 GCC 비표준 확장 기능을 사용하고 있습니다.에 준거한 이라면 「 」라고 쓰여져 것입니다.u8 data[];그리고 그것은 똑같은 것을 의미했을 것이다.Linux 커널의 저자들은 어떤 옵션이 스스로 드러날 경우 불필요하게 복잡하고 비표준적인 것을 만드는 것을 좋아하는 것 같습니다.

오래된 C 표준에서는 빈 배열로 구조체를 끝내는 것을 "구조 해킹"이라고 했습니다.다른 사람들은 이미 다른 대답으로 그것의 목적을 설명했다.C90 표준에서 구조 해크는 정의되지 않은 동작으로 크래시를 일으킬 수 있습니다.주로 C 컴파일러는 구조 끝에 임의의 수의 패딩 바이트를 추가할 수 있기 때문입니다.이러한 패딩 바이트는 구조체의 끝에 있는 "해킹"하려는 데이터와 충돌할 수 있습니다.

GCC는 초기에 비표준 확장을 실시하여 이를 정의되지 않은 동작에서 잘 정의된 동작으로 변경했습니다.그 후 C99 표준은 이 개념을 채택하였으며, 따라서 현대의 C 프로그램은 위험 없이 이 기능을 사용할 수 있습니다.C99/C11에서는 플렉시블 어레이 멤버라고 불립니다.

이것은 실제로 GCC(C90)에 대한 해킹입니다.

구조 해킹이라고도 합니다.

그래서 다음 번엔 이렇게 말할 거야

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

이는 다음과 같은 말을 하는 것과 같습니다.

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

이러한 구조 오브젝트는 얼마든지 만들 수 있습니다.

이 아이디어는 구조의 끝에 가변 크기 배열을 허용하는 것입니다.마도,는bts_action는, 사이즈의 헤더를 입니다( 「 」 。type ★★★★★★★★★★★★★★★★★」size)및 크기 """""""""""""""""""""""""""""""data으로써 다른배열있습니다. '하다', '하다', '하다', ' '하다', '하다', '하다', '하다', '하다', '하다', '하다', '하다', '다',bts_action를 들어 의 '1024'data다음과 같이 합니다.

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

참고 항목: http://c2.com/cgi/wiki?StructHack

제로렝스 배열의 또 다른 용도는 시간 구조 오프셋 체크 컴파일을 지원하기 위해 구조체 내부의 이름 있는 라벨로 사용하는 것입니다.

큰 구조 정의(복수의 캐시 행에 걸쳐 있음)가 있으며, 이러한 정의가 경계를 가로지르는 시작 부분과 중간 부분 모두에서 캐시 행 경계에 맞춰져 있는지 확인하려고 합니다.

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

코드에서는 다음과 같은 GCC 확장을 사용하여 선언할 수 있습니다.

__attribute__((aligned(CACHE_LINE_BYTES)))

그러나 실행 시 이 기능이 적용되도록 해야 합니다.

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

이것은 단일 구조물에 대해 작동하지만, 정렬해야 할 부재 이름이 서로 다르기 때문에 많은 구조물을 포함하기는 어렵습니다.각 구조체의 첫 번째 구성원의 이름을 찾아야 하는 코드는 다음과 같습니다.

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

이 방법을 사용하는 대신 일관된 이름을 가진 이름 있는 레이블로 작동하지만 공간을 사용하지 않는 구조체의 0 길이 배열을 선언할 수 있습니다.

#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
    ....
};

그러면 런타임 어설션 코드를 유지하기가 훨씬 쉬워집니다.

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

반응형