Подтвердить что ты не робот

Каковы несовместимые различия между C (99) и С++ (11)?

Этот вопрос был вызван ответами на сообщение Herb Sutter, где он объяснил, что решение MS не поддерживает/не делает компилятор C99, а просто идет с C (99), которые в любом случае соответствуют стандарту С++ (11).

Комментарий :

(...) C важно и заслуживает хотя бы немного внимания.

Существует много существующего кода, который действителен C, но не является действительный С++. Этот код вряд ли будет переписан (...)

Так как я только программирую в MS С++, я действительно не знаю, что такое "чистый" C, т.е. у меня нет готовой картины того, какие детали языка С++ я использую, не в C (99) и У меня мало подсказок, где какой-то код C99 не работает, как есть в компиляторе С++.

Обратите внимание, что я знаю только о ключе C99 restrict, которое для меня кажется очень узким приложением и массивами переменной длины (я не уверен, насколько они распространены или важны).

Кроме того, мне очень интересно, существуют ли какие-либо важные семантические различия или gotchas, то есть код C (99), который будет компилироваться под С++ (11), но делать что-то по-другому с компилятором С++, чем с компилятором C.


Быстрые ссылки: внешние ресурсы из ответов:

4b9b3361

Ответ 1

Если вы начинаете с общего подмножества C и С++, иногда называемого чистым C (это не совсем C90), вы должны учитывать 3 типа несовместимости:

  • Дополнительные возможности С++, которые делают законным C незаконным С++

    Примерами этого являются ключевые слова С++, которые могут использоваться как идентификаторы в C или преобразованиях, которые неявны в C, но требуют явного перевода в С++.

    Это, вероятно, главная причина, по которой Microsoft по-прежнему отправляет C-интерфейс вообще: иначе устаревший код, который не компилируется, как С++, должен быть перезаписан.

  • Дополнительные возможности C, которые не являются частью С++

    Язык C не прекращал развиваться после того, как С++ был разветвлен. Некоторые примеры представляют собой массивы переменной длины, назначенные инициализаторы и restrict. Эти функции могут быть весьма полезными, но не являются частью какого-либо стандарта С++, и некоторые из них, вероятно, никогда не сделают это.

  • Функции, доступные в C и С++, но имеющие разную семантику

    Примером этого может быть привязка объектов const или inline.

Список несовместимостей между C99 и С++ 98 можно найти здесь (который уже упоминался Mat).

В то время как С++ 11 и C11 стали ближе к некоторым фронтам (переменные макросы теперь доступны на С++, массивы переменной длины теперь являются необязательной функцией языка C), также вырос список несовместимостей (например, общие варианты в C и спецификатор типа auto в С++).

В стороне, в то время как Microsoft приняла немного тепла для решения отказаться от C (что не является недавним), насколько я знаю, никто в сообществе с открытым исходным кодом не предпринял никаких шагов, чтобы что-то сделать: Было бы вполне возможно предоставить многие функции современного C через компилятор C-to-С++, особенно если вы считаете, что некоторые из них тривиальны для реализации. Фактически это возможно сейчас, используя Comau C/С++, который поддерживает C99.

Однако это не настоящая проблема. Лично мне вполне комфортно использовать GCC и Clang в Windows, и есть также запатентованные альтернативы MSVC, например, Pelles C или компилятор Intel.

Ответ 2

Существует множество несовместимостей, которые были целыми веками (C90 или ранее), а также множество действительно приятных функций на C99 и C11. Все это с головы.

// Valid C
int *array = malloc(sizeof(*array) * n);

// Valid C and valid C++, extra typing, it always extra typing...
int *array = (int *) malloc(sizeof(*array) * n);

// Valid C++
int *array = new int[n];

C99 хорош, и программисты C везде должны использовать его

Новые функции на C99 очень приятны для общего программирования. VLAs и restrict не являются (на мой взгляд), ориентированными на общее использование, но в основном для того, чтобы привести FORTRAN и числовых программистов в C (хотя restrict помогает автовекторатору). Поскольку любая соответствующая программа, использующая restrict, будет работать точно так же (но возможно не так быстро), если вы #define restrict в верхней части файла, это не очень важно. Кажется, что VLA довольно редки в дикой природе.

Элементы гибкого массива могут быть приятными. Обратите внимание, что это НЕ то же самое, что массивы переменной длины! Люди используют этот трюк в течение многих лет, но официальная поддержка означает меньшее количество ввода текста, а также позволяет нам создавать константы во время компиляции. (Старый способ состоял в том, чтобы иметь массив размером 1, но затем вычисление размера выделения является реальной проблемой.)

struct lenstr {
    unsigned length;
    char data[];
};
// compile time constant
const struct lenstr hello = { 12, "hello, world" };

Назначенные инициализаторы. Сохраняет много ввода.

struct my_struct { int a; char *b; int c; const char *d; };
struct my_struct x = {
    .a = 15,
    .d = "hello"
    // implicitly sets b = NULL and c = 0
};
int hex_digits[256] = { ['0'] = 0, ['1'] = 1, ['2'] = 2, /* etc */ ['f'] = 15 };

Ключевое слово inline ведет себя по-другому, вы можете выбрать, какая единица перевода получает не встроенную версию функции, объявленной в строке, добавив в это объявление выражение extern.

Составные литералы.

struct point { float x; float y; };
struct point xy_from_polar(float r, float angle)
{
    return (struct point) { cosf(angle) * r, sinf(angle) * r };
}

Функция snprintf, вероятно, входит в десятку самых полезных функций библиотеки в C. Она не только отсутствует в С++, но среда выполнения MSVC предоставляет только функцию _snprintf, который не гарантирует добавление терминатора NUL в строку. (snprintf находится в С++ 11, но все еще заметно отсутствует во время выполнения MSVC C.)

Анонимные структуры и союзы (C11, но расширение GCC с тех пор навсегда) (анонимные объединения, по-видимому, находятся в С++ 03, поддержка MSVC в режиме C):

struct my_value {
    int type;
    union {
        int as_int;
        double as_double;
    }; // no field name!
};

Как вы можете видеть, многие из этих функций просто экономят много ввода (сложные литералы) или облегчают отладку программ (гибкие элементы массива), упрощают избегать ошибок (назначенные инициализаторы/забывают инициализировать структуру поля). Это не радикальные изменения.

Для семантических различий, я уверен, что правила псевдонимов различны, но большинство компиляторов в наши дни достаточно прощают, я не уверен, как вы построите тестовый пример для демонстрации. Разница между C и С++, к которой все стремятся, - это старое выражение sizeof('a'), которое всегда 1 для С++, но обычно 4 в 32-битной системе C. Но никто не заботится о том, что sizeof('a') в любом случае. Однако в стандарте C99 есть некоторые гарантии, которые кодифицируют существующую практику.

Возьмите следующий код. Он использует общий трюк для определения типов объединения в C, не тратя лишнего на хранение. Я думаю, что это семантически допустимый C99, и я думаю, что это семантически сомнительный С++, но я могу ошибаться.

#define TAG_FUNKY_TOWN 5
struct object { int tag; };
struct funky_town { int tag; char *string; int i; };
void my_function(void)
{
    struct object *p = other_function();
    if (p->tag == TAG_FUNKY_TOWN) {
        struct funky_town *ft = (struct funky_town *) p;
        puts(ft->string);
    }
}

Это позор, однако. Генератор кода MSVC хорош, слишком плохо нет интерфейса C99.

Ответ 3

В С++ установка одного члена объединения и доступ к значению другого члена - это поведение undefined, тогда как оно не undefined в C99.

В wikipedia страница есть много других различий.

Ответ 4

Я упомянул "C.1 С++ и ISO C" С++ 11. Этот документ имеет удар от каждого различия и его влияние на развитие.