Я считал существующие вопросы на внешней/внутренней связи здесь на ТАК. Мой вопрос отличается - что происходит, если у меня есть повторные определения той же переменной с внешней связью в различных единицах перевода под C
и C++
?
Например:
/*file1.c*/
typedef struct foo {
int a;
int b;
int c;
} foo;
foo xyz;
/*file2.c*/
typedef struct abc {
double x;
} foo;
foo xyz;
Используя Dev-C++ и как программа C, вышеупомянутые компиляции программы и ссылки отлично; тогда как это дает нескольким ошибку переопределения, если то же компилируется как программа C++. Почему это должно работать под C и каково различие с C++? Действительно ли это поведение не определено и зависимо от компилятора? Как "плохо" этот код и что я должен сделать, если я хочу осуществить рефакторинг его (я столкнулся с большим количеством старого кода, записанного как это)?
И C, и C ++ имеют «одно правило определения», согласно которому каждый объект может быть определен только один раз в любой программе. Нарушение этого правила вызывает неопределенное поведение , что означает, что вы можете видеть или не видеть диагностическое сообщение при компиляции.
Существует разница в языке между следующими объявлениями в области видимости файла, но это не имеет прямого отношения к проблеме с вашим примером.
int a;
В языке C это предварительное определение. Его можно объединить с другими предварительными определениями в той же единице перевода, чтобы сформировать единое определение. В C ++ это всегда определение (вы должны использовать extern
, чтобы объявить объект без его определения), и любые последующие определения того же объекта в той же единице перевода являются ошибкой.
В вашем примере обе единицы перевода имеют (противоречащее) определение xyz
из их предварительных определений.
Это вызвано искажением имени C ++. Из Википедии :
Первые компиляторы C ++ были реализован как переводчик в исходный код C код, который затем будет скомпилирован компилятор C в объектный код; потому что из этого, имена символов должны были соответствовать к правилам идентификатора C. Даже позже, с появлением компиляторов, которые произведенный машинный код или сборка напрямую, компоновщик системы обычно не поддерживает символы C ++, и по-прежнему требовалось возиться.
Относительно совместимости :
Чтобы предоставить поставщикам компиляторов большая свобода, стандарты C ++ комитет решил не диктовать реализация изменения имени, обработка исключений и др. особенности реализации. В Обратной стороной этого решения является то, что объектный код, созданный разными компиляторы, как ожидается, будут несовместимо. Однако есть стандарты третьих сторон для конкретных машины или операционные системы, которые попытаться стандартизировать компиляторы на эти платформы (например, C ++ ABI [18]); некоторые компиляторы принимают вторичный стандарт для этих товаров.
От http://www.cs.indiana.edu/~welu/notes/node36.html приведен следующий пример:
Например, для приведенного ниже кода C
int foo(double*);
double bar(int, double*);
int foo (double* d)
{
return 1;
}
double bar (int i, double* d)
{
return 0.9;
}
Его таблица символов будет (по dump -t
)
[4] 0x18 44 2 1 0 0x2 bar
[5] 0x0 24 2 1 0 0x2 foo
Для того же файла, если компилируется в g ++, то таблица символов будет иметь вид
[4] 0x0 24 2 1 0 0x2 _Z3fooPd
[5] 0x18 44 2 1 0 0x2 _Z3bariPd
_Z3bariPd
означает функцию с именем bar, первый аргумент которой является целым числом, а второй аргумент - указателем на double.
C ++ не позволяет определять символ более одного раза. Не зная, что делает компоновщик C, можно предположить, что он просто отображает оба определения на один и тот же символ, что, конечно, вызовет серьезные ошибки.
Для переноса я бы попытался поместить содержимое отдельных C-файлов в анонимные пространства имен, что, по сути, делает символы разными и локальными для файла, чтобы они не конфликтовали с одинаковыми именами в другом месте.
Программа C допускает это и обрабатывает память как объединение. Он будет работать, но может дать вам не то, что вы ожидали.
Программа на C ++ (которая имеет более строгую типизацию) правильно обнаруживает проблему и просит вас исправить ее. Если вы действительно хотите союза, объявите его как единое целое. Если вам нужны два разных объекта, ограничьте их область действия.
Если вам нужны глубокие знания о алгоритмах ИИ, я думаю, что «искусственный интеллект современный подход» книга является лучшим источником.
-121--2678298-Википедия является безопасной ставкой в качестве отправной точки. Вы там смотрели?
Рыбка вроде бы претендент.
-121--2678299-Найдено Одно правило определения . Очевидно, что ваша программа имеет ошибку, так как
foo
после того, как программа связана. foo
. Компиляторы C++ могут обойти # 1 из-за «искажения имени»: имя переменной в связанной программе может отличаться от выбранного. В этом случае это не обязательно, но, вероятно, именно так компилятор обнаружил проблему. # 2, однако, остается, так что вы не можете сделать это.
Если вы действительно хотите победить механизм безопасности, вы можете отключить искушение так:
extern "C" struct abc foo;
... другой файл...
extern "C" struct foo foo;
extern «C»
предписывает компоновщику использовать соглашения C ABI.