是来自源文件的此结构未定义行为

时间:2013-05-16 16:52:12

标签: c++ c++11 struct g++ undefined-behavior

我有源文件s.cpp的结构:

struct s{
unsigned long long a;
s(unsigned long long b){a=b;}
unsigned long long get(){return a;}
};

在主文件中,当然用g ++ main.cpp s.cpp简要语法编译

struct s{
s(unsigned long long b);
unsigned long long get();
};

s s1(123456);
//some code
unsigned long long c=s1.get();

现在我知道它不会编译返回未定义的引用,这是一种耻辱。必须在s.cpp中的括号之外定义ctor和get()。我想知道是否有可能不需要的g ++标志。 但主要的问题是,s1.get()安全还是未定义的行为?

修改

感谢您的回复,但我仍然不了解该机制。 所以在本例中是s.cpp:

struct teste{
    int a=0;
    teste(int b);
    int g();
};
teste::teste(int b){a+=b;}
int teste::g(){ return a;}

并在主要:

struct teste{
    int c=1;
    teste(int b);
    int g();
};

teste ato(8);
printf("\n%d",ato.g());

考虑到内存分配,不应该在实际执行时返回9而不是8吗? 即使我将c更改为a,使其完全相等但是1,它似乎总是从s.cpp看a和它的init值。 除了主要方法以外的其他东西的声明是超级的。

5 个答案:

答案 0 :(得分:2)

你有一个奇怪的声明和定义组合。

首先,看起来你的类声明(你应该把它放在一个头文件中,包含在main中)应该是这样的:

struct s
{
  unsigned long long a;
  s(unsigned long long b);
  unsigned long long get();
};

然后,您的实现应该类似于

s::s(unsigned long long b) a(b) {}

unsigned long long  s::get() { return a; }

答案 1 :(得分:2)

不,这不安全 - 如果你能设法让它编译,那就是未定义的行为。它直接违反了一个定义规则,特别是C ++03§3.2/ 5,它说:

  

在提供每种类型的程序中,类类型(第9条)[...]可以有多个定义   定义出现在不同的翻译单元中,并且定义满足以下要求。   鉴于在多个翻译单元中定义了名为D的实体,则为    - D的每个定义应由相同的令牌序列组成;和
   ...省略了更多条件......

     

[...]如果   D的定义满足所有这些要求,那么程序应该表现得好像只有一个   D的定义。如果D的定义不满足这些要求,则行为未定义。

由于您的两个类定义 not 由相同的标记序列组成,因此行为未定义。

这里的C ++ 11行为完全相同。

答案 2 :(得分:1)

如果我理解正确,那么问题是在一个文件中使用声明和定义以及在不同的翻译单元中使用不同的声明是否合法,而不是在头文件中声明。

当且仅当所有翻译单元 * 中的所有相同类型的声明完全相同时,它才是合法的。但是如果您修改其中一个声明,或者任何其他更改会影响确切的定义,您将破坏ODR并导致未定义的行为,这将是容易出错的。

* :不仅声明必须在文本上相同,而且每个符号的查找必须产生相同的实体,基本上它们必须真正引用一个且仅一个事物。

答案 3 :(得分:0)

这里的问题是s的成员函数在s.cpp中被隐式定义inline。这限制了这些成员函数仅在s.cpp的转换单元中定义。在main.cpp中,编译器只能看到声明。因此链接器错误。 (我相信这被称为“内部联系”或类似的东西,如果有人可以验证?)。

如上所述,将s的函数定义移到之外会解决编译器错误。原因是定义不再是inline。因此,该定义现在可以链接到main.cpp并在main.cpp中使用。所以新代码是明确定义的c ++。

关于inline函数的事情是它们必须在使用它们的每个翻译单元中定义。因此,inline函数通常放在头文件中,因此简单#include就足够了。

答案 4 :(得分:0)

您正在混合使用相同名称的两个不同结构,可能在同一名称空间中,这可能会导致问题。

在s.cpp中(我稍微修改了一下代码以便更容易理解):

struct s{ //I'll refer to this struct as S1 below
    int _a;
    s(int a) : _a(a) {}
    int get() {return _a;}
};

在main.cpp

// you added this struct declaration because otherwise it desn't compile
// (providing there is no silly #include "s.cpp")
// But it actually declares ANOTHER struct called s as well
struct s{ // I will refer to this struct as S2 below
    s(int a);
    int get();
};

s my_s(123);
//some code
int c = my_s.get(); // the compiler checks that S2 contains a get function: OK

现在,发生的事情是链接器将在同一名称空间中看到两个s符号,并且此处启动的问题对于调试来说真的很烦人。你可能会得到你的123值,一切似乎都能正常工作但是你在发布时编译(或更改编译器,或更改平台,或重新启动你的机器,或者你认为不应该改变的任何其他事情)你的代码的行为)它不再起作用了......

为了避免这种问题,当你想使用一个struct时,需要在使用它之前声明它。在s.h中声明它并在s.cpp中定义它(我想这是你在.cpp文件中尝试做的,但是你需要分别定义每个函数):

// s.h
struct s{
    unsigned long long a;

    s(unsigned long long b);
    unsigned long long get();
};

// s.cpp
s::s(unsigned long long b) : a(b) {}

unsigned long long s::get() { return a; }

或者内联.h文件中的所有内容并将其包含在main.cpp中。