在C语言中,在语义上是否可以创建一个不完整类型的左值?

时间:2022-10-16 16:11:54

In the C89 standard, I found the following section:

在C89标准中,我发现了以下部分:

3.2.2.1 Lvalues and function designators

3.2.2.1左值和函数指示符

Except when it is the operand of the sizeof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue). If the lvalue has qualified type, the value has the unqualified version of the type of the lvalue; otherwise the value has the type of the lvalue. If the lvalue has an incomplete type and does not have array type, the behavior is undefined.

除非它是sizeof运算符的操作数,一元&运算符,++运算符, - 运算符或者左运算符。运算符或赋值运算符,没有数组类型的左值被转换为存储在指定对象中的值(并且不再是左值)。如果左值具有限定类型,则该值具有左值类型的非限定版本;否则该值具有左值的类型。如果左值具有不完整的类型且没有数组类型,则行为未定义。

If I read it correctly, it allows us to create an lvalue and applies some operators on it, which compiles and can cause undefined behavior during runtime.

如果我正确读取它,它允许我们创建一个左值并在其上应用一些运算符,这些运算符在运行时编译并可能导致未定义的行为。

Problem is that, I can't think of an example of "an lvalue with incomplete type" which can pass compiler's semantic check and triggers undefined behavior.

问题是,我想不出一个“不完整类型的左值”的例子,它可以通过编译器的语义检查并触发未定义的行为。

Consider that an lvalue is

考虑一个左值是

An lvalue is an expression (with an object type or an incomplete type other than void) that designates an object.

左值是指定对象的表达式(具有除void之外的对象类型或不完整类型)。

and that incomplete type is

那个不完整的类型是

Types are partitioned into object types (types that describe objects), function types (types that describe functions), and incomplete types (types that describe objects but lack information needed to determine their sizes).

类型被划分为对象类型(描述对象的类型),函数类型(描述函数的类型)和不完整类型(描述对象但缺少确定其大小所需的信息的类型)。

A failed program I tried:

我尝试过失败的程序:

struct i_am_incomplete;
int main(void)
{
    struct i_am_incomplete *p;
    *(p + 1);
    return 0;
}

and got the following error:

并得到以下错误:

error: arithmetic on a pointer to an incomplete type 'struct i_am_incomplete'
    *(p + 1);
      ~ ^

Anyone can think of an example on this ? An example of "an lvalue with incomplete type" which can pass compiler's semantic check and triggers undefined behavior.

任何人都可以想到这方面的例子吗? “不完整类型的左值”的示例,它可以传递编译器的语义检查并触发未定义的行为。


UPDATE:

As @algrid said in the answer, I misunderstood undefined behavior, which contains compile error as an option.

正如@algrid在答案中所说,我误解了未定义的行为,其中包含编译错误作为选项。

Maybe I'm splitting hairs, I still wonder the underlying motivation here to prefer undefined behavior over disallowing an lvalue to have an incomplete type.

也许我正在分裂头发,我仍然想知道这里潜在的动机是不喜欢未定义的行为,而不是让左值不完整的类型。

4 个解决方案

#1


2  

Some build systems may have been designed in a way would allow code like:

某些构建系统可能以某种方式设计,允许以下代码:

extern struct foo x;
extern use_foo(struct foo x); // Pass by value

...
use_foo(x);

to be processed successfully without the compiler having to know or care about the actual representation of struct foo [for example, some systems may process pass-by-value by having the caller pass the address of an object and requiring the called function to make a copy if it's going to modify it].

在没有编译器必须知道或关心struct foo的实际表示的情况下成功处理[例如,某些系统可以通过让调用者传递对象的地址并要求被调用的函数来进行传递来处理值传递复制,如果它将修改它]。

Such a facility may be useful on systems that could support it, and I don't think the authors of the Standard wanted to imply that code which used that feature was "broken", but they also didn't want to mandate that all C implementations support such a feature. Making the behavior undefined would allow implementations to support it when practical, without requiring that they do so.

这样的设施在可以支持它的系统上可能是有用的,我不认为标准的作者想要暗示使用该功能的代码被“破坏”,但他们也不想强制要求所有C实现支持这样的功能。使行为未定义将允许实现在实际时支持它,而不要求它们这样做。

#2


5  

I believe this program demonstrates the case:

我相信这个程序演示了这个案例:

struct S;
struct S *s, *f();

int main(void)
{
    s = f();
    if ( 0 )
        *s;   // here
}

struct S { int x; };
struct S *f() { static struct S y; return &y; }

On the marked line, *s is an lvalue of incomplete type, and it does not fall under any of the "Except..." cases in your quote of 3.2.2.1 (which is 6.3.2.1/2 in the current standard). Therefore it is undefined behaviour.

在标记的行上,* s是不完整类型的左值,它不属于3.2.2.1引用中的任何“除......”情况(当前标准中为6.3.2.1/2) 。因此它是未定义的行为。

I tried my program in gcc and clang and they both rejected it with the error that a pointer to incomplete type cannot be dereferenced; but I cannot find anywhere in the Standard which would make that a constraint violation, so I believe the compilers are incorrect to reject the program. Or possibly the standard is defective by omitting such a constraint, which would make sense.

我在gcc和clang中尝试了我的程序,他们都拒绝了它,错误是指向不完整类型的指针无法解除引用;但我无法在标准中找到任何会导致约束违规的地方,因此我认为编译器拒绝该程序是不正确的。或者可能通过省略这样的约束来使标准有缺陷,这是有意义的。

(Since the code is inside an if(0), that means the compiler cannot reject it merely on the basis of it being undefined behaviour).

(由于代码在if(0)内,这意味着编译器不能仅仅根据它是未定义的行为来拒绝它)。

#3


1  

"Undefined behavior" term includes compilation error as an option. From the C89 standard:

“未定义的行为”术语包括编译错误作为选项。从C89标准:

Undefined behavior - behavior, upon use of a nonportable or erroneous program construct, of erroneous data, or of indeterminately-valued objects, for which the Standard imposes no requirements. Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

未定义的行为 - 在使用不可移植或错误的程序构造时,对错误数据或不确定值对象的行为,标准没有要求。允许的未定义行为包括完全忽略不可预测的结果,在翻译或程序执行过程中以环境特征(有或没有发出诊断消息)的特定行为,终止翻译或执行(发布时)一条诊断信息)。

As you can see "terminating a translation" is ok.

正如您所见,“终止翻译”是可以的。

In this case I believe the compilation error you get for you sample code is an example of "undefined behavior" implemented as compile time error.

在这种情况下,我相信您为您获得的编译错误示例代码是作为编译时错误实现的“未定义行为”的示例。

#4


1  

Sure, array types can be that:

当然,数组类型可以是:

extern double A[];
...
A[0] = 1;           // lvalue conversion of A

This has well defined behavior, even if the definition of A is not visible to the compiler. So inside this TU the array type is never completed.

即使编译器看不到A的定义,这也有明确定义的行为。所以在这个TU中,数组类型永远不会完成。

#1


2  

Some build systems may have been designed in a way would allow code like:

某些构建系统可能以某种方式设计,允许以下代码:

extern struct foo x;
extern use_foo(struct foo x); // Pass by value

...
use_foo(x);

to be processed successfully without the compiler having to know or care about the actual representation of struct foo [for example, some systems may process pass-by-value by having the caller pass the address of an object and requiring the called function to make a copy if it's going to modify it].

在没有编译器必须知道或关心struct foo的实际表示的情况下成功处理[例如,某些系统可以通过让调用者传递对象的地址并要求被调用的函数来进行传递来处理值传递复制,如果它将修改它]。

Such a facility may be useful on systems that could support it, and I don't think the authors of the Standard wanted to imply that code which used that feature was "broken", but they also didn't want to mandate that all C implementations support such a feature. Making the behavior undefined would allow implementations to support it when practical, without requiring that they do so.

这样的设施在可以支持它的系统上可能是有用的,我不认为标准的作者想要暗示使用该功能的代码被“破坏”,但他们也不想强制要求所有C实现支持这样的功能。使行为未定义将允许实现在实际时支持它,而不要求它们这样做。

#2


5  

I believe this program demonstrates the case:

我相信这个程序演示了这个案例:

struct S;
struct S *s, *f();

int main(void)
{
    s = f();
    if ( 0 )
        *s;   // here
}

struct S { int x; };
struct S *f() { static struct S y; return &y; }

On the marked line, *s is an lvalue of incomplete type, and it does not fall under any of the "Except..." cases in your quote of 3.2.2.1 (which is 6.3.2.1/2 in the current standard). Therefore it is undefined behaviour.

在标记的行上,* s是不完整类型的左值,它不属于3.2.2.1引用中的任何“除......”情况(当前标准中为6.3.2.1/2) 。因此它是未定义的行为。

I tried my program in gcc and clang and they both rejected it with the error that a pointer to incomplete type cannot be dereferenced; but I cannot find anywhere in the Standard which would make that a constraint violation, so I believe the compilers are incorrect to reject the program. Or possibly the standard is defective by omitting such a constraint, which would make sense.

我在gcc和clang中尝试了我的程序,他们都拒绝了它,错误是指向不完整类型的指针无法解除引用;但我无法在标准中找到任何会导致约束违规的地方,因此我认为编译器拒绝该程序是不正确的。或者可能通过省略这样的约束来使标准有缺陷,这是有意义的。

(Since the code is inside an if(0), that means the compiler cannot reject it merely on the basis of it being undefined behaviour).

(由于代码在if(0)内,这意味着编译器不能仅仅根据它是未定义的行为来拒绝它)。

#3


1  

"Undefined behavior" term includes compilation error as an option. From the C89 standard:

“未定义的行为”术语包括编译错误作为选项。从C89标准:

Undefined behavior - behavior, upon use of a nonportable or erroneous program construct, of erroneous data, or of indeterminately-valued objects, for which the Standard imposes no requirements. Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

未定义的行为 - 在使用不可移植或错误的程序构造时,对错误数据或不确定值对象的行为,标准没有要求。允许的未定义行为包括完全忽略不可预测的结果,在翻译或程序执行过程中以环境特征(有或没有发出诊断消息)的特定行为,终止翻译或执行(发布时)一条诊断信息)。

As you can see "terminating a translation" is ok.

正如您所见,“终止翻译”是可以的。

In this case I believe the compilation error you get for you sample code is an example of "undefined behavior" implemented as compile time error.

在这种情况下,我相信您为您获得的编译错误示例代码是作为编译时错误实现的“未定义行为”的示例。

#4


1  

Sure, array types can be that:

当然,数组类型可以是:

extern double A[];
...
A[0] = 1;           // lvalue conversion of A

This has well defined behavior, even if the definition of A is not visible to the compiler. So inside this TU the array type is never completed.

即使编译器看不到A的定义,这也有明确定义的行为。所以在这个TU中,数组类型永远不会完成。