Czy kod poprawny zarówno w C, jak i C++ może generować różne zachowania podczas kompilacji w każdym języku?
C i C++ mają wiele różnic i nie każdy poprawny kod C jest poprawnym kodem C++.
(Przez "poprawny" Rozumiem standardowy kod o zdefiniowanym zachowaniu, tzn. nie specyficzny dla implementacji/undefined/etc.)
Czy istnieje jakiś scenariusz, w którym fragment kodu ważny zarówno w C, jak i C++ generowałby Inne zachowanie, gdy skompilowany ze standardowym kompilatorem w każdym języku?
Aby było to rozsądne / użyteczne porównanie (staram się nauczyć czegoś praktycznie użytecznego, a nie próbować znaleźć oczywiste luki w pytaniu), Załóżmy:
- nic związanego z preprocesorem (co oznacza brak hacków z
#ifdef __cplusplus
, Pragma, itp.) - Wszystko zdefiniowane w implementacji jest takie samo w obu językach (np. limity liczbowe itp.) [11]} porównujemy najnowsze wersje każdego standardu (np. C++98 i C90 lub nowsze)
Jeśli wersje mają znaczenie, proszę wspomnieć, które wersje każdej z nich wytwarzają różne zachowania.
17 answers
Następujące, ważne w C i C++, będzie (najprawdopodobniej) skutkować różnymi wartościami w i
W C i C++:
int i = sizeof('a');
Zobacz Rozmiar znaku ('a') w C/C++ dla wyjaśnienia różnicy.
Kolejny z tego artykułu :
#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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-03 15:44:47
Oto przykład, który wykorzystuje różnicę między wywołaniami funkcji i deklaracjami obiektów w C i C++, a także fakt, że C90 pozwala na wywołanie funkcji nierejestrowanych:
#include <stdio.h>
struct f { int x; };
int main() {
f();
}
int f() {
return printf("hello");
}
W C++ to nic nie wydrukuje, ponieważ tymczasowe f
jest tworzone i niszczone, ale w C90 wydrukuje hello
, ponieważ funkcje mogą być wywoływane bez deklaracji.
W przypadku, gdy zastanawiałeś się, czy nazwa f
jest używana dwa razy, standardy C i C++ jawnie pozwala na to, i aby obiekt trzeba powiedzieć struct f
disambiguate jeśli chcesz strukturę, lub zostawić {[5] } jeśli chcesz funkcji.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-09 04:19:09
Dla C++ VS. C90, jest co najmniej jeden sposób na uzyskanie innego zachowania, które nie jest zdefiniowane w implementacji. C90 nie posiada komentarzy. Przy odrobinie ostrożności możemy użyć tego do stworzenia wyrażenia o zupełnie innych wynikach w C90 i w C++.
int a = 10 //* comment */ 2
+ 3;
W C++ Wszystko od //
do końca linii jest komentarzem, więc to działa jak:
int a = 10 + 3;
Ponieważ C90 nie ma komentarzy jednoliniowych, tylko /* comment */
jest komentarzem. Pierwsze /
i 2
są obie części inicjalizacji, więc wychodzi na:
int a = 10 / 2 + 3;
Więc poprawny kompilator C++ Da 13, ale poprawny kompilator C 8. Oczywiście, właśnie wybrałem dowolne liczby tutaj -- możesz użyć innych liczb, jak uważasz za stosowne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-15 06:10:51
C90 vs. C++11 (int
vs.double
):
#include <stdio.h>
int main()
{
auto j = 1.5;
printf("%d", (int)sizeof(j));
return 0;
}
W C auto
oznacza zmienną lokalną. W C90 można pominąć typ zmiennej lub funkcji. Domyślnie jest to int
. W C++11 auto
oznacza coś zupełnie innego, mówi kompilatorowi, aby wywnioskował typ zmiennej z wartości użytej do jej zainicjowania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-27 20:43:35
Kolejny przykład, którego jeszcze nie widziałem, ten podkreślający różnicę w preprocesorze:
#include <stdio.h>
int main()
{
#if true
printf("true!\n");
#else
printf("false!\n");
#endif
return 0;
}
Wyświetla "false" W C i "true" w C++ - w C, dowolne niezdefiniowane makro oblicza się na 0. W C++ jest 1 wyjątek: "true" ocenia na 1.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-17 02:17:59
Na standard C++11:
A. operator przecinka wykonuje konwersję lvalue-to-rvalue w C, ale nie w C++:
char arr[100];
int s = sizeof(0, arr); // The comma operator is used.
W C++ wartość tego wyrażenia będzie wynosić 100, a w C będzie to sizeof(char*)
.
B. W C++ typem enumeratora jest enum. W C typem enumeratora jest int.
enum E { a, b, c };
sizeof(a) == sizeof(int); // In C
sizeof(a) == sizeof(E); // In C++
Oznacza to, że sizeof(int)
może nie być równa sizeof(E)
.
C. W C++ funkcja zadeklarowana z pustą listą params nie pobiera żadnych argumentów. W C puste lista params oznacza, że liczba i typ params funkcji jest nieznana.
int f(); // int f(void) in C++
// int f(*unknown*) in C
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-04 07:54:36
Ten program drukuje 1
w C++ i 0
W C:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int d = (int)(abs(0.6) + 0.5);
printf("%d", d);
return 0;
}
Dzieje się tak, ponieważ istnieje double abs(double)
przeciążenie w C++, więc abs(0.6)
zwraca 0.6
podczas gdy w C zwraca 0
z powodu niejawnej konwersji podwójnego do int przed wywołaniem int abs(int)
. W C, musisz użyć fabs
do pracy z double
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-17 18:44:23
Another sizeof
trap: wyrażenia logiczne.
#include <stdio.h>
int main() {
printf("%d\n", (int)sizeof !0);
}
Jest równe sizeof(int)
W C, ponieważ wyrażenie jest typu int
, ale w C++ jest zazwyczaj równe 1 (choć nie jest wymagane). W praktyce są one prawie zawsze różne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-15 23:31:14
#include <stdio.h>
int main(void)
{
printf("%d\n", (int)sizeof('a'));
return 0;
}
W C wypisuje wartość sizeof(int)
w bieżącym systemie, która jest zazwyczaj 4
w większości systemów powszechnie używanych.
W C++, to musi wydrukować 1.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-27 21:56:37
Stary kasztan, który zależy od kompilatora C, nie rozpoznając komentarzy końca linii C++...
...
int a = 4 //* */ 2
+2;
printf("%i\n",a);
...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-19 02:09:00
język programowania C++ (wydanie 3) podaje trzy przykłady:
-
Sizeof ('a'), jak wspomniał @ Adam Rosenfield;
-
//
komentarze używane do tworzenia ukrytego kodu:int f(int a, int b) { return a //* blah */ b ; }
Konstrukcje itp. ukrywanie rzeczy w lunetach, jak w twoim przykładzie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-03 15:58:44
Kolejny wymieniony przez Standard C++:
#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));
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-24 00:28:16
Funkcje Inline w C domyślnie do zewnętrznego zakresu, gdzie jak te w c++ nie.
Skompilowanie dwóch poniższych plików razem wypisywałoby "I am inline" w przypadku GNU C, ale nic dla C++.
Plik 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;
}
Plik 2
#include <stdio.h>
inline void fun(void)
{
printf("I am inline\n");
}
Również C++ domyślnie traktuje dowolne const
Globalne jako static
, chyba że jest jawnie zadeklarowane extern
, w przeciwieństwie do C, w którym extern
jest domyślne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-09 04:32:00
struct abort
{
int x;
};
int main()
{
abort();
return 0;
}
Zwraca z kodem wyjścia wynoszącym 0 W C++ lub 3 w C.
Ten trik mógłby być prawdopodobnie użyty do zrobienia czegoś bardziej interesującego, ale nie mogłem wymyślić dobrego sposobu na stworzenie konstruktora, który byłby smakowity dla C. próbowałem zrobić podobnie nudny przykład z konstruktorem kopiującym, który pozwoliłby na przekazanie argumentu, choć w sposób raczej nie przenośny: {]}
struct exit
{
int x;
};
int main()
{
struct exit code;
code.x=1;
exit(code);
return 0;
}
VC++ 2005 odmówił kompilacji tego w trybie C++, narzekając jednak na to, jak" exit code " był redefinicja. (Myślę, że to błąd kompilatora, chyba że nagle zapomniałem jak programować.) Kończył się z kodem wyjścia procesu równym 1, gdy został skompilowany jako C.
#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;
}
Ten program drukuje 128
(32 * sizeof(double)
) w przypadku kompilacji przy użyciu kompilatora C++ i 4
przy kompilacji przy użyciu kompilatora C.
Dzieje się tak dlatego, że C nie ma pojęcia rozdzielczości zakresu. W strukturach C zawartych w innych strukturach wchodzi się w zakres struktury zewnętrznej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-18 16:23:12
Nie zapomnij o rozróżnieniu między globalnymi przestrzeniami nazw C i C++. Załóżmy, że masz foo.cpp
#include <cstdio>
void foo(int r)
{
printf("I am C++\n");
}
I foo2.c
#include <stdio.h>
void foo(int r)
{
printf("I am C\n");
}
Załóżmy, że masz główną.C i main.cpp które wyglądają tak:
extern void foo(int);
int main(void)
{
foo(1);
return 0;
}
Gdy skompilowany jako C++, będzie używał symbolu w globalnej przestrzeni nazw C++; W C będzie używał symbolu 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-17 09:27:43
Dotyczy to wartości lvalu i rvalue w C i C++.
W języku C zarówno operatory pre-increment, jak i post-increment zwracają wartości R, a nie lvalues. Oznacza to, że nie mogą znajdować się po lewej stronie operatora =
przypisania. Oba te stwierdzenia spowodują błąd kompilatora w C:
int a = 5;
a++ = 2; /* error: lvalue required as left operand of assignment */
++a = 2; /* error: lvalue required as left operand of assignment */
W C++ operator pre-increment Zwraca wartość lvalue, natomiast operator post-increment Zwraca wartość R. Oznacza to, że wyrażenie z operator pre-increment może być umieszczony po lewej stronie =
operatora przypisania!
int a = 5;
a++ = 2; // error: lvalue required as left operand of assignment
++a = 2; // No error: a gets assigned to 2!
Dlaczego tak jest? Post-increment zwiększa zmienną i zwraca zmienną tak, jak była przed przyrost miał miejsce. To jest właściwie tylko wartość R. Pierwsza wartość zmiennej a jest kopiowana do rejestru jako tymczasowa, a następnie a jest zwiększana. Ale poprzednia wartość a jest zwracana przez wyrażenie, jest wartością rvalue. Nie reprezentuje już bieżąca zawartość zmiennej.
Pre-increment najpierw zwiększa zmienną, a następnie zwraca zmienną, jak stała się po przyrost miał miejsce. W tym przypadku nie musimy zapisywać starej wartości zmiennej do tymczasowego rejestru. Po prostu pobieramy nową wartość zmiennej po jej zwiększeniu. Tak więc pre-increment zwraca lvalue, zwraca samą zmienną A. Możemy użyć przypisać ten lvalue do czegoś innego, to jest jak po stwierdzeniu. Jest to niejawna konwersja lvalue na rvalue.
int x = a;
int x = ++a;
Ponieważ pre-increment zwraca lvalue, możemy również przypisać coś do niego. Poniższe dwa stwierdzenia są identyczne. W drugim przydziale, najpierw a jest inkrementowane, a następnie jego nowa wartość jest nadpisywana przez 2.
int a;
a = 2;
++a = 2; // Valid in C++.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-22 03:06:58