不同的function有不同的地址吗?
考虑这两个function:
void foo() {} void bar() {}
是否保证&foo != &bar
?
同样的,
template<class T> void foo() { }
是否保证&foo<int> != &foo<double>
?
有两个连接器我知道折叠函数定义在一起。
MSVC积极地COMDAT折叠函数,所以具有相同实现的两个函数可以变成一个函数。 作为副作用,这两个函数共享相同的地址。 我的印象是这是非法的,但是我不知道哪里是违法的。
金链接器也折叠function,既safe
, all
设置。 safe
意味着如果一个function地址被占用,它就不会被折叠,即使地址被占用也会折叠。 所以如果函数具有不同的地址,黄金的折叠safe
行为。
虽然折叠可能是意想不到的,并且有依赖于具有不同地址的不同(相同的实现)函数的代码(因此折叠可能是危险的),但在当前C ++标准下实际上是否是非法的? (C ++ 14在这一点上)(当然,如果safe
折叠是合法的)
它看起来像缺陷报告1400:function指针的平等处理这个问题,似乎在说我可以说,这是可以做这种优化,但正如评论指出,有分歧。 它说( 强调我的 ):
根据5.10 [expr.eq]第2段,如果两个函数指针指向相同的函数,则它们只会相等 。 然而,作为优化, 实现目前具有相同定义的混叠函数 。 标准是否需要明确地处理这个优化还不清楚。
答复是:
标准清晰地expression了要求, 实现可以在“假设”规则的约束条件下自由优化 。
问题是询问两个问题:
- 这些指针可以被认为是平等的吗?
- 可以合并这些function吗?
根据评论,我看到了两个解释:
-
这个优化是可以的,标准赋予了这个自由在as if规则下的实现。 第
1.9
条涵盖了“ 如果”规则 ,并且意味着实施只需仿效标准要求的可观察行为。这仍然是我对这个回应的解释。 -
这个问题在眼前是完全被忽略的,声明只是说没有必要调整标准,因为显然这个规范涵盖了这一点,但是这个解释只是给读者一个练习。 虽然我承认,由于答复的简洁,我不能排除这种观点,但最后却是一个毫无益处的回应。 这似乎也不符合在其他
NAD
问题上的反应,据我所知,如果问题存在的话。
标准草案说的是什么
既然我们知道我们正在处理as-if规则 ,我们可以从那里开始,注意1.8
节说:
除非对象是零大小的位域或基类子对象,否则该对象的地址就是它占据的第一个字节的地址。 如果一个对象是另一个对象的子对象,或者如果至less有一个是大小为零的基类子对象,并且它们是不同types的,则两个不是位域的对象可以具有相同的地址; 否则应有明确的地址。 4
注4
说:
在“如果”规则下,允许实现将两个对象存储在相同的机器地址,或者如果程序不能观察到差别,则不存储对象
但该部分的一个说明说:
函数不是一个对象,不pipe它是否以对象的方式占用存储空间
虽然这不是规范性的,但是对于第1
款所述的对象的要求在职能范围内是没有意义的,所以与本说明一致。 所以我们明确地限制了一些例外的混淆对象,但是这样的限制不适用于函数。
接下来我们有第5.10
节平等运算符 ( 强调我的 ):
[…]如果两个指针都为null, 都指向相同的函数,或者两者都代表相同的地址 (3.9.2),则两个指针相等,否则它们会比较不相等。
这告诉我们两个指针是相等的,如果它们是:
- 空指针
- 指向相同的function
- 代表相同的地址
或者两者代表相同的地址,似乎给了足够的纬度允许编译器别名两个不同的函数,并不需要指针指向不同的函数来比较不等。
意见
基思·汤普森(Keith Thompson)做了一些很好的观察,他认为值得回答这个问题,因为他们涉及到核心问题,他说:
如果一个程序打印&foo ==&bar的结果,那是可观察的行为; 所讨论的优化改变了可观察到的行为。
我同意这一点,如果我们能够certificate指标的要求是不平等的,这实际上违反了“如果”的规定,但到目前为止我们不能certificate这一点。
和:
考虑定义空函数并将其地址用作唯一值的程序(考虑<signal.h> / <csignal>中的 SIG_DFL , SIG_ERR和SIG_IGN )。 分配给他们相同的地址会破坏这样的程序
正如我在我的评论中指出的那样,C标准要求这些macros生成不同的值 ,从C11中的7.14
开始:
[…]扩展为常量expression式,其中不同的值具有与第二个参数types兼容的信号函数的返回值,并且其值不等于任何可声明函数的地址[…]
所以尽pipe这个案例被覆盖,也许还有其他一些情况会使这种优化变得危险。
更新
JanHubička是一位gcc
开发人员在GCC 5中撰写了一篇博客文章链接时间和程序间优化改进 ,代码折叠是他所涉及的众多主题之一。
我问他是否对同一地址的相同function进行折叠是否符合行为进行评论,他说这不符合行为,实际上这样的优化会打破gcc
本身:
不符合转换两个函数具有相同的地址,所以MSVC在这里是相当积极的。 这样做,例如,打破海湾合作委员会本身,因为我惊讶的地址比较是在预编译的头文件代码中完成的。 它适用于许多其他项目,包括Firefox。
事后看来,经过几个月的阅读缺陷报告和思考优化问题,我偏向于更为保守地阅读委员会的回应。 取一个函数的地址是可观察的行为,因此折叠相同的函数将违反as-if规则 。
是。 从标准(§5.10/ 1):“相同types的两个指针比较相等的当且仅当它们都是空的,都指向相同的函数,或者两者都代表相同的地址”
一旦它们被实例化, foo<int>
和foo<double>
是两个不同的函数,所以上面也适用它们。
所以有问题的部分显然是这个词或两者都代表了同一个地址(3.9.2) 。
国际海事组织这部分显然是有定义对象指针types的语义。 只适用于对象指针types。
短语引用部分3.9.2,这意味着我们应该看看那里。 3.9.2讨论(其中包括)对象指针表示的地址。 它没有提到函数指针表示的地址。 国际海事组织只留下两种可能的解释:
1)这个短语根本不适用于函数指针。 只留下两个空指针和两个指向相同函数的指针,这两个指针可能大部分是我们期望的。
2)这个短语确实适用。 由于它指的是3.9.2,它没有提到函数指针表示的地址,所以我们可以使任何两个函数指针比较相等。 这是非常意想不到的,当然,比较函数指针是完全无用的。
所以,虽然在技术上可以说(2)是一个有效的解释,但是IMO不是一个有意义的解释,因此应该被忽视。 而且由于不是每个人似乎都同意这一点,我也认为需要在标准中作出澄清。
5.10平等运算符
[expr.eq]
1
==
(等于)和!=
(不等于)运算符组从左到右。 操作数应具有算术,枚举,指针或指向成员types的指针,或者inputstd::nullptr_t
。 运算符==
和!=
都产生true
或false
,即bool
types的结果。 在下面的每种情况下, 应用指定的转换后 ,操作数应该具有相同的types 。
2 如果至less有一个操作数是一个指针,则在两个操作数上执行指针转换(4.10)和限定转换(4.4),以将它们转换为其复合指针types (第5章)。 比较指针的定义如下: 如果两个指针都为空,则指向相同的函数,或者两者都代表相同的地址(3.9.2),否则两个指针比较相等,否则比较不相等。
我们来看看最后一点:
- 两个空指针比较相等。
有利于你的理智。 - 两个指向同一个函数的指针相等。
其他任何事情都是非常令人惊讶的。
这也意味着只有一个inline
函数的线外版本可能会使用它的地址,除非你想让函数指针比较过于复杂和昂贵。 - 两者代表相同的地址。
现在,一个是它的全部。 抛弃这个并且减lessif and only if
一个简单的if
将它留给解释,但是这是一个明确的任务使任何两个function相同 ,只要它不改变符合的程序的可观察的行为。