C柔性阵列用另一种类型而不是malloc定义

发布于 2025-01-24 18:31:00 字数 1022 浏览 2 评论 0原文

灵活数组的一般用法是使用Malloc来定义柔性阵列。我正在尝试使用另一个结构来探索定义灵活数组。 。

typedef struct {
    uint64_t header;
    size_t data_size;
    float data[];
} test_base_t;

typedef struct {
    test_base_t base;
    float data[3];
} test_t;

据我了解,需要在结构末尾定义灵活数组 Clangd将发出以下警告。 -wgnu-variable sized-type-not-at-at-at-ind-code>

我只是想问一个人以前是否已经这样做了,这是否安全?还是有一种更好的方法来定义没有异洛克的柔性阵列尺寸?

然后,您可以将对象的用法包裹在宏中,以静态sustert ext.base.data == ext.data在铸造和传递给常规API之前,请消耗code> test_base_t 。这样,您可以拥有编译所需的内存而不是分配。

编辑

似乎对我想如何消费感到困惑,这是一个示例

#define SUM_BASE(test) \
    sum_base(&test->base); \
    _Static_assert(test->data == test->base.data);

float sum_base(test_base_t *base)
{
  float sum = 0;
  for (size_t i = 0; i < base->data_size; i++)
  {
    sum += base->data[i];
  }
  return sum;
}

test_t test = { .base = { .data_size = 3, }, .data = { 1, 2, 3, }, };
SUM_BASE((&test));

the general usage of flexible array is to use malloc to define the flexible array. I'm trying to explore defining the flexible array with another struct. An example

typedef struct {
    uint64_t header;
    size_t data_size;
    float data[];
} test_base_t;

typedef struct {
    test_base_t base;
    float data[3];
} test_t;

As I understand, flexible array needs to be defined at the end of a struct. And clangd will give the following warning. -Wgnu-variable-sized-type-not-at-end

I just wanted to ask if anybody has done this before and is it safe? Or is there a better way to define flexible array size without alloc?

You can then wrap the usage of the object in a macro to static assert ext.base.data == ext.data before casting and passing to a general API consumes test_base_t. This way you can have the memory required in compile instead of allocing.

Edit

There seem to be a confusion on how I wanted to consume it, here is an example to demonstrate

#define SUM_BASE(test) \
    sum_base(&test->base); \
    _Static_assert(test->data == test->base.data);

float sum_base(test_base_t *base)
{
  float sum = 0;
  for (size_t i = 0; i < base->data_size; i++)
  {
    sum += base->data[i];
  }
  return sum;
}

test_t test = { .base = { .data_size = 3, }, .data = { 1, 2, 3, }, };
SUM_BASE((&test));

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

遇见了你 2025-01-31 18:31:00

您无法使用初始化的数组创建test_base_t的实际实例,但是您可以使用指定长度的初始化数组创建复合文字,并以test_base_t POINTERS的形式施放其地址。鉴于它们具有完全相同的类型,因此两种结构的布局和对齐都应兼容。

这是一个示例:

#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>

typedef struct {
    uint64_t header;
    size_t data_size;
    float data[];
} test_base_t;

#define TEST_ARRAY(n) (test_base_t*)&(struct { uint64_t header;  \
                                               size_t data_size; \
                                               float data[n]; })

float sum_base(const test_base_t *p) {
    float sum = 0.F;
    for (size_t i = 0; i < p->data_size; i++) {
        sum += p->data[i];
    }
    return sum;
}

void print_test(const test_base_t *p) {
    printf("%"PRIu64" %zu { ", p->header, p->data_size);
    if (p->data_size) {
        printf("%g", p->data[0]);
        for (size_t i = 1; i < p->data_size; i++) {
            printf(" %g", p->data[i]);
        }
    }
    printf(" } sum=%g\n", sum_base(p));
}

int main() {
    test_base_t *p1 = TEST_ARRAY(1){.data_size = 1, .data = {1}};
    test_base_t *p2 = TEST_ARRAY(2){.data_size = 2, .data = {1, 2}};
    print_test(p1);
    print_test(p2);
    print_test(TEST_ARRAY(3){.data_size = 3, .data = {1, 2, 3}});
    print_test(TEST_ARRAY(4){.data_size = 4, .data = {1, 3, 5, 7}});
    return 0;
}

这是另一种方法,也许更接近您的期望,使用unionbase具有灵活类型的基本成员,并且具有适当数组的参数实例类型尺寸:

#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>

typedef struct {
    uint64_t header;
    size_t data_size;
    float data[];
} test_base_t;

/* parametric type template using C macros */
/* structures with a flexible array can be members of union types */
#define test_base_t(...) \
    union { \
        test_base_t base; \
        struct { \
            uint64_t header; \
            size_t data_size; \
            float data[__VA_ARGS__]; \
        }; \
    }

float sum_base(const test_base_t *p) {
    float sum = 0.F;
    for (size_t i = 0; i < p->data_size; i++) {
        sum += p->data[i];
    }
    return sum;
}

void print_test(const test_base_t *p) {
    printf("%"PRIu64" %zu { ", p->header, p->data_size);
    if (p->data_size) {
        printf("%g", p->data[0]);
        for (size_t i = 1; i < p->data_size; i++) {
            printf(" %g", p->data[i]);
        }
    }
    printf(" } sum=%g\n", sum_base(p));
}

int main() {
    test_base_t(1) t1 = { .data_size = 1, .data = {1} };
    test_base_t(2) t2 = { .data_size = 2, .data = {1, 2} };
    /* the print_test function can be called without casts */
    print_test(&t1.base);
    print_test(&t2.base);
    print_test(&((test_base_t(3)){.data_size = 3, .data = {1, 2, 3}}).base);
    print_test(&((test_base_t(4)){.data_size = 4, .data = {1, 3, 5, 7}}).base);
    return 0;
}

You cannot create actual instances of test_base_t with an initialized array, but you can create compound literals with an initialized array of a specified length and cast their address as test_base_t pointers. The layout and alignment of both structures should be compatible, given that they have exactly the same types, save for the flexible array length.

Here is an example:

#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>

typedef struct {
    uint64_t header;
    size_t data_size;
    float data[];
} test_base_t;

#define TEST_ARRAY(n) (test_base_t*)&(struct { uint64_t header;  \
                                               size_t data_size; \
                                               float data[n]; })

float sum_base(const test_base_t *p) {
    float sum = 0.F;
    for (size_t i = 0; i < p->data_size; i++) {
        sum += p->data[i];
    }
    return sum;
}

void print_test(const test_base_t *p) {
    printf("%"PRIu64" %zu { ", p->header, p->data_size);
    if (p->data_size) {
        printf("%g", p->data[0]);
        for (size_t i = 1; i < p->data_size; i++) {
            printf(" %g", p->data[i]);
        }
    }
    printf(" } sum=%g\n", sum_base(p));
}

int main() {
    test_base_t *p1 = TEST_ARRAY(1){.data_size = 1, .data = {1}};
    test_base_t *p2 = TEST_ARRAY(2){.data_size = 2, .data = {1, 2}};
    print_test(p1);
    print_test(p2);
    print_test(TEST_ARRAY(3){.data_size = 3, .data = {1, 2, 3}});
    print_test(TEST_ARRAY(4){.data_size = 4, .data = {1, 3, 5, 7}});
    return 0;
}

Here is another approach, perhaps closer to your expectations, using a union with a base member with the flexible type and a parametric instance type with the appropriate array size:

#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>

typedef struct {
    uint64_t header;
    size_t data_size;
    float data[];
} test_base_t;

/* parametric type template using C macros */
/* structures with a flexible array can be members of union types */
#define test_base_t(...) \
    union { \
        test_base_t base; \
        struct { \
            uint64_t header; \
            size_t data_size; \
            float data[__VA_ARGS__]; \
        }; \
    }

float sum_base(const test_base_t *p) {
    float sum = 0.F;
    for (size_t i = 0; i < p->data_size; i++) {
        sum += p->data[i];
    }
    return sum;
}

void print_test(const test_base_t *p) {
    printf("%"PRIu64" %zu { ", p->header, p->data_size);
    if (p->data_size) {
        printf("%g", p->data[0]);
        for (size_t i = 1; i < p->data_size; i++) {
            printf(" %g", p->data[i]);
        }
    }
    printf(" } sum=%g\n", sum_base(p));
}

int main() {
    test_base_t(1) t1 = { .data_size = 1, .data = {1} };
    test_base_t(2) t2 = { .data_size = 2, .data = {1, 2} };
    /* the print_test function can be called without casts */
    print_test(&t1.base);
    print_test(&t2.base);
    print_test(&((test_base_t(3)){.data_size = 3, .data = {1, 2, 3}}).base);
    print_test(&((test_base_t(4)){.data_size = 4, .data = {1, 3, 5, 7}}).base);
    return 0;
}
怀里藏娇 2025-01-31 18:31:00

C 2018 6.7.2.1 3谈到包含灵活数组成员的结构:

…&nbsp;这样的结构(以及任何包含这种结构的成员的联合)不得是阵列的结构或元素的成员。

因此,test_t在问题中输入违反了此“应”要求,C 2018 4 2说,这使得行为未由C标准定义。编译器可以拒绝此代码。如果编译器接受它,则程序的行为未由C标准定义。

作为可能出问题的一个示例(因为C标准允许它),请考虑以下代码:

test_t test = { .base = { .data_size = 3, }, .data = { 1, 2, 3, }, };
printf("%g\n", test.base.data[0]);

因为test.base.data [0]从未通过该表达式分配一个值,并且标准不要将test.data定义为balias test.base.data,编译器可以假设test.base.base.data [0]>是非初始化的,因此未指定,即使float类型,此printf也可以使用任何值,即使test.base.base.base.data [0]test.data [0]名义上参考相同的内存。

在此代码中:

test_t test = { .base = { .data_size = 3, } };
for (int i = 0; i < 4; ++i)
    test.base.data[i] = i+1;
test_t copy = test;

编译器可以假设,由于test.data从未初始化,因此从test初始化它时,它不需要复制到复制

C 2018 6.7.2.1 3 says of a structure containing a flexible array member:

… such a structure (and any union containing, possibly recursively, a member that is such a structure) shall not be a member of a structure or an element of an array.

Thus, the test_t type in the question violates this “shall” requirement, and C 2018 4 2 says that makes the behavior not defined by the C standard. A compiler could reject this code. If the compiler accepts it, the behavior of the program is not defined by the C standard.

As an example of what could go wrong (in that the C standard allows it), consider this code:

test_t test = { .base = { .data_size = 3, }, .data = { 1, 2, 3, }, };
printf("%g\n", test.base.data[0]);

Since test.base.data[0] was never assigned a value through that expression, and the standard does not define test.data to alias test.base.data, the compiler may assume the value of test.base.data[0] is uninitialized and hence unspecified, and this printf may use any value of the float type, even if test.base.data[0] and test.data[0] nominally refer to the same memory.

And in this code:

test_t test = { .base = { .data_size = 3, } };
for (int i = 0; i < 4; ++i)
    test.base.data[i] = i+1;
test_t copy = test;

The compiler may assume that, since test.data was never initialized, it does not need to be copied to copy when initializing it from test.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文