2016-11-25 2 views
1

Я делаю пример упражнений в учебнике, который я использую для изучения. Все, что мне нужно сделать, это собрать, ссылку и выполните следующие 3 файла:g ++ не удается связать файлы .o в исполняемом файле

//file my.h 
extern int foo; 
void print_foo(); 
void print(int); 

my.h простого заголовочный файл, который объявляет две функции и «глобальный» Int Foo, без начального значения.

//file my.cpp 
#include "my.h" 
#include "std_lib_facilities.h" //not included but not source of error 

void print_foo() 
{ 
    cout << foo << endl; 
} 

void print(int i) 
{ 
    cout << i << endl; 
} 

my.cpp содержит реализацию функций, включенных в my.h. std_lib_facilities.h - это файл из учебника и не является источником ошибки (согласно g ++). Я могу отредактировать его в теле вопроса, если это необходимо.

//file use.cpp 
#include "my.h" 
#include <iostream> 

int main() { 
    foo = 7; 
    print_foo(); 
    print(99) 

    char cc; cin >> cc; 
    return 0; 
} 

use.cpp служит в качестве основного файла реализации в этой программе, и пытается использовать все три объявленных & определенные объекты.

Я использовал двухступенчатый командный подход для построения с использованием g ++. Во-первых, я скомпилированные как .cpp файлы:

g++ -c my.cpp use.cpp 

, которые создали два объектных файлов, my.o и use.o. Я использовал следующую команду, чтобы связать их:

g++ -o myprog my.o use.o 

дает мне эту ошибку:

Undefined symbols for architecture x86_64: 
    "_foo", referenced from: 
     print_foo() in my.o 
     _main in use.o 
    (maybe you meant: __Z9print_foov) 
ld: symbol(s) not found for architecture x86_64 
clang: error: linker command failed with exit code 1 (use -v to see invocation) 

Я попытался положить

int foo; 

в my.h вместо

extern int foo; 

, который дал мне ту же ошибку.

Я попытался использовать флаг

-std=c++11 

, а в результате которого в одной и той же ошибки.

Я использую MacBook Pro с последним macOS (только что обновленным на самом деле), если это помогает с интерпретацией сообщения об ошибке.

Я попытался инициализировать foo, который ничего не изменил.

Кроме того, я пробовал обновлять инструменты командной строки, такую ​​же ошибку.

Насколько я понимаю, ошибка говорит мне, что хотя my.h включен в оба файла, ни один из них не может реально реализовать какую-либо функцию, используя переменную foo (которую он вызывает _foo), несмотря на то, что она явно объявлена в my.h. Я предполагаю, что компоновщик использует неправильные имена под капотом, что делает невозможным ссылку на исполняемый файл. Это связано с тем, что указанная погрешность была отмечена как

__Z9print_foov 

, который нигде не существует ни в одном из файлов.

Это почти похоже на ошибку g ++ или macOS/Command Line в этой точке. Я не хочу добавлять объявления каждый раз, потому что это все равно создает дубликаты символов. Поместите my.cpp и используйте.cpp в один файл, вероятно, будет правильно связываться, но мне нужно убедиться, что я могу связать несколько файлов cpp, потому что в конечном итоге (надеюсь) будет работать с несколькими файлами cpp, которые необходимо связать. Любая помощь приветствуется!

ответ

1

Здесь вы объявляете переменную:

extern int foo; 

и использовать переменную:

cout << foo << endl; 

но вы не определили переменную в любом месте. Ошибка компоновщика говорит, что компоновщик не смог найти определение переменной. Чтобы исправить это, поместите int foo; в область файла в одном из файлов .cpp.

В вопросе вы говорите, что смена extern int foo; на int foo; дает ту же ошибку. Однако, если вы посмотрите более внимательно на сообщение об ошибке, я думаю, вы обнаружите, что он дает другое, о нескольких определениях.

1

Я предлагаю собрать в две команды g++ -Wall -c my.cpp (что дает my.o) и g++ -Wall -c use.cpp (давая use.o), затем связать программу с g++ my.o use.o -o myprog. На самом деле вы должны написать Makefile (см this для вдохновения) и просто запустить make

Ваши единицы перевода my.cpp и use.cpp оба объявляя некоторые extern int foo; переменная, которая не является никогда определена. Так что вам нужно определить его в один один файл (но не в других!), Вероятно, путем добавления (в my.cppтолько, например)

int foo; 

(без extern) или даже с некоторыми явное начальное значение, например int foo = 34;

This comes from the fact that the error mentioned a __Z9print_foov which exists nowhere

Это mangled name, на который ссылается (но не определен) в обоих объектных файлов (смотри также this).

It almost seems like a g++ or macOS/Command Line Tools bug at this point

Вы очень маловероятно, чтобы найти ошибки в компиляторах (как GCC & Clang/LLVM является очень хорошо испытанием, так как они многомиллионных линии свободного программного обеспечения, они имеют остаточных ошибок, но у вас есть больше шансов выиграть в лотерее, чем на ошибку компилятора). Я кодирую с 1974 года, и это случилось со мной только один раз в моей жизни. Более реалистичное отношение должно быть более скромным, и вопрос ваш собственный код (и знания) перед тем, как заподозрить компилятор или построить цепочку.

BTW, всегда скомпилируйте сначала все предупреждения и информацию об отладке (например, g++ -Wall -g и, возможно, также -Wextra). Используйте отладчик gdb. Когда вы уверены, что ваш код не имеет ошибок, вы можете сравнить его, запросив компилятор optimize (так что используйте g++ -Wall -O2, возможно, также с -g для компиляции).

Читайте также linker wikipage. Окунитесь в свой C++ textbook (также см this site и стандарт C++11, например n3337 проекта) для понять разницу между объявляющей и определением некоторыми переменным или функцией. Обычно вы объявляете глобальную переменную extern в некотором распространенном заголовке (включенном в несколько единиц перевода перевода) и определяете его как-то где-то в другом месте, но хорошая практика заключается в том, чтобы избежать большого количества глобальных переменных. См. Также C++17 новые inline переменных.

+0

Объявлено int foo; в my.cpp; это сработало! Спасибо за помощь. Я не понимаю, почему у компоновщика были проблемы, однако, если переменная была объявлена ​​в заголовке и, следовательно, в .cpp-файле, который включает его (если только я что-то не хватает). Какой смысл объявлять extern int foo в заголовке, если мне все равно придется объявить его снова в файле .cpp? Ради соглашения? Я не думаю, что extern делает что-то особенное, но все еще любопытно, так что у меня опять нет этой проблемы. – gasoline

+0

Подробнее о вашей книге на C++ о разнице между объявлением переменной и ее определением. –

+0

Достаточно честный. Комментарий к ошибке был больше разочарован, чем что-либо еще, не намеревался подорвать усилия, которые пошли в gcc haha. Будем стараться быть более смиренными. Спасибо, что нашли время, чтобы дать мне совет, а не только ответить на вопрос! Я прочитал некоторую документацию и понял, что простой int foo, даже без какого-либо инициализированного значения, считается объявлением И определением, в то время как extern int foo в заголовке объявлялся без определения и, следовательно, оставался неопределенным, вызывая мою ошибку. – gasoline