Элементы языка С, которые являются неподдерживаемыми в языке С++

от автора

Нижеприведенный список является моей небольшой коллекцией примеров кода на языке С, которые не являются корректными с точки зрения языка С++ или имеют какое-то специфичное именно для языка С поведение. (Именно в эту сторону: С код, являющийся некорректным с точки зрения С++.)

Этот материал я уже публиковал на другом ресурсе в менее причесанном виде, Я бы, наверное, поддался прокрастинации и никогда не собрался опубликовать эту коллекцию здесь, но из-за горизонта уже доносится стук копыт неумолимо приближающегося С23, который безжалостно принесет некоторые жемчужины моей коллекции в жертву богам С-С++ совместимости. Поэтому мне и пришлось встать с печи, пока они еще актуальны…

Разумеется, язык С имеет много существенных отличий от языка С++, т.е. не составит никакого труда привести примеры несовместимостей, основанные, скажем, на ключевых словах или других очевидных эксклюзивных свойствах С99. Таких примеров вы не найдете в списке ниже. Мой основной критерий для включения примеров в этот список заключался именно в том, что пример кода должен выглядеть на первый взгляд достаточно «невинно» для С++-наблюдателя, т.е. не содержать бросающихся в глаза С-эксклюзивов, но тем не менее являться специфичным именно для языка С.

(Пометка [C23] помечает те пункты, которые станут неактуальными с выходом C23.)

  1. В языке C разрешается «терять» замыкающий \0 при инициализации массива символов строковым литералом:

    char s[4] = "1234";

    В С++ такая инициализация является некорректной.

  2. C поддерживает

    Такой формат для printf корректен c точки зрения C, но некорректен с точки зрения C++.

  3. Рекурсивные вызовы функции main разрешены в C, но запрещены в C++. Программам на С++ вообще не дозволяется никак использовать основную функцию main.

  4. В языке C строковые литералы имеют тип char [N], а в языке C++ — const char [N]. Даже если считать, что «старый» C++ в виде исключения поддерживает преобразование строкового литерала к типу char *, это исключение работает только тогда, когда оно применяется непосредственно к строковому литералу

    char *p = &"abcd"[0];

    Такая инициализация некорректна с точки зрения C++.

  5. В языке С битовое поле, объявленное с типом int без явного указания signed или unsigned может быть как знаковым, там и беззнаковым (определяется реализацией). В языке С++ такое битовое поле всегда является знаковым.

  6. В языке С typedef-имена типов и тэги struct-типов располагаются в разных пространствах имен и не конфликтуют друг с другом. Например, такой набор объявлений корректен с точки зрения языка С:

    struct A { int a; }; typedef struct B { int b; } A; typedef struct C { int c; } C;

    В языке С++ не существует отдельного понятия тэга для класс-типов: имена классов разделяют одно пространство имен с typedef-именами и могут конфликтовать с ними. Для частичной совместимости с кодом на С язык С++ разрешает объявлять typedef-псевдонимы, совпадающие с именами существующих класс-типов, но только при условии, что псевдоним ссылается на класс-тип с точно таким же именем. В вышеприведенном примере typedef-объявление в строке 2 некорректно с точки зрения C++, а объявление в строке 3 — корректно.

  7. В языке С разрешается использовать имя поля, совпадающее с существующим именем типа

    typedef int I;  struct S {   I I; };

    В языке С++ такое «переопределение» идентификатора не допускается.

  8. В языке С неявный конфликт между внутренним и внешним связыванием при объявлении одной и той же переменной приводит к неопределенному поведению, а в языке С++ такой конфликт делает программу https://habr.com/ru/articles/680312/