C и С++ имеют много отличий, и не все допустимые C-коды являются действительными С++-кодом.
(Под "действительным" я подразумеваю стандартный код с определенным поведением, т.е. Не специфичный для реализации/ undefined/и т.д.)
Есть ли какой-либо сценарий, в котором часть кода, действительная в C и С++, создавала бы другое поведение при компиляции со стандартным компилятором на каждом языке?
Чтобы сделать это разумным/полезным сравнением (я пытаюсь узнать что-то практически полезное, а не пытаться найти очевидные лазейки в вопросе), допустим:
#ifdef __cplusplus
, прагмами и т.д.)Следующие, действительные в 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;
}
Вот пример, который использует разницу между вызовами функций и объявлениями объектов в 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
, если вы хотите эту функцию.
int f()
встроен компилятором?
Для С++ и C90 существует хотя бы один способ получить различное поведение, которое не определено в реализации. C90 не имеет однострочных комментариев. С небольшим вниманием мы можем использовать это, чтобы создать выражение с совершенно разными результатами в C90 и на С++.
int a = 10 //* comment */ 2
+ 3;
В С++ все от //
до конца строки - это комментарий, поэтому это выглядит как:
int a = 10 + 3;
Так как C90 не имеет однострочных комментариев, только комментарий /* comment */
является комментарием. Первые /
и 2
являются обеими элементами инициализации, поэтому он выходит на:
int a = 10 / 2 + 3;
Итак, правильный компилятор С++ даст 13, но правильный компилятор C. 8. Конечно, я просто выбрал произвольные числа здесь - вы можете использовать другие числа, как вы сочтете нужным.
2
он будет читаться как 10 / + 3
что является действительным (унарный +).
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
означает нечто совершенно другое, оно сообщает компилятору вывести тип переменной из значения, используемого для ее инициализации.
auto
?
Еще один пример, о котором я еще не упоминал, подчеркивает препроцессорную разницу:
#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.
В стандарте С++ 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
Эта программа печатает 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
.
Другая ловушка sizeof
: логические выражения.
#include <stdio.h>
int main() {
printf("%d\n", (int)sizeof !0);
}
Он равен sizeof(int)
в C, потому что выражение имеет тип int
, но обычно 1 в С++ (хотя это и не требуется). На практике они почти всегда разные.
!
должно быть достаточно для bool
.
#include <stdio.h>
int main(void)
{
printf("%d\n", (int)sizeof('a'));
return 0;
}
В C это печатает независимо от того, какое значение sizeof(int)
находится в текущей системе, обычно это 4
в большинстве систем, которые обычно используются сегодня.
В С++ это должно печатать 1.
Язык программирования С++ (3-е издание) дает три примера:
sizeof ('a'), как упоминал @Adam Rosenfield,
//
комментарии, используемые для создания скрытого кода:
int f(int a, int b)
{
return a //* blah */ b
;
}
Структуры и т.д. скрывают вещи вне областей, как в вашем примере.
Старый каштан, который зависит от компилятора C, не распознавая комментарии конца строки С++...
...
int a = 4 //* */ 2
+2;
printf("%i\n",a);
...
Встроенные функции в 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
является значением по умолчанию.
extern
функций демонстрируется здесь?
Другой, указанный в стандарте С++:
#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));
}
x
. я думал , вы сказали , «массив ». a
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, хотя.
exit(code)
- это, по-видимому, допустимое объявление code
переменной типа exit
. (См. «Самый неприятный анализ», это другая, но похожая проблема).
#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-структурах, содержащихся в других структурах, попадают в сферу внешней структуры.
32*sizeof(double)
а не 32, хотя :))
Не забывайте о различии между глобальными пространствами имен 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