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.
 603
c
Author: Luchian Grigore, 2012-10-15

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;
}
 356
Author: Alexey Frunze,
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.

 453
Author: Seth Carnegie,
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.

 402
Author: Jerry Coffin,
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.

 161
Author: detunized,
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.

 107
Author: godlygeek,
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
 96
Author: Kirill Kobelev,
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.

 45
Author: Pavel Chikulaev,
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.

 36
Author: Alex B,
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.

 36
Author: Adam Rosenfield,
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);
...
 22
Author: dmckee,
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:

  1. Sizeof ('a'), jak wspomniał @ Adam Rosenfield;

  2. // komentarze używane do tworzenia ukrytego kodu:

    int f(int a, int b)
    {
        return a //* blah */ b
            ;
    }
    
  3. Konstrukcje itp. ukrywanie rzeczy w lunetach, jak w twoim przykładzie.

 22
Author: derobert,
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)); 
}
 19
Author: Johannes Schaub - litb,
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.

 18
Author: fayyazkl,
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.

 15
Author: ,
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:17:03
#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.

 11
Author: wefwefa3,
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
 4
Author: user23614,
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++.
 0
Author: Galaxy,
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