Изменение имени C ++ в C

В языке C не используется изменение имен, как в C ++. Это может привести к незаметным ошибкам, когда прототип функции объявлен по-разному в разных файлах. Простой пример:

/* file1.c */
int test(int x, int y)
{
    return y;
}

/* file2.c */
#include <stdio.h>

extern int test(int x);

int main()
{
    int n = test(2);
    printf("n = %d\n", n);
    return 0;
}

При компиляции такого кода с помощью компилятора C (в моем случае gcc) ошибок не сообщается. После перехода на компилятор C ++ компоновка завершится ошибкой «неопределенная ссылка на 'test (int)'». К сожалению, на практике это не так просто - бывают случаи, когда код принимается компилятором C (с возможными предупреждающими сообщениями), но компиляция не выполняется при использовании компилятора C ++.

Это, конечно, плохая практика кодирования - все прототипы функций должны быть добавлены в файл .h, который затем включается в файлы, в которых функция реализована или используется. К сожалению, в моем приложении таких случаев много, и исправить все в ближайшее время невозможно. Переход на g ++ тоже невозможен, я довольно быстро получил ошибку компиляции.

Одним из возможных решений было бы использовать изменение имени C ++ при компиляции кода C. К сожалению, gcc не позволяет этого сделать - я не нашел для этого параметра командной строки. Вы знаете, можно ли это сделать (может быть, использовать другой компилятор?). Мне также интересно, могут ли некоторые инструменты статического анализа это уловить.


person Daniel Frużyński    schedule 30.07.2014    source источник
comment
Просто скомпилировать весь код как C ++ и покончить с этим?   -  person Dark Falcon    schedule 30.07.2014
comment
Программа на C или программа на C ++. Это очень разные языки, не пытайтесь использовать их, как если бы они были одними и теми же   -  person Manu343726    schedule 30.07.2014
comment
или поместите заголовок в extern "C"{} block, чтобы он знал, что не нужно искать искаженную версию   -  person Grady Player    schedule 30.07.2014
comment
@DarkFalcon: это не так просто, я уже пробовал это. Из-за различий между C и C ++ компиляция не выполняется, и мне кажется, что исправление всего для компиляции в режиме C ++ потребует много работы. Поэтому ищу другие решения.   -  person Daniel Frużyński    schedule 30.07.2014
comment
Технически изменение имен не является частью C ++. Некоторые поставщики предпочитают использовать эту деталь внедрения. Если они могут обрабатывать перегрузку функций в единицах перевода по-другому, они могут это использовать.   -  person James Curran    schedule 30.07.2014
comment
Я что-то упускаю или это не проблема с искажением имени? Вы действительно в порядке с компоновщиком C, связывающим ваш вызов функции с одним аргументом с функцией с двумя аргументами? Тем более, что эта функция возвращает неинициализированный аргумент? Мне это кажется полным беспорядком.   -  person indiv    schedule 30.07.2014
comment
@indiv Он хочет, чтобы компилятор C отказался его связывать. Один из возможных способов сделать это - выправить имена.   -  person T.C.    schedule 30.07.2014
comment
@ T.C .: Ах, спасибо! Вижу, я понял проблему, но не вопрос.   -  person indiv    schedule 30.07.2014
comment
Я предполагаю, что такой инструмент, как lint, может диагностировать проблему.   -  person ach    schedule 30.07.2014
comment
Так это не совсем вопрос C ++? Вы хотите предотвратить связывание кода C, который вызывает функцию с неправильными типами аргументов или неправильным количеством аргументов?   -  person Rob K    schedule 30.07.2014
comment
Я предполагаю, что необходимо найти все объявления функций в файлах .c и переместить их в файлы заголовков, если они не предназначены для рекурсивных статических функций. Потому что каждое прямое объявление, которое компилятор никогда не проверяет на соответствие определению, является либо ошибкой, либо ожидает своего исправления. grep твой друг ...   -  person cmaster - reinstate monica    schedule 30.07.2014
comment
stackoverflow.com/questions/15137702/   -  person T.C.    schedule 30.07.2014
comment
На самом деле это зависит от компилятора. MSVC, например, искажает имена C.   -  person Moby Disk    schedule 30.07.2014
comment
@MobyDisk, это придирки. Конечно, добавление "_" технически искажает. Но то, что нужно OP, - это не все виды искажений, а такие виды искажений, которые дают разные результаты для разных прототипов.   -  person ach    schedule 30.07.2014
comment
@AndreyChernyakhovskiy Собственно, именно поэтому MSVC помещает количество байтов в списке аргументов в имя функции. См. msdn.microsoft.com/en-us/library/zxk0tw93.aspx   -  person Moby Disk    schedule 30.07.2014
comment
@MobyDisk Только если вы используете __stdcall (или __fastcall) и компилируете для 32-битных систем.   -  person T.C.    schedule 30.07.2014
comment
@MobyDisk, в большом проекте практически невозможно применить __stdcall ко всем функциям. И даже тогда он различается только по размеру.   -  person ach    schedule 30.07.2014
comment
@AndreyChernyakhovskiy Существует переключатель компилятора, который делает все функции явно не объявленными в противном случае __stdcall.   -  person T.C.    schedule 31.07.2014
comment
@ T.C., Да, конечно. Но у реальных проектов также есть зависимости, и связать их таким образом может (а может и не быть) проблематично.   -  person ach    schedule 31.07.2014


Ответы (1)


Использование splint позволяет отловить подобные ошибки.

foo.c:

int test(int x);
int main() {
    test(0);
}

bar.c:

int test(int x, int y) {
    return y;
}

Запуск splint:

$ splint -weak foo.c bar.c
Splint 3.1.2 --- 20 Feb 2009

bar.c:1:5: Function test redeclared with 2 args, previously declared with 1
  Types are incompatible. (Use -type to inhibit warning)
   foo.c:4:5: Previous declaration of test

Finished checking --- 1 code warning
person Frxstrem    schedule 30.07.2014
comment
На первый взгляд шина выглядит красиво. Однако я боюсь, что не смогу его использовать - у меня огромная база кода с несколькими модулями, скомпилированными в промежуточные библиотеки (каждый с настраиваемыми параметрами компиляции), а затем подключенными к окончательному приложению. Я проверил руководство по разделению, и на первый взгляд в gcc нет опций, соответствующих -c и -o. - person Daniel Frużyński; 31.07.2014
comment
Я должен был упомянуть, что я более или менее отвечал на статический анализ вашего вопроса; splint не является компилятором, поэтому вы должны компилировать как обычно с GCC после получения успешного результата (т. Е. Без ошибок) от splint. - person Frxstrem; 31.07.2014