gpt4 book ai didi

c - C标准是否认为此 header 中存在一种或两种“struct uperms_entry”类型?

转载 作者:行者123 更新时间:2023-12-04 10:28:43 25 4
gpt4 key购买 nike

能否从三种C标准之一(最好是C99或C11)中给出章和经节的内容,以指示以下头文件中是否包含一种或两种struct uperms_entry类型?

#ifndef UPERMS_CACHE_INCLUDE
#define UPERMS_CACHE_INCLUDE

typedef struct mutex MT_MUTEX;

typedef struct uperms_cache
{
MT_MUTEX *cache_lock;
int processing;
struct uperms_entry *uperms_list; // No prior struct uperms_entry
} uperms_cache_t;

typedef struct uperms_entry // Does this define a different struct uperms_entry?
{
char username[32];
int perms;
struct uperms_entry *next;
} uperms_entry_t;

#endif /* UPERMS_CACHE_INCLUDE */


附加问题:


如果有两种类型,是否有任何办法让GCC报告问题?
如果有两种类型,在实践中有关系吗?


(我认为答案是“是-严格来说有两种类型”,然后是(1)否和(2)否。)

上下文:内部代码审阅—我想颠倒结构的顺序,但是我不确定我是否完全过分学究。

更新:

显然,第一个问题的答案是“有一个 struct uperms_entry”,因此编号1和2的问题没有意义。我很高兴在进行代码审查之前仔细检查了一下。

背景思维

在解决主要问题很久之后添加了此部分。



以下是ISO / IEC 9899:2011中的一些广泛但相关的报价:


§6.2.7兼容类型和复合类型

¶1如果两个类型相同,则它们具有兼容类型。
确定两种类型是否兼容的其他规则是
对于类型说明符在6.7.2中进行了说明,对于类型限定符在6.7.3中进行了说明,
和6.7.6中的声明符。55)此外,还有两个结构,
联合,或在单独的翻译单元中声明的枚举类型是
如果他们的标签和成员满足以下要求,则兼容:
如果一个带有标签声明,则另一个应带有相同的声明。
标签。如果两者都在各自翻译的任何地方完成
单位,则需要满足以下附加要求:
成员之间的一对一对应关系
声明对应的成员具有兼容的类型;如果一个成员
该对中的一个是使用对齐说明符声明的,另一个是
用等效的对齐说明符声明;并且如果
该对使用一个名称声明,另一个使用相同的名称声明
名称。对于两个结构,应在以下位置声明相应的成员
相同的顺序。对于两个结构或联合,对应的位域
应具有相同的宽度。对于两个枚举,对应的成员
应该具有相同的值。

55)两种类型不必完全相同才能兼容。

第6.7.2.1节结构和联合说明符

¶8一个struct-declaration-list的存在
struct-or-union-specifier在翻译中声明一个新类型
单元。 struct-declaration-list是一系列声明的
结构或工会的成员。如果struct-declaration-list执行
不包含任何直接或通过匿名的命名成员
结构或匿名联合,则行为未定义。类型是
直到紧接终止列表的 }之后才是不完整的,并且
此后完成。

§6.7.2.3标签

¶4具有以下特征的所有结构,联合或枚举类型的声明
相同的作用域和使用相同的标记声明相同的类型。无关紧要
是否有标签或其他类型的声明
相同的翻译单元,类型不完整129)直到
在定义内容的列表的右大括号之后,
然后完成。

¶5两个结构,联合或枚举类型的声明是
在不同范围内或使用不同标记声明不同的类型。每
不声明的结构,联合或枚举类型的声明
包含标记可声明不同的类型。

¶6形式的类型说明符

struct-or-union identifier选择 { struct-declaration-list }

要么

enum identifier选择 { enumerator-list }

要么

enum identifier选择 { enumerator-list , }

声明结构,联合或枚举类型。该列表定义了
结构内容,联合内容或枚举内容。如果
130)提供了类型说明符
作为该类型标签的标识符。

¶7形式的声明

struct-or-union identifier ;


指定结构或联合类型,并将标识符声明为
131)

¶8如果是形式的类型说明符

struct-or-union identifier


除了上述形式之一的一部分以外发生,并且没有其他形式
将标识符声明为标记是可见的,然后声明一个
结构或联合类型不完整,并将标识符声明为
131)

¶9如果是形式的类型说明符

struct-or-union identifier


要么

enum identifier


除了上述形式之一的一部分以外,还有声明
标签的标识符可见,然后指定相同的类型
与其他声明一样,并且不会重新声明标签。

¶12示例2举例说明如何使用标签的预先声明
指定一对相互引用的结构,声明

struct s1 { struct s2 *s2p; /* ... */ }; // D1
struct s2 { struct s1 *s1p; /* ... */ }; // D2


指定一对包含彼此的指针的结构。
但是请注意,如果s2已在
封闭范围,声明D1将引用它,而不是标签
D2中声明的s2。为了消除这种上下文敏感性,
宣言

struct s2;


可以在D1之前插入。这在内部声明了一个新标签s2
范围;然后声明D2完成新类型的说明。

129)不完整类型只能在以下情况下使用:
不需要该类型的对象。不需要,例如,
当将typedef名称声明为结构的说明符时,或
联合,或指向结构或联合的指针或函数返回
正在声明。 (请参阅6.2.5中的不完整类型。)
必须在调用或定义此函数之前完成。

130)如果没有标识符,则类型可以在
翻译单位,只能通过其所在的声明来引用
一部分。当然,当声明具有typedef名称时,
后续声明可以使用该typedef名称进行声明
具有指定结构,联合或枚举类型的对象。

131)不存在带有枚举的类似构造。

§6.7.3类型限定符

¶10为了使两个合格的类型兼容,两者都应具有
兼容类型的完全相同的版本;的顺序
在指定符或限定符列表中的类型限定符不
影响指定的类型。


第6.7.6节中的讨论与指针,数组和函数有关。
声明符,并不会真正影响结构或联合。



当我编写问题时,我知道示例2。这是一些
大声思考以上信息的含义。

考虑下面的示例,它可以干净地编译:

#include <stdio.h>
struct r1 { int x; };

struct r1;

struct r1 p0;

//struct r1 { int y; }; // Redefinition of struct r1

extern void z(void);

void z(void)
{
struct r1 p1 = { 23 };
struct r1;
//struct r1 p2; // Storage size of p2 is not known
struct r2 { struct r1 *rn; int y; };
struct r1 { struct r2 *rn; int z; };
struct r2 p = { 0, 1 };
struct r1 q = { &p, 2 };
p.rn = &q;
printf("p.y = %d, q.z = %d\n", p.y, q.z);
printf("p1.x = %d\n", p1.x);
}


该函数说明了示例2适用于何时,但不明智
码。函数中 p1的声明是一个结构
与全局变量 p0具有相同类型。即使其类型名称
struct r1,它的类型不同于(不兼容)
局部变量 p的类型。

不允许在全局级别重新定义 struct r1
无论元素是命名为 x还是 y。先验
在这种情况下, struct r1;是空操作。

一个有趣的问题是“函数 z是否可以将 pq传递给任何其他函数
功能(称为 a)?答案是合格的“是”,还有一些
约束很有趣。 (这也将是令人震惊的编码
尝试使用它的样式,在疯狂的地方验证。)该函数必须存在于
单独的翻译单元(TU)。函数声明必须在内部
函数 z(因为如果它在函数外部,则其原型必须
请参考函数外部定义的 struct r1,而不是函数内部定义的 struct
r1


在另一个TU中,必须保持一定程度的理智:函数 a必须
具有兼容的结构类型 struct r1struct r2
在全球范围内。

这是另一个示例,但此示例无法编译:

#include <stdio.h>

struct r1;
extern void z(struct r1 *r1p);
extern void y(struct r1 *r1p);

void y(struct r1 *r1p)
{
struct r2 { struct r1 *rn; int y; };
struct r1 { struct r2 *rn; int z; };
struct r2 p = { r1p, 1 };
struct r1 q = { &p, 2 };
p.rn = &q;
printf("p.y = %d, q.z = %d\n", p.y, q.z);
}

void z(struct r1 *r1p)
{
struct r1
struct r2 { struct r1 *rn; int y; };
struct r1 { struct r2 *rn; int z; };
struct r2 p = { r1p, 1 };
struct r1 q = { &p, 2 };
p.rn = &q;
printf("p.y = %d, q.z = %d\n", p.y, q.z);
}


Mac OS X 10.7.4上来自GCC 4.7.1的警告是:

structs3.c: In function 'y':
structs3.c:13:10: warning: assignment from incompatible pointer type [enabled by default]
structs3.c: In function 'z':
structs3.c:22:12: warning: initialization from incompatible pointer type [enabled by default]
structs3.c:22:12: warning: (near initialization for 'p.rn') [enabled by default]


第13行是函数 p.rn = &q;中的赋值 y,第23行是
尝试在函数 struct r2 p中定义和初始化 z

这表明在函数中, rnstruct
r2
元素是指向在函数中声明的不完整类型 struct r1的指针。
全球范围。在其中添加 struct r1;作为第一行代码
该函数将允许代码进行编译,但需要初始化
引用 r1p->rn正在取消引用不完整类型的指针
再次(不完整的类型是在全局变量中声明的 struct r1
范围)。

函数声明和前面的 struct r1;行可以
在标题中显示为不透明类型。支持功能列表
不完整;必须有一种方法来获取指向
初始化 struct r1传递给函数,但这很详细。

要使代码在第二个TU中工作, struct r1的类型必须
在定义功能之前,请先在全局范围内完成,并且
因为有递归引用,所以`struct r21也必须是完整的。

#include <stdio.h>

/* Logically in a 3-line header file */
struct r1;
extern void z(struct r1 *r1p);
extern void y(struct r1 *r1p);

/* Details private to this TU */
struct r2 { struct r1 *rn; int y; };
struct r1 { struct r2 *rn; int z; };

void y(struct r1 *r1p)
{
struct r2 p = { r1p, 1 };
struct r1 q = { r1p->rn, 2 };
p.rn = &q;
printf("p.y = %d, q.z = %d\n", p.y, q.z);
}

void z(struct r1 *r1p)
{
struct r2 p = { r1p, 1 };
struct r1 q = { r1p->rn, 2 };
p.rn = &q;
printf("p.y = %d, q.z = %d\n", p.y, q.z);
}


在实现文件中定义结构的过程
在公共头文件中保留类型不完整可以在
必要时有多个实施文件,尽管有多个TU
使用完整的结构定义,最好放置
专用头文件中的定义仅在以下文件之间共享
实施结构。我注意到,是否
专用标头位于公共标头之前或之后。

也许这对您已经很明显了。我不需要思考
在此之前详细介绍一下。

最佳答案

在C1中,它们引用相同的类型。 C99§6.2.1定义了存在的范围:


2对于标识符指定的每个不同实体,
标识符仅在以下区域内可见(即可以使用)
程序文本称为其作用域。由指定的不同实体
同一标识符具有不同的范围或名称不同
空格。有四种作用域:函数,文件,块和
功能原型。 (函数原型是对
声明其参数类型的函数。)


函数作用域仅适用于标签(如本节后面部分所述)。块范围适用于在块中声明的标识符-块是由复合语句,迭代语句和选择语句创建的(而不是由struct声明或复合初始化程序创建的)。函数原型作用域适用于在函数原型声明中声明的标识符。

这些都不适用于您的示例-您示例中所有struct uperms_entry的提及均在文件范围内。

C99§6.7.2.3说:


1具有以下内容的所有结构,联合或枚举类型的声明
相同的作用域和使用相同的标记声明相同的类型。在定义内容的列表的大括号之前,该类型不完整,此后完成。


这很清楚,适用于您的情况。

该部分的第8段适用于struct uperms_entry的第一次提及:


8如果出现形式为struct-or-union标识符的类型说明符
除上述形式之一的一部分外,无其他声明
标签的标识符可见,然后声明不完整
结构或联合类型,并将标识符声明为
这种类型。


因此,这时它在文件范围内被声明为不完整的类型。第6段适用于struct uperms_entry的第二次提及:


6形式为struct-or-union的类型说明符
identifieropt {struct-declaration-list}或枚举
标识符{枚举器列表}或枚举标识符{枚举器列表
,}声明结构,联合或枚举类型。列表定义
结构内容,联合内容或枚举内容。如果
提供了标识符,类型说明符还声明了
该类型的标签的标识符。


因此,在该typedef声明末尾的}之后,它现在是一个完整的类型。

附带的问题尚无定论。


1.我相信在C ++中并非如此。

关于c - C标准是否认为此 header 中存在一种或两种“struct uperms_entry”类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11697705/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com