Что такое «неопределенный ссылочный / неразрешенный внешний символ»
Я попытаюсь объяснить, что такое «неопределенный ссылочный / неразрешенный внешний символ».
note : я использую g ++ и Linux, и все примеры для него
blockquote>Например, у нас есть некоторый код
// src1.cpp void print(); static int local_var_name; // 'static' makes variable not visible for other modules int global_var_name = 123; int main() { print(); return 0; }
и
// src2.cpp extern "C" int printf (const char*, ...); extern int global_var_name; //extern int local_var_name; void print () { // printf("%d%d\n", global_var_name, local_var_name); printf("%d\n", global_var_name); }
Создание объектных файлов
$ g++ -c src1.cpp -o src1.o $ g++ -c src2.cpp -o src2.o
После фазы ассемблера у нас есть объектный файл, который содержит любые экспортируемые символы. Посмотрите на символы
$ readelf --symbols src1.o Num: Value Size Type Bind Vis Ndx Name 5: 0000000000000000 4 OBJECT LOCAL DEFAULT 4 _ZL14local_var_name # [1] 9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 global_var_name # [2]
Я отклонил некоторые строки из вывода, потому что они не имеют значения
Итак, мы видим следующие символы для экспорта.
[1] - this is our static (local) variable (important - Bind has a type "LOCAL") [2] - this is our global variable
src2.cpp ничего не экспортирует, и мы не видели его символов
Свяжите наши объектные файлы
$ g++ src1.o src2.o -o prog
и запустите его
$ ./prog 123
Linker видит экспортированные символы и связывает их. Теперь мы пытаемся раскомментировать строки в src2.cpp, как здесь
// src2.cpp extern "C" int printf (const char*, ...); extern int global_var_name; extern int local_var_name; void print () { printf("%d%d\n", global_var_name, local_var_name); }
, и перестроить объектный файл
$ g++ -c src2.cpp -o src2.o
OK (нет ошибок), потому что мы только строим объектный файл, связь еще не завершена. Попробуйте установить ссылку
$ g++ src1.o src2.o -o prog src2.o: In function `print()': src2.cpp:(.text+0x6): undefined reference to `local_var_name' collect2: error: ld returned 1 exit status
Это произошло потому, что наше local_var_name статично, то есть оно не отображается для других модулей. Теперь глубже. Получить выход фазы перевода
$ g++ -S src1.cpp -o src1.s // src1.s look src1.s .file "src1.cpp" .local _ZL14local_var_name .comm _ZL14local_var_name,4,4 .globl global_var_name .data .align 4 .type global_var_name, @object .size global_var_name, 4 global_var_name: .long 123 .text .globl main .type main, @function main: ; assembler code, not interesting for us .LFE0: .size main, .-main .ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2" .section .note.GNU-stack,"",@progbits
Итак, мы видели, что для local_var_name нет метки, поэтому линкер не нашел его. Но мы хакеры :), и мы можем это исправить. Откройте src1.s в текстовом редакторе и измените
.local _ZL14local_var_name .comm _ZL14local_var_name,4,4
на
.globl local_var_name .data .align 4 .type local_var_name, @object .size local_var_name, 4 local_var_name: .long 456789
i.e. вам должно быть как ниже
.file "src1.cpp" .globl local_var_name .data .align 4 .type local_var_name, @object .size local_var_name, 4 local_var_name: .long 456789 .globl global_var_name .align 4 .type global_var_name, @object .size global_var_name, 4 global_var_name: .long 123 .text .globl main .type main, @function main: ; ...
мы изменили видимость local_var_name и установили его значение в 456789. Попробуйте построить из него объектный файл
$ g++ -c src1.s -o src2.o
ok, см.
$ readelf --symbols src1.o 8: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 local_var_name
В настоящее время local_var_name имеет привязку GLOBAL (LOCAL)
link
$ g++ src1.o src2.o -o prog
и запускает ее
$ ./prog 123456789
ok, мы взломаем его:)
Итак, в результате - «неопределенная ссылка / неразрешенная внешняя ошибка символа» происходит, когда компоновщик не может найти глобальные символы в объектных файлах.
Если вы запускаете без отладки (Ctrl + F5), то по умолчанию он предлагает вам нажать return, чтобы закрыть окно. Если вы хотите использовать отладчик, вы должны поставить точку останова на последней строке.
Использовать:
cin.get();
или
system("pause");
Обязательно сделайте любой из них в конце функции main()
и перед оператором return
.
попытайтесь вызвать getchar()
прямо перед возвратом main()
.
Просто нажмите CNTRL + F5, чтобы открыть его во внешнем окне командной строки (Visual Studio не контролирует его).
Если это не работает, добавьте следующее в конец вашего Код:
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
Ожидайте, пока вы нажмете клавишу, чтобы закрыть окно терминала, когда код достиг конца.
Если вы хотите сделать это в нескольких местах, поместите вышеуказанный код в метод (например, private void Pause()
) и вызовите Pause()
всякий раз, когда программа достигает возможного конца.
Если вы используете .NET, поставьте Console.ReadLine()
до конца программы.
Он будет ждать <ENTER>
.
Вот решение, которое (1) не требует каких-либо изменений кода или точек останова, и (2) приостанавливает после завершения программы , чтобы вы могли видеть все , которые были распечатаны. Он остановится после F5 или Ctrl + F5. Основной недостаток заключается в том, что на VS2013 Express (как проверено) он не загружает символы, поэтому отладка очень ограничена.
runthenpause.bat
со следующим содержимым: %1 %2 %3 %4 %5 %6 %7 %8 %9
pause
Первая строка будет запускать любую команду, которую вы предоставляете, и до восьми аргументов. Вторая строка будет ... пауза. $(TargetPath)
(или что-то в «Команде»). runthenpause.bat
. Теперь, когда вы запустите, runthenpause.bat
запустит ваше приложение и после того, как ваше приложение завершится, приостановит вас, чтобы увидеть вывод консоли.
Я опубликую обновление, если выясню, как загрузить символы. Я пробовал /Z7
за этот , но безуспешно.
Щелкните правой кнопкой мыши по вашему проекту
Свойства> Свойства конфигурации> Коннектор> Система
blockquote>выберите Консоль (/ SUBSYSTEM: CONSOLE) в опции
SubSystem
,Теперь попробуйте ... он должен работать
(/ SUBSYSTEM: CONSOLE) не работал для моего vs2013 (у меня его уже было).
«работать без отладки» не является параметром, так как я не хочу переключаться между отладкой и просмотром output.
Я закончил с
int main() {
...
#if _DEBUG
LOG_INFO("end, press key to close");
getchar();
#endif // _DEBUG
return 0;
}
Решение, используемое в qtcreator до 2.6. Теперь, когда qt растет, vs идет другим путем. Насколько я помню, в vs2008 нам не нужны такие трюки.
Вы можете запустить исполняемый файл из командной строки. Таким образом, вы можете увидеть весь вывод. Или вы можете сделать что-то вроде этого:
int a = 0;
scanf("%d",&a);
return YOUR_MAIN_CODE;
, и таким образом окно не будет закрываться, пока вы не введете данные для переменной a
.
Несколько лучшее решение:
atexit([] { system("PAUSE"); });
в начале вашей программы.
Плюсы:
cin.sync(); cin.ignore();
system("pause");
) Минусы:
extern "C" int __stdcall IsDebuggerPresent(void);
int main(int argc, char** argv) {
if (IsDebuggerPresent())
atexit([] {system("PAUSE"); });
...
}
Visual Studio 2015 с импортом. Потому что я ненавижу, когда примеры кода не дают необходимых импортов.
#include <iostream>;
int main()
{
getchar();
return 0;
}
Перейти к меню отладки -> Нажмите StartWithoutDebugging
просто введите вашу последнюю строку кода:
system("pause");
Используйте console.readline
. Ваш пишет строку, но не читает ее.
добавить "| pause "в поле аргументов команды в разделе отладки в свойствах проекта.