【问题标题】:Link error during gcc compile with makefile使用 makefile 进行 gcc 编译期间的链接错误
【发布时间】:2013-10-07 10:01:38
【问题描述】:

(在继续之前,
没有释放和编写代码中从未使用过的变量,
用于测试工具)

我写了这样的代码和这样的 Makefile:

unread_two.h

#ifndef __UNREAD_TWO_H
#define __UNREAD_TWO_H

const int SIZEOF_INT = sizeof(int);
int addTwo();

unread_twomain.c

#include <stdio.h> 
#include <stdlib.h>
#include "unread_two.h"

int main(int argc, char *argv[])
{
    int *x;

    x = (int *)malloc(SIZEOF_INT);
    x = addTwo();
    free(x);

    return 0;
}

unread_two.c

#include <stdio.h>
#include <stdlib.h>
#include <unread_two.h>

int addTwo()
{
    int *y, *z, sum;
    y = (int *)malloc(SIZEOF_INT);
    z = (int *)malloc(SIZEOF_INT);

    *y = 3;
    sum = *y + *y;
    return sum;
}

生成文件

CC=gcc
CCFLAGS=-g

%.o: %.c
    $(CC) -c $< $(CCFLAGS)

all: unread_two
clobber: clean
    rm -f *~ \#`\# core

clean:
    rm -f unread_two *.o

unread_two: unread_twomain.o unread_two.o

unread_twomain.o: unread_two.h

unread_two.o: unread_two.h

当我把 make all ,这个消息出现:

unread_twomain.o:(.rodata+0x0): multiple definition of `SIZEOF_INT'
unread_two.o:(.rodata+0x0): first defined here
collect2: error: ld returned 1 exit status

我应该解决什么问题?

【问题讨论】:

  • 最快的解决方法是在标题中SIZEOF_INT 的定义前面添加static(但这不太可能是一个好的长期解决方案,如果只是因为你可以最终得到两个对 SIZEOF_INT 的实际值存在分歧的目标文件)。在 C++ 中,常量会被有效地处理,但是 C 会创建两个初始化变量,每个源文件一个,并且您不能将这两个源文件链接在一起。

标签: c gcc linker makefile


【解决方案1】:

您不应该定义 SIZEOF_INT 在标头中,否则当您在多个编译单元中包含此标头时,您将获得多个定义,正如您所见。而是在标题中声明它并在源文件中定义它:

// unread_two.h

extern const int SIZEOF_INT;         // *declare* SIZEOF_INT


// unread_two.c

const int SIZEOF_INT = sizeof(int);  // *define* SIZEOF_INT


或者,在这种特殊情况下,您可能有理由使用“old skool”方式使用宏:

// unread_two.h

#define SIZEOF_INT sizeof(int)

【讨论】:

  • @LocustSpectre 对于常量我推荐宏方式,可以让编译器更好地优化。如果这解决了您的问题,您也应该接受答案。
【解决方案2】:

您实际上有 两个 错误,一个是您报告的错误,另一个是更狡猾的错误。

您遇到错误的问题是常量SIZEOF_INT 是在您包含头文件的所有源文件中定义的。包含防护仅防止在同一源文件中多次包含(或从技术上讲是翻译单元) ,但不反对您在多个来源中包含相同的文件。这意味着编译器将在unread_twomain.o 和unread_two.o 中创建SIZEOF_INT 的定义,然后链接器会报错。

对此的解决方案是仅在头文件中声明常量,然后在单个源文件中定义它。


另一个问题是,在main 中,您将x 创建为指针,并为它分配内存(顺便说一下,您不应该对malloc 的返回进行类型转换),然后从@ 获取结果987654328@ 指向此指针。但是addTwo 不返回指针,它返回一个直接值,所以你让指针x 指向地址6,我猜这不是你打算做的。当您尝试释放x 指向的内存时,这将导致未定义的行为,很可能会导致崩溃。

在您的程序中,您根本不必使用指针。只需使用普通的非指针变量:

int addTwo()
{
    int y = 3;
    int sum = y + y;

    return sum;
}

和

int main(int argc, char *argv[])
{
    int x = addTwo();

    return 0;
}

【讨论】:

  • 谢谢,我第一个通过修复标题来修复,第二个通过添加 *.我需要这些变量作为指针来查看我的工具是否检测到自初始化以来从未读取过的内存位置。
  • 顺便说一句,你能解释一下“(顺便说一句,你不应该对 malloc 的返回进行类型转换)吗?”
  • @LocustSpectre 类型转换如果您忘记包含正确的头文件,返回将隐藏。这可能会导致您发出警告,但仍然可以正常编译,但在运行程序时很可能会导致奇怪的崩溃。
猜你喜欢
  • 2013-03-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-15
  • 2020-11-11
  • 2014-05-30
相关资源
最近更新 更多