gpt4 book ai didi

c - 函数原型(prototype)范围内的可变长度数组类型

转载 作者:太空宇宙 更新时间:2023-11-04 06:12:00 25 4
gpt4 key购买 nike

我正在学习 VLA 并编写了以下示例:

struct array_t{
const size_t length;
const char data[];
};

struct array_t *create(const size_t n, const char data[n]){
const size_t data_offset = offsetof(struct array_t, data);
struct array_t *array = malloc(data_offset + n * sizeof(char));
memcpy(&(array -> length), &n, sizeof(n));
memcpy(&(array -> data), data, n);
return array;
}

所以我测试了一下

char ca[3] = {'a', 'b', 'c'};
struct array_t *array_ptr = create(5, ca);

它编译得很好(不幸的是)。当我想出 6.7.6.2(p5) 时:

If the size is an expression that is not an integer constant expression: if it occurs in a declaration at function prototype scope, it is treated as if it were replaced by *; otherwise, each time it is evaluated it shall have a value greater than zero.

很明显 n 不是常量表达式,const char data[n] 被简单地视为 const char* 这不是什么我想要。

如果这样的数组声明不提供任何类型安全性,那么有什么理由要声明它们吗?也许我们可以编写一些宏函数来执行以下操作:

#define create_array_t //...

const char a1[5];
const char a2[10];
const char *a_ptr;

create_array_t(5, a1); //fine
create_array_t(5, a2); //error
create_array_t(5, a_ptr); //error

最佳答案

首先,为具有灵活数组成员的结构体分配空间的函数应该是这样的:

array_t* create (const size_t n, const char data[n])
{
array_t* array = malloc( sizeof(array_t) + sizeof(char[n]) );
array->length = n;
memcpy(array->data, data, n);
return array;
}

So is there any reason of such arrays declarations if they don't give any type safety?

理论上,优秀的编译器可以忽略警告,但我认为没有这样做的编译器。静态分析器会发出警告。

然而,主要原因是自文档化代码。您在大小变量和数组变量之间建立了紧密耦合。

Maybe we can write some macro function

当然,使用标准 ISO C,我们可以编写包装器宏来提高类型安全性并利用 VLA 表示法。像这样:

#define create_array_t(n, array)      \
_Generic(&array, \
char(*)[n]: create, \
const char(*)[n]: create) (n, array)

这里的技巧是通过使用&来避免数组衰减,取一个数组指针。然后比较数组类型是否与该指针匹配,然后在使用传递的参数调用 create 之前。

完整示例:

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>

typedef struct
{
size_t length;
char data[];
} array_t;

array_t* create (const size_t n, const char data[n])
{
array_t* array = malloc(sizeof(array_t) + sizeof(char[n]));
array->length = n;
memcpy(array->data, data, n);
return array;
}

#define create_array_t(n, array) \
_Generic(&array, \
char(*)[n]: create, \
const char(*)[n]: create) (n, array)

int main (void)
{
const char a1[5];
const char a2[10];
const char *a_ptr;

(void) create_array_t(5, a1); // fine
//(void) create_array_t(5, a2); // error _Generic selector of type 'const char(*)[10]' is not compatible
//(void) create_array_t(5, a_ptr); // error _Generic selector of type 'const char**' is not compatible

return 0;
}

这可以通过使 array_t 成为不透明类型、将结构实现隐藏在 .c 文件中并获得具有私有(private)封装的面向对象的 ADT 来进一步改进。

关于c - 函数原型(prototype)范围内的可变长度数组类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54234838/

25 4 0
文章推荐: python - 在 python Flask/Gevent Web 服务中缓存大对象
文章推荐: html - 将
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com