Typedefs,(二进制)代码复制和对象文件

时间:2010-12-17 17:33:53

标签: c++ class compiler-construction typedef object-files

假设我编译了一个包含这段代码的源文件,

struct Point
{
    int x;
    int y;
};

struct Size
{
    int x;
    int y;
};

由于PointSize完全相同(就其成员的内存布局而言),编译器是否会在目标文件中生成重复的代码(每个struct一个)?这是我的第一个问题。


现在,让我们从源代码中删除struct Size,然后使用typedef来定义它,就像这样,

typedef Point Size;

编译现在会做什么?它是否会重复代码(因为typedef不只是重命名,而是更多)?


现在假设我们有一个类模板:

template <int UnUsed>
class ConcreteError : public BaseError {
public:
    ConcreteError () :BaseError(), error_msg() {}

    ConcreteError (int errorCode, int osErrorCode, const std::string& errorMessage)
        :BaseError(errorCode, osErrorCode, errorMessage){}
};

然后我们设置了几个定义,比如这个,

typedef ConcreteError<0> FileError;
typedef ConcreteError<1> NetworkError;
typedef ConcreteError<2> DatabaseError;

由于模板参数int UnUsed未在类的实现中使用(只是假设),所以看起来这种情况与具有完全相同的内存布局的多个类完全相同(类似于{的情况) {1}}和struct Point),对象文件中是否会有重复的代码?

如果我们这样做会怎么样,

struct Size

这种情况是否更好,因为现在我们在typedef中使用相同的实例化类?

PS:此类模板代码取自此处:

How to create derived classes from a base class using template programming in C++?


实际上,我不知道编译器如何从源代码生成目标文件,以及它如何处理类名,它的成员,其他符号等等。它如何处理typedef?它有什么用呢,

typedef ConcreteError<0> FileError;
typedef ConcreteError<0> NetworkError;
typedef ConcreteError<0> DatabaseError;

此处是typedef int ArrayInt[100]; 类型吗?代码编译器在目标文件中为它创建了什么? ArrayInt存放在哪里?

3 个答案:

答案 0 :(得分:2)

首先,没有为您包含的第一个结构定义生成代码,因此比较这两种类型是没有意义的。但是在C ++中,类型名称很重要,因此明确区分struct Astruct B

typedef创建类型别名,因此typedef-ed类型 确实是原始类型(它不会创建不同的类型)。

ConcreteError<0>ConcreteError<1>不同。

当参数在数据布局方面相同且函数不需要调用数据上的其他子函数时,我认为没有什么能阻止编译器变得时髦并且将错位函数名称别名化为相同的代码不同的实际类型和函数对两种类型都做了相同的事情,但我认为这不是在实践中真正完成的。实际上有编译器可以做事(参见下面Ben的评论)。

对于最后一个typedef(所有都是ConcreteError<0>的别名),只创建了ConcreteError的一个“版本”(因为只有那个被实例化)。

答案 1 :(得分:2)

示例中没有任何一行会在目标文件中生成任何代码。或者,更确切地说,它根本不会产生任何数据。我认为&#34;代码&#34;仅指处理器指令。

目标文件中的数据分为三个部分:代码,静态数据和常量数据。

代码由实际函数定义(带函数体,而不仅仅是声明)生成,但内联函数除外。内联函数每次实际使用时都会生成代码。模板函数在实例化时生成代码,但多个实例通常由编译器,链接器或两者优化为单个实例。

通过定义全局变量,静态成员变量(再次,实际定义而不仅仅是类中的声明)和静态局部变量来生成静态数据。不能使用const修饰符声明变量以转到静态数据段。

常量数据由与静态数据相同的变量声明生成,但是使用const修饰符,加上浮点文字加上字符串文字加上可能更多文字,具体取决于硬件平台。操作系统实际上可能不允许对硬件级别的常量数据进行写访问,因此如果您尝试在那里编写内容,程序可能会因访问冲突或分段错误而崩溃。

我并不是这类低级别事情的专家,所以我可能错过了一些东西,但我认为我对整体情况描述得很好。

除非我真的遗漏了某些东西,否则程序中的任何其他内容都不会在目标文件中生成任何数据,特别是声明和类型定义。这些由编译器在内部使用。因此,当编译器看到结构定义时,它会记住它由两个32位整数组成。当它找到一些使用该结构的真实代码时,它知道它必须生成适用于两个32位整数的代码,必须至少分配8个字节来存储它,依此类推。但是所有这些信息都是在编译时内部使用的,并没有真正进入目标文件。如果C ++有类似反射的东西,那将是另一个故事。

请注意,虽然定义了很多结构,但是不会向目标文件添加任何内容,它可能会增加编译器本身的内存使用量。因此,您可能会说定义相同的内容会导致编译时的数据重复,但不会导致运行时。

答案 2 :(得分:0)

不,没有未使用过的PODS的重复代码。如果你使用它们,将有两个整数,并可能在内存中分配一些填充。它们当然看起来都是一样的,所以你想称之为有争议的但它不再是“重复”而不是在两个地方使用相同的类型。

不,没有带别名的重复代码。实际上根本没有代码。

可能,取决于编译器是否使用某些优化。

也许。取决于你的typedef是否在不同的翻译单元中使用,以及你的编译器在删除重复的实例化方面有多好。

不,它是int [100]的别名。

在很大程度上,“这个构造产生多少机器代码”的问题完全取决于实现。