When is it necessary or meaningful to write &f for a function f or *fp for a function pointer fp (except in: sizeof &f)?

huangapple go评论67阅读模式
英文:

When is it necessary or meaningful to write &f for a function f or *fp for a function pointer fp (except in: sizeof &f)?

问题

从C17标准草案(6.3.2.1 ¶4; 删除了脚注):

函数设计符是具有函数类型的表达式。除非它是sizeof运算符的操作数或一元&运算符的操作数,类型为"返回type的函数"的函数设计符将被转换为具有类型"指向返回type的函数的指针"的表达式。

根据我省略的引用中的脚注,sizeof f(对于函数设计符f)不符合标准。
请注意,C11标准还提到了_Alignof运算符。
请注意,C2x草案还提到了typeof运算符。

因此(这里:f是函数设计符;fp是函数指针):

  1. 一个可以表示函数地址为&f(显式)或f(简短)。
  2. 一个可以进行函数调用,如:
    • (&f)(args)f(args)
    • fp(args)(*fp)(args)

在这里,

  • 左侧(&f)(args)/fp(args)在技术上与函数调用在函数指针上运行一致,
  • 右侧f(args)/(*fp)(args)与函数调用在函数(而不是函数指针)上运行的天真假设一致。

风格上,

  • 此答案&f标记为"始终冗余",
  • (&f)(args)在实践中几乎不使用,
  • f/f(args)/(*fp)(args)是K&R(2e)中使用的风格,
  • f/f(args)/fp(args)是最简短的风格。

(为了完整起见(以及如果其他人想要一个详细的总结),这里有一个更长的表达式列表,它们等效于f(&f)(*******f)(***&***f)(***&**&f)(&**&***f)(&**&**&f)。)

但是:
何时有必要或有意义地

  • &f(其中f是函数设计符)或
  • *fp(其中fp是函数指针)?

(通过"有意义",我指的是程序的行为会发生变化。)

  • 我唯一知道的情况是sizeof &f。(sizeof f是非法的,尽管GCC的C方言允许它(至少在版本12.2.0中是这样),但具有不同的语义:它似乎在所有情况下都返回1。为什么?)
    • 我不理解这一点。让f自动转换为&f将使我们能够简化标准的措辞,并使sizeof f具有有用的语义。 (也许这个限制存在是为了保持与typeof的平行,typeof已经作为编译器扩展存在了一段时间。)
  • 我不确定_Alignof发生了什么。
  • 如果typeof被正式添加到C中的下一个标准中,typeof &f/typeof fptypeof f/typeof *fp之间的区别显然是有意义的。
英文:

From the C17 standard draft (6.3.2.1 ¶4; footnote removed):

> A function designator is an expression that has function type. Except when it is the operand of the sizeof operator, or the unary & operator, a function designator with type "function returning type" is converted to an expression that has type "pointer to function returning type".

  • According to the footnote which I omitted from the quote above, sizeof f (for a function designator f) is non-standards-conformant.
  • Note that the C11 standard additionally mentions the _Alignof operator.
  • Note that the C2x draft additionally mentions the typeof operator.

As a result (here: f is a function designator; fp is a function pointer):

  1. One can indicate the address of a function as &f (explicit) or f (short).
  2. One can make a function call as:
    • (&f)(args) or f(args)
    • fp(args) or (*fp)(args)

Here,

  • left-hand (&f)(args)/fp(args) is consistent with function calls technically operating on function pointers, and
  • right-hand f(args)/(*fp)(args) is consistent with the naive assumption that function calls operate on functions (instead of pointers to functions).

Stylistically,

  • this answer labels &f as "always redundant",
  • (&f)(args) is virtually unused in practice,
  • f/f(args)/(*fp)(args) is the style used in K&R (2e), and
  • f/f(args)/fp(args) is the shortest style.

(For completeness' sake (and in case anyone else wants a nice summary), here is a longer list of expressions that are equivalent to f: (&f), (*******f), (***&***f), (***&**&f), (&**&***f), (&**&**&f).)

But:
When is it ever necessary or meaningful to

  • write &f (where f is a function designator) or
  • write *fp (where fp is a function pointer)?

(By "meaningful", I mean that the behavior of the program is altered.)

  • The only case I am aware of is sizeof &f. (sizeof f is illegal, even though the GCC dialect of C permits it (at least as of version 12.2.0), but with different semantics: It seemingly returns 1 in all(?) cases. Why?)
    • I don't understand this. Letting f auto-convert to &f would let us simplify the standard's phrasing and give sizeof f useful semantics too. (Perhaps this restriction exists to maintain parallelism with typeof, which has been floating around as a compiler extension for a while.)
  • I am not sure what happened with _Alignof.
  • If typeof is officially added to C with the next standard, a distinction between typeof &f/typeof fp and typeof f/typeof *fp would be obviously meaningful.

答案1

得分: 7

由于在许多情况下,函数标识符 `f` 会自动转换为函数的地址,因此 `f` 与 `&f` 在语义上唯一不同的时候是当它没有被转换时。根据 C 2018 6.3.2.1 4,这些情况包括它作为 `sizeof` 或一元 `&` 的操作数。因此我们有:

* `sizeof f` 需要一个诊断消息,因为它违反了 C 2018 6.5.3.4 1 中的约束(`sizeof` 不得应用于具有函数类型的表达式),并且其行为未被 C 标准定义(因为违反了约束),而 `sizeof &f` 则产生指向函数的指针的大小。 (`sizeof f` 尽管如此,根据 C 2018 4 7,它是*符合*代码,尽管不是*严格符合*,因为存在接受它的 C 编译器。)
* `&f` 产生 `f` 的地址,而 `&&f` 需要一个诊断消息,因为它违反了 C 2018 6.5.3.2 1 中的约束(一元 `&` 的操作数应为函数标识符、数组下标或一元 `*` 的结果,或者是不是位字段或用 `register` 声明的 lvalue)。我不知道有哪个 C 编译器接受 `&&f`,因此这不是严格符合的代码,除非我们能够想象并构建一个接受它的 C 编译器。(`& &&f` 中的空格用于分隔标记。否则 `&&` 是一个单一的标记,用于逻辑 AND 运算符。)

我猜测 GCC 接受 `sizeof f` 的原因是它是为了通常支持指针上的地址算术而采取的一部分,甚至是 `void *` 和函数指针,以支持操作系统、调试器、程序加载器和类似软件上的函数和任意内存。使 `sizeof f` 为 1 是设计 GCC 上的函数指针算术以字节为单位运行的一部分。(这是 GCC 的一个不必要的特性,因为我们可以通过要求将它们首先转换为字符类型的指针来支持对函数指针和 `void *` 的算术。然而,我想它是为了支持当时的代码实践而完成的,现在已经是遗留的。)

从语法角度看,`&f` 与 `f` 不同之处在于它更长,因此可能会违反编译器的环境限制。例如,C 2018 5.2.4.1 1 要求编译器在逻辑源行中支持 4095 个字符(至少有一个程序),因此将 `f` 更改为 `&f` 可能会使编译器支持的行长度超过限制。

对于 `*fp` 与 `fp`,类似的推理适用:在许多情况下,`*fp` 被转换为等效于 `fp`,因此只有在它不被转换时才会出现语义差异。因此,`sizeof *fp` 和 `sizeof fp` 与上面讨论的不同。但是对于 `&`,`&*fp` 是函数的地址,而 `&fp` 是指针的地址。(这假设 `fp` 是一个对象,而不是不是 lvalue 的其他表达式。如果它不是对象,那么它会引发与上述 `&` 的讨论相同的问题。)

`*fp` 与 `fp` 与 `&f` 与 `f` 具有相同的环境限制考虑因素。
英文:

Since a function designator f is automatically converted to the address of the function in many situations, the only times when f is semantically different from &f is when it is not converted. Per C 2018 6.3.2.1 4, these are when it is the operand of sizeof or unary &. Thus we have:

  • sizeof f requires a diagnostic message because it violates the constraints in C 2018 6.5.3.4 1 (sizeof shall not be applied to an expression with function type) and its behavior is not defined by the C standard (because of the constraint violation), whereas sizeof &f yields the size of a pointer to the function. (sizeof f is nonetheless conforming code per C 2018 4 7, albeit not strictly conforming, since there exists a C compiler that accepts it.)
  • &f yields the address of f whereas & &f requires a diagnostic message because it violates the constraints in C 2018 6.5.3.2 1 (the operand of unary & shall be a function designator, the result of array subscripting or unary *, or an lvalue other than a bit-field or one declared with register). I am unaware of a C compiler that accepts & &f, so this is not strictly conforming code, except to the extent we could imagine and construct a C compiler that accepts it. (The space in & &f serves to separate the tokens. Otherwise && is a single token, for the logical AND operator.)

My guess at why GCC accepts sizeof f is that it is part of allowing address arithmetic on pointers generally, even void * and pointers to functions, to support code used in operating systems, debuggers, program loaders, and similar software that operates on functions and arbitrary memory. Making sizeof f be 1 is a part of designing GCC’s arithmetic on function pointers to operate in units of bytes. (This is an unnecessary feature of GCC because we could support arithmetic on pointers to functions and void * by requiring they be first converted to a pointer to a character type. However, I suppose it was done to support code practices at the time it was done and is now legacy.)

At a syntax level, &f differs from f in being longer, so it could run afoul of environmental limits of the compiler. For example, C 2018 5.2.4.1 1 requires a compiler support 4095 characters in a logical source line (in at least one program), so changing f to &f could push the line over the length that a compiler supports.

With *fp versus fp, similar reasoning applies: *fp is converted to the equivalent of fp in many situations, so semantic differences arise only when it is not. So sizeof *fp and sizeof fp differ as discussed above. However, for &, &*fp is the address of the function, whereas &fp is the address of the pointer. (This supposes that fp is an object, not some other expression that is not an lvalue. If it is not an object, it raises the same issues as discussed for & above.)

And *fp versus fp has the same environmental limit consideration as &f versus f.

huangapple
  • 本文由 发表于 2023年5月10日 19:34:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/76217910.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定