Может ли код, действительный как на C, так и на C ++, производить разное поведение при компиляции на каждом языке?

549

C и С++ имеют много отличий, и не все допустимые C-коды являются действительными С++-кодом.
(Под "действительным" я подразумеваю стандартный код с определенным поведением, т.е. Не специфичный для реализации/ undefined/и т.д.)

Есть ли какой-либо сценарий, в котором часть кода, действительная в C и С++, создавала бы другое поведение при компиляции со стандартным компилятором на каждом языке?

Чтобы сделать это разумным/полезным сравнением (я пытаюсь узнать что-то практически полезное, а не пытаться найти очевидные лазейки в вопросе), допустим:

  • Ничего связанного с препроцессором (что означает отсутствие хаков с #ifdef __cplusplus, прагмами и т.д.)
  • Все значения, определенные реализацией, одинаковы на обоих языках (например, числовые ограничения и т.д.).
  • Мы сравниваем разумно последние версии каждого стандарта (например, С++ 98 и C90 или более поздние версии)
    Если версии имеют значение, то, пожалуйста, укажите, какие версии каждого продукта отличаются друг от друга.
  • 0
    Было бы полезно, если бы вы предоставили версии компилятора и параметры командной строки компилятора?
  • 0
    наоборот: в C структуры не являются областями видимости, поэтому структура A внутри структуры B называется «struct A», поэтому C ответит 512, а C ++ скажет 8.
Показать ещё 11 комментариев
Теги:

16 ответов

359
Лучший ответ

Следующие, действительные в C и С++, будут (скорее всего) результатом в разных значениях в i в C и С++:

int i = sizeof('a');

См. Размер символа ('a') в C/С++ для объяснения разницы.

Еще один из в этой статье:

#include <stdio.h>

int  sz = 80;

int main(void)
{
    struct sz { char c; };

    int val = sizeof(sz);      // sizeof(int) in C,
                               // sizeof(struct sz) in C++
    printf("%d\n", val);
    return 0;
}
  • 7
    Определенно не ожидал этого! Я надеялся на что-то более драматичное, но это все еще полезно, спасибо. :) +1
  • 0
    Возможно, вам будет интересно прочитать эту статью . Там должно быть больше.
Показать ещё 10 комментариев
467

Вот пример, который использует разницу между вызовами функций и объявлениями объектов в C и С++, а также тот факт, что C90 позволяет вызывать необъявленные функции:

#include <stdio.h>

struct f { int x; };

int main() {
    f();
}

int f() {
    return printf("hello");
}

В С++ это ничего не будет печатать, потому что временный f создается и уничтожается, но на C90 он печатает hello, потому что функции могут быть вызваны без объявления.

В случае, если вам было интересно, что имя f используется дважды, стандарты C и С++ явно разрешают это, и чтобы сделать объект, который вы должны сказать struct f, чтобы устранить неоднозначность, если вы хотите создать структуру, или оставьте struct, если вы хотите эту функцию.

  • 0
    не будет ли int f() встроен компилятором?
  • 5
    Строго говоря, под C это не скомпилируется, потому что объявление int (()) после определения int (()) :)
Показать ещё 9 комментариев
407

Для С++ и C90 существует хотя бы один способ получить различное поведение, которое не определено в реализации. C90 не имеет однострочных комментариев. С небольшим вниманием мы можем использовать это, чтобы создать выражение с совершенно разными результатами в C90 и на С++.

int a = 10 //* comment */ 2 
        + 3;

В С++ все от // до конца строки - это комментарий, поэтому это выглядит как:

int a = 10 + 3;

Так как C90 не имеет однострочных комментариев, только комментарий /* comment */ является комментарием. Первые / и 2 являются обеими элементами инициализации, поэтому он выходит на:

int a = 10 / 2 + 3;

Итак, правильный компилятор С++ даст 13, но правильный компилятор C. 8. Конечно, я просто выбрал произвольные числа здесь - вы можете использовать другие числа, как вы сочтете нужным.

  • 31
    Вау это умопомрачительно !! Из всех возможных вещей, о которых я никогда бы не подумал, комментарии могут быть использованы для изменения поведения, хаха. +1
  • 85
    даже без 2 он будет читаться как 10 / + 3 что является действительным (унарный +).
Показать ещё 5 комментариев
161

C90 против С++ 11 (int vs. double):

#include <stdio.h>

int main()
{
  auto j = 1.5;
  printf("%d", (int)sizeof(j));
  return 0;
}

В C auto используется локальная переменная. В C90 можно опустить переменную или тип функции. По умолчанию он равен int. В С++ 11 auto означает нечто совершенно другое, оно сообщает компилятору вывести тип переменной из значения, используемого для ее инициализации.

  • 9
    С90 имеет auto ?
  • 6
    Да: tigcc.ticalc.org/doc/keywords.html#auto
Показать ещё 6 комментариев
97

Еще один пример, о котором я еще не упоминал, подчеркивает препроцессорную разницу:

#include <stdio.h>
int main()
{
#if true
    printf("true!\n");
#else
    printf("false!\n");
#endif
    return 0;
}

Это печатает "false" в C и "true" в С++. В C любой макрос undefined оценивается равным 0. В С++ существует 1 исключение: "true" оценивается в 1.

  • 2
    Интересно. Кто-нибудь знает обоснование этого изменения?
  • 2
    потому что «истина» является ключевым словом / действительным значением, поэтому оно оценивается как истинное, как любое «истинное значение» (как любое положительное целое число). Вы все еще можете сделать #define true false, чтобы напечатать "false" также в C ++;)
Показать ещё 4 комментария
93

В стандарте С++ 11:

a. Оператор запятой выполняет преобразование lvalue-rvalue в C, но не С++:

   char arr[100];
   int s = sizeof(0, arr);       // The comma operator is used.

В С++ значение этого выражения будет равно 100, а в C это будет sizeof(char*).

b. В С++ тип перечислителя - это его перечисление. В C тип перечислителя - int.

   enum E { a, b, c };
   sizeof(a) == sizeof(int);     // In C
   sizeof(a) == sizeof(E);       // In C++

Это означает, что sizeof(int) может быть не равно sizeof(E).

c. В С++ функция, объявленная с пустым списком параметров, не принимает аргументов. В C пустых параметрах список означает, что число и тип параметров функции неизвестны.

   int f();           // int f(void) in C++
                      // int f(*unknown*) in C
  • 0
    Первый также определяется реализацией, как у Алексея. Но +1.
  • 1
    @ Seth, Все вышеперечисленные материалы взяты непосредственно из Приложения C.1 стандарта C ++ 11.
Показать ещё 5 комментариев
38

Эта программа печатает 1 в С++ и 0 в C:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int d = (int)(abs(0.6) + 0.5);
    printf("%d", d);
    return 0;
}

Это происходит из-за перегрузки double abs(double) в С++, поэтому abs(0.6) возвращает 0.6, а в C возвращает 0 из-за неявного преобразования double-to-int перед вызовом int abs(int). В C вы должны использовать fabs для работы с double.

  • 5
    пришлось отлаживать код другого человека с этой проблемой. О, как я любил это. В любом случае, ваша программа также печатает 0 в C ++. C ++ должен использовать заголовок "cmath", см. Сравнение сначала один возврат в 0 ideone.com/0tQB2G 2-й ответ, возвращающий 1 ideone.com/SLeANo
  • 0
    Рад / жаль слышать, что я не единственный, кто находит эту разницу с помощью отладки. Только что протестированный в VS2013, пустой файл, содержащий только этот файл, выведет 1, если расширение - .cpp, и 0, если расширение - .c. Похоже, <math.h> косвенно включен в VS.
Показать ещё 5 комментариев
32

Другая ловушка sizeof: логические выражения.

#include <stdio.h>
int main() {
    printf("%d\n", (int)sizeof !0);
}

Он равен sizeof(int) в C, потому что выражение имеет тип int, но обычно 1 в С++ (хотя это и не требуется). На практике они почти всегда разные.

  • 6
    Один ! должно быть достаточно для bool .
  • 2
    !! является оператором int для логического преобразования :)
Показать ещё 1 комментарий
31
#include <stdio.h>

int main(void)
{
    printf("%d\n", (int)sizeof('a'));
    return 0;
}

В C это печатает независимо от того, какое значение sizeof(int) находится в текущей системе, обычно это 4 в большинстве систем, которые обычно используются сегодня.

В С++ это должно печатать 1.

  • 3
    Да, я на самом деле был знаком с этим трюком, потому что 'c' - это int в C и char в C ++, но все же хорошо, чтобы он был здесь перечислен.
  • 8
    Это сделало бы интересным вопрос для интервью - особенно для людей, которые ставят эксперта по c / c ++ в свои резюме.
Показать ещё 7 комментариев
20

Язык программирования С++ (3-е издание) дает три примера:

  • sizeof ('a'), как упоминал @Adam Rosenfield,

  • // комментарии, используемые для создания скрытого кода:

    int f(int a, int b)
    {
        return a //* blah */ b
            ;
    }
    
  • Структуры и т.д. скрывают вещи вне областей, как в вашем примере.

20

Старый каштан, который зависит от компилятора C, не распознавая комментарии конца строки С++...

...
int a = 4 //* */ 2
        +2;
printf("%i\n",a);
...
  • 5
    Так что это только в компиляторах C89, а не в современных ...
16

Встроенные функции в C по умолчанию для внешней области, где, как и в С++, нет.

Компиляция следующих двух файлов вместе приведет к печати "Я встроен" в случае GNU C, но ничего для С++.

Файл 1

#include <stdio.h>

struct fun{};

int main()
{
    fun();  // In C, this calls the inline function from file 2 where as in C++
            // this would create a variable of struct fun
    return 0;
}

Файл 2

#include <stdio.h>
inline void fun(void)
{
    printf("I am inline\n");
} 

Кроме того, С++ неявно рассматривает любой const global как static, если он явно не объявлен extern, в отличие от C, в котором extern является значением по умолчанию.

  • 0
    Я действительно так не думаю. Возможно, вы упустили момент. Речь идет не об определении struct st, которое просто используется для того, чтобы сделать код допустимым c ++. Дело в том, что он подчеркивает различное поведение встроенных функций в c vs c ++. То же относится и к extern. Ни одно из них не обсуждается ни в одном из решений.
  • 2
    Какое поведение встроенных и extern функций демонстрируется здесь?
Показать ещё 3 комментария
16

Другой, указанный в стандарте С++:

#include <stdio.h>

int x[1];
int main(void) {
    struct x { int a[2]; };
    /* size of the array in C */
    /* size of the struct in C++ */
    printf("%d\n", (int)sizeof(x)); 
}
  • 0
    так вы получаете различия в отступах?
  • 0
    ах, извини, я понял, наверху есть еще один x . я думал , вы сказали , «массив ». a
12
struct abort
{
    int x;
};

int main()
{
    abort();
    return 0;
}

Возвращает с кодом выхода 0 в С++ или 3 в C.

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

struct exit
{
    int x;
};

int main()
{
    struct exit code;
    code.x=1;

    exit(code);

    return 0;
}

VС++ 2005 отказался компилировать, что в режиме С++, однако, жалуется на то, как переопределился код выхода. (Я думаю, что это ошибка компилятора, если только я не забыл, как программировать.) Он вышел с кодом завершения процесса 1, когда скомпилирован как C, хотя.

  • 0
    Ваш второй пример, использующий exit, к сожалению, не компилируется на gcc или g ++. Это хорошая идея.
  • 1
    exit(code) - это, по-видимому, допустимое объявление code переменной типа exit . (См. «Самый неприятный анализ», это другая, но похожая проблема).
9
#include <stdio.h>

struct A {
    double a[32];
};

int main() {
    struct B {
        struct A {
            short a, b;
        } a;
    };
    printf("%d\n", sizeof(struct A));
    return 0;
}

Эта программа печатает 128 (32 * sizeof(double)) при компиляции с использованием компилятора С++ и 4 при компиляции с использованием компилятора C.

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

  • 0
    Это интересно! (Я думаю, что вы имеете в виду 32*sizeof(double) а не 32, хотя :))
  • 0
    Ах, да, конечно. Исправляем это сейчас. :)
Показать ещё 1 комментарий
2

Не забывайте о различии между глобальными пространствами имен C и С++. Предположим, что у вас есть foo.cpp

#include <cstdio>

void foo(int r)
{
  printf("I am C++\n");
}

и foo2.c

#include <stdio.h>

void foo(int r)
{
  printf("I am C\n");
}

Теперь предположим, что у вас есть main.c и main.cpp, которые выглядят следующим образом:

extern void foo(int);

int main(void)
{
  foo(1);
  return 0;
}

При компиляции как С++ он будет использовать символ в глобальном пространстве имен С++; в C он будет использовать C один:

$ diff main.cpp main.c
$ gcc -o test main.cpp foo.cpp foo2.c
$ ./test 
I am C++
$ gcc -o test main.c foo.cpp foo2.c
$ ./test 
I am C
  • 0
    Вы имеете в виду спецификацию связей?
  • 0
    искажение имени. Имена C ++ имеют префиксы и суффиксы, в то время как C не
Показать ещё 2 комментария
Сообщество Overcoder
Наверх
Меню