Что такое «неопределенный ссылочный / неразрешенный внешний символ»
Я попытаюсь объяснить, что такое «неопределенный ссылочный / неразрешенный внешний символ».
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, мы взломаем его:)
Итак, в результате - «неопределенная ссылка / неразрешенная внешняя ошибка символа» происходит, когда компоновщик не может найти глобальные символы в объектных файлах.
Вы можете использовать .stripIndent()
, чтобы удалить отступ в многострочных строках. Но вы должны иметь в виду, что если не указывается количество отступов, оно будет автоматически определяться из строки, содержащей наименьшее число ведущих пробелов .
Учитывая вашу строку это будет только одно пустое пространство перед This
, которое будет удалено из каждой строки вашей многострочной строки.
def s = """ This
is
multiline
"""
Чтобы обойти эту проблему, вы можете избежать первой строки многострочной строки, как показано в следующем примере, чтобы получить ожидаемый результат:
def s = """\
This
is
multiline
"""
Также используйте .stripMargin () (если это возможно).
def s = """ This
| is
| multiline
"""
println s.stripMargin().stripIndent()
Для любого другого, у кого есть аналогичная проблема, решение stefanglase отлично, но дает мне исключение MultipleCompilationErrorsException в тесте Spock при включении многострочной строки в утверждение, которое не выполняется:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Spec expression: 1: unexpected char: '\' @ line 1, column 16.
myString == '''\ This Is Multiline '''.stripIndent()
Мое решение для этого is:
def myString = '''
This
Is
Multiline
'''.replaceFirst("\n","").stripIndent()
Теперь, когда утверждение терпит неудачу, вы увидите обычный diff, указывающий, что пошло не так, а не исключение компиляции.
Вы намеревались использовать == вместо =? Единственные ошибки, которые я получаю, - это использовать ваш пример. если я изменил его на = и использовал ваш пример без replaceFirst (), он отлично работает без ошибок.
Также вы не можете использовать a \nри выполнении одной строки. Я могу дублировать вашу точную проблему, если я использую '' '\ This Is Multiline' '' .stripIndent ()
У меня есть аналогичный случай использования, потому что я хотел бы отформатировать мой SQL-запрос в строке. Например, вложенный запрос:
String query = '''
select ${CONTROL_ID} from ${TABLE_NAME}
where
location_id = (
select id from location where code = ${locationCode}
)
''';
Выглядит намного лучше в Groovy, чем версия Java с "..."+ TABLE_NAME +"..."
, поскольку я уверен, что вы можете согласиться.
Этот тип строки не работает с методом .stripIndent()
. Вместо этого я сохранил новые строки в query
(выше) для какой-либо цели. Обратите внимание на «\» в конце строки.
И, таким образом,
String query = """
select ${CONTROL_ID} from ${TABLE_NAME}
where
location_id = (
select id from location where code = '${locationCode}'
)
""".replaceAll( /\n\s*/, " " );
println " Query= ${query};";
Дайте аккуратно отформатированный однострочный результат SQL-запроса:
Query = select id from controls where location_id = ( select id from location where code = '003');
Который я считаю весьма полезным и более простым для чтения. Я заменяю одно пространство, чтобы убедиться, что SQL остается дискретным. Важная часть состоит в том, чтобы использовать новую строку (\n
) в качестве стартовой линии / якоря de facto . Отлично подходит для смешанных TAB-s и SPACE-s.
Конечно, это также работает и для оригинального вопроса.