Не могу скомпилировать C ++ в Ubuntu с помощью GCC - Включить / Проблемы с библиотекой (collect2: ld вернул 1 состояние выхода) - PullRequest
4 голосов
/ 20 августа 2010

Думаю, я что-то не связываю, верно?

Я хочу позвонить ABC.cpp, для которого нужны XYZ.h и XYZ.cpp.Все в моем текущем каталоге, и я попробовал #include <XYZ.h>, а также #include "XYZ.h".

Запуск $ g++ -I. -l. ABC.cpp в терминале Ubuntu 10 дает мне:

`/tmp/ccCneYzI.o: In function `ABC(double, double, unsigned long)':
ABC.cpp:(.text+0x93): undefined reference to `GetOneGaussianByBoxMuller()'
collect2: ld returned 1 exit status`

ВотСводка ABC.cpp:

#include "XYZ.h"
#include <iostream>
#include <cmath>

using namespace std;

double ABC(double X, double Y, unsigned long Z)
{
...stuff...
}

int main()
{
...cin, ABC(cin), return, cout...
}

Вот XYZ.h:

#ifndef XYZ_H
#define XYZ_H

double GetOneGaussianByBoxMuller();
#endif

Вот XYZ.cpp:

#include "XYZ.h"
#include <cstdlib>
#include <cmath>

// basic math functions are in std namespace but not in Visual C++ 6
//(comment's in code but I'm using GNU, not Visual C++)

#if !defined(_MSC_VER)
using namespace std;
#endif



double GetOneGaussianByBoxMuller()
{
...stuff...
}

Я использую версию компилятора GNUg++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3.

Это мой первый пост;Я надеюсь, что я включил все, что кому-то нужно знать, чтобы помочь мне.На самом деле я прочитал «Связанные вопросы» и статью Gough , перечисленные в одном из ответов, а также искал сообщение об ошибке.Однако я до сих пор не могу понять, как это относится к моей проблеме.

Заранее спасибо!

Ответы [ 3 ]

9 голосов
/ 20 августа 2010

Когда вы запускаете g++ -I. -l. ABC.cpp, вы просите компилятор создать исполняемый файл из ABC.cpp. Но код в этом файле отвечает на функцию, определенную в XYZ.cpp, поэтому исполняемый файл не может быть создан из-за этой отсутствующей функции.

У вас есть два варианта (в зависимости от того, что вы хотите сделать). Либо вы даете компилятору все исходные файлы одновременно, чтобы он имел все определения, например

 g++ -I. -l. ABC.cpp XYZ.cpp

или вы используете опцию -c для компиляции в ABC.cpp с объектным кодом (.obj в Windows, .o в Linux), который можно связать позже, например,

 g++ -I. -l. -c ABC.cpp

Который выдаст ABC.o, который позже можно связать с XYZ.o для создания исполняемого файла.

Редактировать: В чем разница между включением и включением # 1018 *

Для полного понимания этого требуется понимание того, что происходит при компиляции программы на C ++, чего, к сожалению, даже многие люди, считающие себя программистами на C ++, нет. На высоком уровне компиляция программы на C ++ проходит три этапа: предварительная обработка, компиляция и компоновка.

1024 * Препроцессирование *

Каждая строка, начинающаяся с #, представляет собой директиву препроцессора , которая оценивается на этапе предварительной обработки. Директива #include является буквально копированием и вставкой. Если вы напишите #include "XYZ.h", препроцессор заменит эту строку всем содержимым XYZ.h (включая рекурсивные вычисления #include в XYZ.h).

Цель включения - сделать объявления видимыми. Чтобы использовать функцию GetOneGaussianByBoxMuller, компилятор должен знать, что GetOneGaussianByBoxMuller является функцией, и знать, какие (если они есть) аргументы, которые он принимает, и какое значение он возвращает, компилятору необходимо увидеть объявление для него. , Объявления помещаются в заголовочные файлы, а заголовочные файлы включены, чтобы сделать объявления видимыми для компилятора до момента использования.

Компиляция

Эта часть запускает компилятор и превращает ваш исходный код в машинный код. Обратите внимание, что машинный код - это не то же самое, что исполняемый код. Исполняемый файл требует дополнительной информации о том, как загрузить машинный код и данные в память, и как при необходимости использовать внешние динамические библиотеки. Это не сделано здесь. Это та часть, где ваш код переходит с C ++ на необработанные машинные инструкции.

В отличие от Java, Python и некоторых других языков, C ++ не имеет понятия «модуль». Вместо этого C ++ работает в терминах единиц перевода . Почти во всех случаях единица перевода соответствует одному файлу исходного кода (без заголовка), например ABC.cpp или XYZ.cpp. Каждый модуль перевода компилируется независимо (независимо от того, запускаете ли вы для них отдельные команды -c или передаете их компилятору сразу).

Когда исходный файл компилируется, препроцессор запускается первым и выполняет #include вставку копии, а также макросы и другие действия, которые выполняет препроцессор. В результате получается один длинный поток кода C ++, состоящий из содержимого исходного файла и всего включенного в него (и всего, что включено в него, и т. Д.). Этот длинный поток кода является единицей перевода.

Когда модуль трансляции компилируется, каждая функция и каждая используемая переменная должны быть объявлены . Компилятор не позволит вам вызвать функцию, для которой нет объявления, или использовать глобальную переменную, для которой нет объявления, потому что тогда он не будет знать типы, параметры, возвращаемые значения и т. Д., Участвующие и не сможет генерировать разумный код. Вот почему вам нужны заголовки - имейте в виду, что на данный момент компилятор даже не знает о существовании каких-либо других исходных файлов; он рассматривает только этот поток кода, созданный обработкой директив #include.

В машинном коде, созданном компилятором, нет таких вещей, как имена переменных или имена функций.Все должно стать адресом памяти.Каждая глобальная переменная должна быть преобразована в адрес памяти, где она хранится, и каждая функция должна иметь адрес памяти, к которому переходит поток выполнения при ее вызове.Для вещей, которые определены (т. Е. Для функций, реализованных ) в модуле перевода, компилятор может назначить адрес.Для вещей, которые только объявлены (обычно в результате включенных заголовков) и не определены, компилятор на данный момент не знает, каким должен быть адрес памяти.Эти функции и глобальные переменные, для которых компилятор имеет только объявление, но не определение / реализацию, называются внешними символами , и предполагается, что они существуют в другой единице перевода.На данный момент их адреса памяти представлены заполнителями.

Например, при компиляции единицы перевода, соответствующей ABC.cpp, он имеет определение (реализацию) ABC, поэтому он может назначать адресфункция ABC и где бы в этом блоке перевода не вызывался ABC, она может создать инструкцию перехода по этому адресу.С другой стороны, хотя его объявление является видимым, GetOneGaussianByBoxMuller не реализовано в этом модуле перевода, поэтому его адрес должен быть представлен с помощью заполнителя.

Результатом компиляции модуля перевода является объектный файл (с суффиксом .o в Linux).

Linking

Одно из основных заданий компоновщика - resol внешние символы.То есть компоновщик просматривает набор объектных файлов, видит их внешние символы и затем пытается выяснить, какой адрес памяти должен быть им назначен, заменяя заполнитель.

В вашем случае функцияGetOneGaussianByBoxMuller - это , определенный в единице перевода, соответствующей XYZ.cpp, поэтому внутри XYZ.o ему был назначен определенный адрес памяти.В единице перевода, соответствующей ABC.cpp, было только объявлено , поэтому внутри ABC.o это только заполнитель (внешний символ).Компоновщик, если ему даны ABC.o и XYZ.o, увидит, что ABC.o нужен адрес, заполненный для GetOneGaussianByBoxMuller, найдите этот адрес в XYZ.o и замените заполнитель в ABC.o на него.Адреса для внешних символов также можно найти в библиотеках.

Если компоновщик не может найти адрес для GetOneGaussianByBoxMuller (как это происходит в вашем примере, когда он работает только на ABC.o, из-за того, что не передал XYZ.cpp компилятору),он сообщит о неразрешенной внешней ошибке символа, также описанной как неопределенная ссылка .

Наконец, после того, как компилятор разрешил все внешние символы, он объединяет все объект, свободный от заполнителей.код, добавляет всю информацию о загрузке, которая нужна операционной системе, и создает исполняемый файл.Тада!

Обратите внимание, что при всем этом имена файлов не имеют значения, один бит.Это соглашение о том, что XYZ.h должно содержать объявления для вещей, которые определены в XYZ.cpp, и это хорошо для поддерживаемого кода, чтобы организовать вещи таким образом, но компилятору и компоновщику все равно,это правда или нет.Компоновщик просматривает все предоставленные ему объектные файлы и только предоставленные им объектные файлы, чтобы попытаться разрешить символ.Он не знает и не заботится, в каком заголовке содержалось объявление символа, и он не будет пытаться автоматически извлекать другие объектные файлы или компилировать другие исходные файлы для разрешения отсутствующего символа.

... wowэто было долго.

5 голосов
/ 20 августа 2010

Попробуйте

g++ ABC.cpp XYZ.cpp

Если вы хотите скомпилировать отдельно, вам нужно создать объектные файлы:

g++ -c ABC.cpp
g++ -c XYZ.cpp
g++ ABC.o XYZ.o
1 голос
/ 28 ноября 2011

Жаль, что я прочитал их, когда у меня были следующие проблемы:

...