代码之家  ›  专栏  ›  技术社区  ›  V0ldek

作用域中的结构与函数定义

  •  28
  • V0ldek  · 技术社区  · 7 年前

    据我所知,这在C中是合法的:

    Fo.C

    struct foo {
       int a;
    };
    

    酒吧

    struct foo {
        char a;
    };
    

    但同样的功能也是非法的:

    Fo.C

    int foo() {
        return 1;
    }
    

    酒吧

    int foo() {
        return 0;
    }
    

    会导致链接错误(函数的多重定义 foo )

    为什么?结构名和函数名之间的区别是什么使得C不能处理一个而不能处理另一个? 这种行为是否也扩展到C++?

    6 回复  |  直到 7 年前
        1
  •  24
  •   haccks    7 年前

    为什么?

    struct foo {
       int a;
    };
    

    定义用于创建对象的模板。它不创建任何对象或函数。除非 struct foo 在代码中的某个地方使用,就编译器/链接器而言,这些代码行也可能不存在。

    请注意,C和C++如何处理不兼容是有区别的。 struct 定义。

    结构foo 在您发布的代码中,只要您不混合使用C程序就可以了。

    然而,它在C++中是不合法的。在C++中,它们具有外部链接,必须定义相同。见 3.2 One definition rule/5 更多详情。

        2
  •  18
  •   AnT stands with Russia    7 年前

    这种情况下的区别概念称为 联动装置 .

    在C结构中,union或enum标记具有 无联动装置 . 它们实际上在其范围内是本地的。

    6.2.2标识符链接
    以下标识符没有链接:声明为除 对象或函数;声明为函数参数的标识符;块范围 未使用存储类说明符声明的对象的标识符 extern .

    不能在同一范围内重新声明它们(除非如此调用 提前声明 )但它们可以在不同的范围内自由地重新声明,包括不同的翻译单元。在不同的范围中,它们可以声明完全独立的类型。这就是您在示例中所拥有的:在两个不同的翻译单元中(即在两个不同的文件范围中),您声明了两个不同且不相关的 struct foo 类型。这是完全合法的。

    同时,函数在C中有链接。在您的示例中,这两个定义定义了相同的函数。 foo 具有 外部的 联动装置。并且不允许在整个程序中提供任何外部链接函数的多个定义

    6.9外部定义
    链接在表达式中使用(不是作为操作数的一部分 sizeof _Alignof 运算符,其结果为整数常量),位于整个 程序标识符应该只有一个外部定义;否则, 不得超过一个。


    C++中的概念 联动装置 扩展:它将特定的链接分配给更广泛的实体,包括类型。在C++类中有链接类型。命名空间范围中声明的类具有 外联动装置 . C++的一个定义规则明确指出,如果一个具有外部链接的类具有多个定义(跨不同的翻译单位),则应该在所有这些翻译单元中等价地定义。 http://eel.is/c++draft/basic.def.odr#12 )所以,在C++中 struct 定义是非法的。

    您的函数定义在C++中仍然是非法的,因为C++规则(但基本上与C中的原因相同)。

        3
  •  12
  •   Jonathan Wakely    7 年前

    函数定义都声明一个名为 foo 对于外部链接,C标准表示,对于具有外部链接的实体,不能有多个定义。您定义的结构类型不是具有外部链接的实体,因此可以有多个 struct foo .

    如果使用相同的名称声明具有外部链接的对象,则这将是一个错误:

    Fo.C

    struct foo {
       int a;
    };
    struct foo obj;
    

    酒吧

    struct foo {
        char a;
    };
    struct foo obj;
    

    现在有两个对象被调用 obj 两者都有外部链接,这是不允许的。

    即使其中一个对象只是声明的,而不是定义的,这仍然是错误的:

    Fo.C

    结构foo{
    国际会计准则;
    };
    结构foo obj;
    

    酒吧

    struct foo {
        char a;
    };
    extern struct foo obj;
    

    这是未定义的,因为 OBJ 引用同一对象,但它们没有兼容的类型(因为 结构foo 在每个文件中定义不同)。

    C++有类似但更复杂的规则来解释。 inline 功能和 内联的 变量、模板和其他C++特性。在C++中,相关的要求被称为一个定义规则(ODR)。一个显著的区别是C++甚至不允许这两种不同。 struct 定义,即使它们从未用于声明具有外部链接或在翻译单元之间“共享”的对象。

        4
  •  3
  •   dbush    7 年前

    两份声明 struct foo 彼此不兼容,因为成员的类型不同。在每一个翻译单元中同时使用它们是可以的,只要你不做任何事情来混淆两者。

    例如,如果您这样做:

    struct foo {
       char a;
    };
    
    void bar_func(struct foo *f);
    
    void foo_func()
    {
        struct foo f;
        bar_func(&f);
    }
    

    酒吧:

    struct foo {
       int a;
    };
    
    void bar_func(struct foo *f)
    {
        f.a = 1000;
    }
    

    你会调用 undefined behavior 因为 结构foo 那个 bar_func 预期与不兼容 结构foo 那个 foo_func 正在供应。

    结构的兼容性详见 C standard :

    如果两种类型的类型相同,则它们具有兼容的类型。用于确定两种类型是否兼容的其他规则是 以及6.7.6中的声明人。此外,两种结构,联合,或 在单独的翻译单元中声明的枚举类型是兼容的 如果他们的标签和成员满足以下要求:如果 用标签声明,另一个用同一个标签声明。 如果两者都在各自翻译的任何地方完成 单位,则以下附加要求适用:应 在成员之间进行一对一的通信,以便 对应的一对成员用兼容类型声明;如果 使用对齐说明符声明对的一个成员,即 另一个用等价的对齐说明符声明;如果一个 对的成员用名称声明,另一个用名称声明 同名。对于两个结构,相应构件应为 以相同顺序声明。对于两个结构或工会, 相应的位字段应具有相同的宽度。两个人的 枚举时,相应的成员应具有相同的值。

    引用同一对象或函数的所有声明都应具有兼容类型;否则,该行为是未定义的。

    总之,这两个例子 结构foo 必须具有具有相同名称和类型且顺序相同的成员才能兼容。

    需要这样的规则,以便 struct 可以在头文件中定义一次,该头随后包含在多个源文件中。这导致 结构 在多个源文件中定义,但每个实例都是兼容的。

        5
  •  2
  •   molbdnilo    7 年前

    不同的是名称和存在的不同;结构定义不存储在任何地方,它的名称只在编译期间存在。
    (程序员有责任确保同名结构的使用不发生冲突。否则,我们亲爱的老朋友不明确的行为就来了。)

    另一方面,函数需要存储在某个地方,如果它有外部链接,则链接器需要它的名称。

    如果你发挥你的作用 static 因此,它们在各自的编译单元之外是“不可见”的,链接错误将消失。

        6
  •  2
  •   armagedescu    7 年前

    要从链接器中隐藏函数定义,请使用关键字static。

    Fo.C

        static int foo() {
            return 1;
        }
    

    酒吧

        static int foo() {
            return 0;
        }