Различие в связи между C и C++?

Я считал существующие вопросы на внешней/внутренней связи здесь на ТАК. Мой вопрос отличается - что происходит, если у меня есть повторные определения той же переменной с внешней связью в различных единицах перевода под 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++? Действительно ли это поведение не определено и зависимо от компилятора? Как "плохо" этот код и что я должен сделать, если я хочу осуществить рефакторинг его (я столкнулся с большим количеством старого кода, записанного как это)?

10
задан hlovdal 8 January 2010 в 08:46
поделиться

5 ответов

И C, и C ++ имеют «одно правило определения», согласно которому каждый объект может быть определен только один раз в любой программе. Нарушение этого правила вызывает неопределенное поведение , что означает, что вы можете видеть или не видеть диагностическое сообщение при компиляции.

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

int a;

В языке C это предварительное определение. Его можно объединить с другими предварительными определениями в той же единице перевода, чтобы сформировать единое определение. В C ++ это всегда определение (вы должны использовать extern , чтобы объявить объект без его определения), и любые последующие определения того же объекта в той же единице перевода являются ошибкой.

В вашем примере обе единицы перевода имеют (противоречащее) определение xyz из их предварительных определений.

4
ответ дан 4 December 2019 в 02:50
поделиться

Это вызвано искажением имени 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.


2
ответ дан 4 December 2019 в 02:50
поделиться

C ++ не позволяет определять символ более одного раза. Не зная, что делает компоновщик C, можно предположить, что он просто отображает оба определения на один и тот же символ, что, конечно, вызовет серьезные ошибки.

Для переноса я бы попытался поместить содержимое отдельных C-файлов в анонимные пространства имен, что, по сути, делает символы разными и локальными для файла, чтобы они не конфликтовали с одинаковыми именами в другом месте.

1
ответ дан 4 December 2019 в 02:50
поделиться

Программа C допускает это и обрабатывает память как объединение. Он будет работать, но может дать вам не то, что вы ожидали.

Программа на C ++ (которая имеет более строгую типизацию) правильно обнаруживает проблему и просит вас исправить ее. Если вы действительно хотите союза, объявите его как единое целое. Если вам нужны два разных объекта, ограничьте их область действия.

0
ответ дан 4 December 2019 в 02:50
поделиться

Minimax

Если вам нужны глубокие знания о алгоритмах ИИ, я думаю, что «искусственный интеллект современный подход» книга является лучшим источником.

-121--2678298-

Википедия является безопасной ставкой в качестве отправной точки. Вы там смотрели?

Рыбка вроде бы претендент.

-121--2678299-

Найдено Одно правило определения . Очевидно, что ваша программа имеет ошибку, так как

  • может быть только один объект с именем foo после того, как программа связана.
  • Если какой-либо исходный файл содержит все заголовочные файлы, то появится два определения foo .

Компиляторы C++ могут обойти # 1 из-за «искажения имени»: имя переменной в связанной программе может отличаться от выбранного. В этом случае это не обязательно, но, вероятно, именно так компилятор обнаружил проблему. # 2, однако, остается, так что вы не можете сделать это.

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

extern "C" struct abc foo;

... другой файл...

extern "C" struct foo foo;

extern «C» предписывает компоновщику использовать соглашения C ABI.

0
ответ дан 4 December 2019 в 02:50
поделиться