C 传递一个';typedef结构';函数
我有以下情况: 文件A.c:C 传递一个';typedef结构';函数,c,arrays,typedef,C,Arrays,Typedef,我有以下情况: 文件A.c: typedef struct element { uint16_t value_raw; float value_scaled; char *desc; } element; element sv[REG_READ_COUNT]; 文件A.h: typedef struct element element; B.c.文件: #include "A.h" void dostuff (element sv[]) { } 在编译时,我得到
typedef struct element
{
uint16_t value_raw;
float value_scaled;
char *desc;
} element;
element sv[REG_READ_COUNT];
文件A.h:
typedef struct element element;
B.c.文件:
#include "A.h"
void dostuff (element sv[]) { }
在编译时,我得到
“错误:数组类型的元素类型不完整”
对于B.c.中的函数参数定义
正确的方法是什么?
如何将“element”类型的数组传递到函数中?在
B.c
中,element
是一个不完整的类型(它没有在a.h
中定义,只有在a.c
中定义)。C不允许具有不完整元素类型的数组声明符(正如您所发现的)。以下是C99草案的相关文本:
6.7.5.2阵列声明器
约束条件
static
,[
和]
可以限定表达式或*
。如果它们限定表达式(指定数组大小),则表达式应为整数类型。如果表达式是常数表达式,则其值应大于零元件类型不得为不完整或功能类型。可选类型限定符和关键字static
应仅出现在具有数组类型的函数参数声明中,然后仅出现在最外层的数组类型派生中要修复代码,请将完整的结构定义放入
A.h
。或者,如果dostuff
实际上不需要使用元素(例如,只需将“数组”传递给其他函数),则可以使用void dostuff(元素*sv)
因为A.h仅定义不透明类型typedef struct-element元素
B.c不可能知道元素的组成,甚至无法确定其大小。所以它不能创建这些结构的数组。如果希望此代码正常工作,则必须将A.c中的整个typedef移到A.h。如果您这样做,那么就没有信息隐藏,完整的结构可以通过标头获得
此外,您可以创建一个指向结构的指针数组(即使它可能不完整)并将其传递到函数中,但您不能直接访问任何结构成员变量
在指向这些类型的指针数组中使用不透明数据类型的示例:
typedef struct element element;
#define REG_READ_COUNT 100
void dostuff (element *sv[])
{
sv++; /* get next pointer to element */
};
int main()
{
element *sv[REG_READ_COUNT]; /* array of pointers to element */
dostuff(sv);
}
在需要任何需要实际类型大小的内容之前,此代码是可以使用的。如果没有额外的粘合代码(另一个模块),我们甚至无法将数据成员初始化为任何实际具有完整元素类型访问权限的对象
可以使用指针数组(甚至指向不完整类型)的原因是指针是C中的基本类型。它既不是不完整类型,也不是函数类型。指针具有固定大小,编译器可以使用该大小生成指针数组
6.7.5.2数组声明器
约束条件
除了可选的类型限定符和关键字static外,[和]还可以分隔表达式或*。如果它们限定表达式(指定数组大小),则表达式应为整数类型。如果表达式是常数表达式,则其值应大于零元件类型不得为不完整或功能类型。可选类型限定符和关键字static应仅出现在具有数组类型的函数参数声明中,然后仅出现在最外层的数组类型派生中
因为指针不是不完整的类型或函数类型,所以即使它们指向不完整的类型,也可以创建它们的数组。指向不完整类型的指针不会使指针不完整。你只是不能去引用它,而希望直接用它做任何有用的事情。我说直接是因为在数据隐藏技术和不透明指针中,您可以提供间接机制来处理不透明指针数据
下面是一个代码示例,该代码无法以与OPs类似的方式编译。我们认为指向不完整类型的指针可以传递(函数参数),但它们仍然不能用作函数中的数组:
typedef struct element element;
#define REG_READ_COUNT 100
void dostuff (element *sv) /* Completely legal but useless if you intend to use it as an array */
{
sv++; /* This should fail - as we are doing array arithmetic on
* an incomplete type. Can't find the starting point of the next
* array element without knowing the size of the object */
};
int main()
{
element sv[REG_READ_COUNT]; /* array of elements will also fail - size of object unknown */
dostuff(sv);
}
这与前一个几乎相同。在这个例子中,我们有一个指向不完整类型的指针sv
,作为一个函数参数(这是来自NNENONNEO答案)。这是完全合法的,因为它只是一个指针。但是,尝试对其执行数组算术(在body函数中使用++
)将失败,因为它需要知道元素的大小,而不知道它的大小and--或索引数组是未定义的行为(大多数符合标准的编译器都会抛出错误)。ISO/IEC 9899:TC2规定:
6.3.2其他操作数
6.3.2.1左值、数组和函数指示符
2除非它是sizeof运算符、一元运算符和++
运算符、--运算符或的左操作数。运算符或赋值运算符,
没有数组类型的左值将转换为存储在指定数组中的值
对象(不再是左值)。如果左值具有限定类型,则该值具有
左值类型的不合格版本;否则,该值的类型为
左值如果左值的类型不完整且没有数组类型,则行为为
未定义
有关不透明类型的更多信息,请参见再现错误的最小代码
struct element;
void dostuff (struct element sv[]) { }
使用coliru对clang
和gcc
进行测试:结果:GCC和clang总是抱怨类型数组的参数不完整,而从不抱怨指向不完整类型的指针 相关标准报价: 6.7.6.3功能声明器(包括原型) […]
#include <stdint.h>
typedef struct element element;
typedef element *ELEM_HANDLE;
extern element *element_new();
extern void element_delete(element *elem);
extern void element_set_value_raw(element *elem, uint16_t value_raw);
extern uint16_t element_get_value_raw(element *elem);
extern void element_set_value_scaled(element *elem, float value_scaled);
extern float element_get_value_scaled(element *elem);
extern void element_set_desc(element *elem, char *desc);
extern char *element_get_desc(element *elem);
#include <stdint.h>
#include <stdlib.h>
typedef struct element
{
uint16_t value_raw;
float value_scaled;
char *desc;
} element;
element *element_new()
{
return calloc(1, sizeof(element));
}
void element_delete(element *elem)
{
free(elem);
}
void element_set_value_raw(element *elem, uint16_t value_raw)
{
elem->value_raw = value_raw;
}
uint16_t element_get_value_raw(element *elem)
{
return elem->value_raw;
}
void element_set_value_scaled(element *elem, float value_scaled)
{
elem->value_scaled = value_scaled;
}
float element_get_value_scaled(element *elem)
{
return elem->value_scaled;
}
void element_set_desc(element *elem, char *desc)
{
elem->desc = desc;
}
char *element_get_desc(element *elem)
{
return elem->desc;
}
#include <stdio.h>
#include "element.h"
#define REG_READ_COUNT 2
void dostuff(element *sv[], int arrLen)
{
int index;
element *curelem;
uint16_t raw;
float scaled;
char *desc;
for (index = 0; index < arrLen ; index++){
curelem = sv[index];
raw = element_get_value_raw(curelem);
scaled = element_get_value_scaled(curelem);
desc = element_get_desc(curelem);
/* Do more interesting stuff here */
printf("%s, %d, %.4f\n", desc, raw, scaled);
}
}
int main()
{
unsigned int index;
element *sv[REG_READ_COUNT]; /* array of element pointers*/
char desc1[] = "The answer to everything";
char desc2[] = "OtherStuff";
/* Initialize an array of pointers to element items */
for (index = 0; index < sizeof(sv) / sizeof(element *); index++)
sv[index] = element_new();
element_set_value_raw(sv[0], 42);
element_set_value_scaled(sv[0], 6.66f);
element_set_desc(sv[0], desc1);
element_set_value_raw(sv[1], 123);
element_set_value_scaled(sv[1], 456.7f);
element_set_desc(sv[1], desc2);
dostuff(sv, REG_READ_COUNT);
/* free the array of pointers to element items*/
for (index = 0; index < sizeof(sv) / sizeof(element *); index++)
element_delete(sv[index]);
return 0;
}