为什么这里的符号未解决?

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

Why are symbols unresolved here?

问题

I've translated the content you provided:

我已经阅读了这里

其中提到:

> 一个仅使用inline定义的函数。始终会生成独立的目标代码。在整个程序中,你只能编写一个这样的定义。如果你想要从其他翻译单元中使用它,那么你需要在头文件中放置一个声明;但在这些翻译单元中它将不会被内联。

然而,在我的最小可重现示例中:

test.c

inline                                                                                                                                                                         
int foo(void)                                                                                                                                                                  
{                                                                                                                                                                              
  return 0;                                                                                                                                                                    
}                                                                                                                                                                              
                                                                                                                                                                               
int main(void)                                                                                                                                                                 
{                                                                                                                                                                              
  foo();                                                                                                                                                                       
}  

收到以下错误信息:

cc -std=c99 -Wall -Wextra -Wshadow -Wpedantic test.c -o test                                                                                                                   
Undefined symbols for architecture x86_64:                                                                                                                                     
  "foo", referenced from:                                                                                                                                                      "main" in test-febb67.o                                                                                                                                                   
ld: symbol(s) not found for architecture x86_64                                                                                                                                
clang: error: linker command failed with exit code 1 (use -v to see invocation)                                                                                                
make: *** [Makefile:4: test] Error 1 

因此,似乎代码没有被正确生成。在gcc中也存在相同的问题。这里发生了什么?具体而言,我想知道以下几点的区别:

这个修复了它。

inline foo(void) { /* 定义 */ }
foo(void);

这个修复了它。

foo(void) { /* 定义 */ }
inline foo(void);

以及我的情况:
错误。

inline foo(void) { /* 定义 */ }

我在这里看到可能是因为它具有外部链接性,但没有提供外部定义;然而,它仅在main中引用,并且在单个翻译单元(内部)中。未解析的符号在哪里以及为什么?它必须是在main中的调用。

我发现在打开-O2时,就没有问题,因为代码最初没有被生成,并且默认情况下不会被内联。

这里没有回答我的问题。我不是在问关于static inline修复的问题,而是为什么在这种情况下它与仅使用inline不同待遇,我仍然不理解这一点。为什么它总是导致生成目标代码?

我想了解为什么这个问题首次出现,因为我读到它将生成代码。这个问题是否明确存在于标准中?

英文:

I've read here:

and it stated:

> A function defined with inline on its own. Stand-alone object code is always emitted. You can only write one definition like this in your entire program. If you want to use it from other translation units to the one where it is defined, you put a declaration in a header file; but it would not be inlined in those translation units.

however, in my minimal reproducible example:

test.c

inline                                                                                                                                                                         
int foo(void)                                                                                                                                                                  
{                                                                                                                                                                              
  return 0;                                                                                                                                                                    
}                                                                                                                                                                              
                                                                                                                                                                               
int main(void)                                                                                                                                                                 
{                                                                                                                                                                              
  foo();                                                                                                                                                                       
}  

receives

cc -std=c99 -Wall -Wextra -Wshadow -Wpedantic test.c -o test                                                                                                                   
Undefined symbols for architecture x86_64:                                                                                                                                     
  "_foo", referenced from:                                                                                                                                                     
      _main in test-febb67.o                                                                                                                                                   
ld: symbol(s) not found for architecture x86_64                                                                                                                                
clang: error: linker command failed with exit code 1 (use -v to see invocation)                                                                                                
make: *** [Makefile:4: test] Error 1 

so it seems that code is not being properly emitted. The same problem is also present in gcc. What is going on here? Specifically, I'd like to know, what are the differences here:

This fixes it.

inline foo(void) { /* definition */ }
foo(void);

This fixes it.

foo(void) { /* definition */ }
inline foo(void);

and my case:
Wrong.

inline foo(void) { /* definition */ }

I saw here that it could be because it has external linkage, but an external definition is not provided; however, this is only references in main and in a single translation unit (internally). Where is the symbol unresolved and why? It must be the call in main.

I’ve found that with -O2 turned on, there is no issue, and this is because code is not originally being emitted and it is not being inlined by default.

This did not answer my question. I am not asking about the static inline fix, except in why it is treated differently than just inline in this case, which I still do not understand. Why does that always cause an emission?

I’d like to understand why this is a problem in the first place, since I’d read that it would emit code. Is this present anywhere explicitly in the standard?

答案1

得分: 3

以下是翻译好的部分:

Is this present anywhere explicitly in the standard?

这是C语言规范关于它的说法:

具有内部链接的任何函数都可以是内联函数。对于具有外部链接的函数,有以下限制:如果函数声明中包含 inline 函数说明符,则它也必须在同一翻译单元中定义。如果在翻译单元中的文件范围声明中都包含 inline 函数说明符而没有 extern,则该翻译单元中的定义是一个 内联定义内联定义不会为函数提供外部定义,也不会禁止另一个翻译单元中的外部定义。内联定义提供了对外部定义的替代方案,翻译器可以用它来实现同一翻译单元中对函数的任何调用。不确定调用函数时是使用内联定义还是外部定义。

(C23 6.7.4/7; 强调添加)

以及

外部定义 是对函数(除内联定义之外的函数)或对象的外部声明,也是定义。如果具有外部链接的标识符在表达式中使用(除[...]之外),整个程序中必须有一个外部定义与该标识符相对应[...]。

(C23 6.9/5; 强调添加)

在你的代码中表现如下:

  • 由于没有使用 static 在文件范围内声明函数 foo,因此函数 foo 具有外部链接。

  • 因为 foo 声明为 inline,但没有明确声明为 extern,所提供的定义是内联定义,而不是外部定义。

  • 你的 main() 调用了 foo,而 foo 具有外部链接,因此整个程序中需要有一个外部定义的函数,但没有。因此,你的程序不符合规范。

看起来代码没有正确生成。

不,你弄混了。相反,代码正确地没有生成。内联定义不是外部定义,编译器需要留出空间以供另一个翻译单元提供外部定义。

GCC不必拒绝不正确的程序,可以想象它可能会解决你的问题,但它完全有权根据规范拒绝该程序。当启用优化时,它接受程序是完全一致的。

这个修复了它。

inline foo(void) { /* definition */ }
foo(void);

是的。现在不再满足条件 "如果在翻译单元中的文件范围声明中都包含 inline 函数说明符而没有 extern",因此定义是外部定义,而不是内联定义。

这个修复了它。

foo(void) { /* definition */ }
inline foo(void);

同样。现在 foo 的定义是外部定义。

在同一思路下的另一个解决方案:

extern inline foo(void) { /* definition */ }

当然,你可以通过给 foo 提供内部链接来避免这个问题。有几种方式可以实现这一点,但这是其中一种:

static inline foo(void) { /* definition */ }

如果你觉得这有点奇怪,那么你是对的。通常最好通过给你的内联函数提供内部链接(通过声明它们为 static)来避免这个问题。但如果你不这样做,那么一个合理的使用模式类似于声明和定义外部变量:

  • 将(内联)定义放在头文件中。
  • 选择一个翻译单元来提供外部定义。在这个翻译单元中,
    • 使用 #include 包含头文件,并
    • 使用 extern 说明符重新声明函数。

这样就可以在不重复任何代码的情况下获得所需的外部定义,但我没有看到有什么理由比简单地将定义设置为 static 更有优势。

英文:

> Is this present anywhere explicitly in the standard?

This is what the C language specification says about it:

> Any function with internal linkage can be an inline function. For a
> function with external linkage, the following restrictions apply: If a
> function is declared with an inline function specifier, then it shall
> also be defined in the same translation unit. If all of the file scope
> declarations for a function in a translation unit include the inline
> function specifier without extern, then the definition in that
> translation unit is an inline definition. An inline definition does
> not provide an external definition for the function
, and does not
> forbid an external definition in another translation unit. An inline
> definition provides an alternative to an external definition, which a
> translator may use to implement any call to the function in the same
> translation unit. It is unspecified whether a call to the function
> uses the inline definition or the external definition.

(C23 6.7.4/7; emphasis added)

and

> An external definition is an external declaration that is also a
> definition of a function (other than an inline definition) or an
> object. If an identifier declared with external linkage is used in an
> expression (other than [...]), somewhere in
> the entire program there shall be exactly one external definition for
> the identifier
[...]

(C23 6.9/5; emphasis added)

That plays out for your code like so:

  • By virtue of being declared at file scope without static, function foo has external linkage.

  • Because foo is declared inline but not explicitly extern, the provided definition is an inline definition, not an external definition.

  • Your main() calls foo, which has external linkage, so somewhere in the whole program there needs to be an external definition of that function, but there isn't. Therefore your program is non-conforming.

> so it seems that code is not being properly emitted.

No, you've got that mixed up. Rather: the code is properly not being emitted. An inline definition is not an external definition, and the compiler needs to leave room for an external definition to be provided by another translation unit.

GCC is not required to reject incorrect programs, and it's reasonable to imagine that it might figure out what to do with yours, but it is completely within its rights and the spec to reject the program. That it accepts the program when you enable optimization is fully consistent.

> This fixes it.
>
> inline foo(void) { /* definition */ }
> foo(void);
>

Yes. Now the condition "If all of the file scope declarations for a function in a translation unit include the inline function specifier without extern" is no longer satisfied, so the definition is an external one, not an inline one.

> This fixes it.
>
> foo(void) { /* definition */ }
> inline foo(void);
>

Same. Now foo's definition is an external one.

Another solution in the same vein:

extern inline foo(void) { /* definition */ }

And of course, you can avoid the issue by giving foo internal linkage. There are several variations on that, but this is one of them:

static inline foo(void) { /* definition */ }

.

If you think this is all a bit weird, then you're right. It's usually best to avoid the issue by giving your inline functions internal linkage (by declaring them static). But if you don't do that, then a reasonable usage pattern is similar to that for declaring and defining external variables:

  • put the (inline) definition in a header.
  • choose one translation unit to provide the external definition. In this TU,
    • #include the header, and
    • redeclare the function with the extern specifier.

That gets you the needed external definition without duplicating any code, but I don't see much to recommend it over simply making the definition static.

huangapple
  • 本文由 发表于 2023年8月4日 21:58:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76836628.html
匿名

发表评论

匿名网友

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

确定