Czy kod, który jest poprawny zarówno w C, jak i C ++, może powodować inne zachowanie po kompilacji w każdym języku?

664

C i C ++ mają wiele różnic i nie wszystkie poprawne kody C są poprawnymi kodami C ++.
(Przez „prawidłowy” rozumiem standardowy kod ze zdefiniowanym zachowaniem, tj. Nie jest specyficzny dla implementacji / niezdefiniowany / itp.)

Czy istnieje scenariusz, w którym fragment kodu poprawny zarówno w C, jak i C ++ powodowałby inne zachowanie po skompilowaniu ze standardowym kompilatorem w każdym języku?

Aby było to rozsądne / przydatne porównanie (staram się nauczyć czegoś praktycznie przydatnego, a nie próbować znaleźć oczywiste luki w pytaniu), załóżmy:

  • Nic nie jest związane z preprocesorem (co oznacza brak hacków #ifdef __cplusplus, pragm itp.)
  • Wszystko zdefiniowane w implementacji jest takie samo w obu językach (np. Ograniczenia liczbowe itp.)
  • Porównujemy stosunkowo nowe wersje każdego standardu (np. C ++ 98 i C90 lub nowszy)
    Jeśli wersje mają znaczenie, proszę wspomnieć, które wersje każdego z nich mają inne zachowanie.
użytkownik541686
źródło
11
Nawiasem mówiąc, przydatne może być programowanie w dialekcie, który jest jednocześnie C i C ++. Zrobiłem to w przeszłości i jeden bieżący projekt: język TXR. Co ciekawe, twórcy języka Lua zrobili to samo i nazywają ten dialekt „Clean C”. Korzyści płynące z lepszego sprawdzania czasu kompilacji i ewentualnie dodatkowej przydatnej diagnostyki dzięki kompilatorom C ++, przy jednoczesnym zachowaniu przenośności C.
Kaz.
9
Połączyłem starsze pytanie z tym pytaniem, ponieważ ma ono więcej poglądów i pozytywnych odpowiedzi. To wciąż przykład niekonstruktywnego pytania, ale jest dość graniczne, ponieważ tak, uczy użytkowników SO. Zamykam to jako niekonstruktywne, aby odzwierciedlić stan pytania przed scaleniem. Możesz się nie zgodzić i ponownie otworzyć.
George Stocker
13
Głosowanie na ponowne otwarcie, ponieważ myślę, że można obiektywnie odpowiedzieć „tak”, a następnie przykład (jak udowodniono poniżej). Myślę, że jest to konstruktywne, ponieważ ludzie mogą nauczyć się z niego odpowiednich zachowań.
Anders Abel
6
@AndersAbel Sama liczba odpowiedzi, z których wszystkie są poprawne, jednoznacznie pokazuje, że pozostaje to pytanie z listy. Nie było możliwości, abyś zadał to pytanie bez uzyskania listy.
dmckee --- były moderator kociąt
2
@dmckee Jeśli chodzi o wartość, zgadzam się z tobą. Jednak ludzie z tagami C ++ są ... Czy możemy powiedzieć ... zadziorni .
George Stocker

Odpowiedzi:

397

Następujące, poprawne w C i C ++, (najprawdopodobniej) spowodują różne wartości iw C i C ++:

int i = sizeof('a');

Zobacz Rozmiar znaku („a”) w C / C ++, aby uzyskać wyjaśnienie 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;
}
Alexey Frunze
źródło
8
Zdecydowanie nie spodziewałem się tego! Miałem nadzieję na coś bardziej dramatycznego, ale nadal jest to przydatne, dzięki. :) +1
użytkownik541686,
17
+1 drugi przykład jest dobry, ponieważ C ++ nie wymaga structprzed nazwami struktur.
Seth Carnegie
1
@Andrey Pomyślałem kiedyś tak samo i przetestowałem to i działało na GCC 4.7.1 bez standardowej, wbrew moim oczekiwaniom. Czy to błąd w GCC?
Seth Carnegie
3
@SethCarnegie: Niezgodny program nie musi zawieść, ale nie gwarantuje też, że będzie działał.
Andrey Vihrov
3
struct sz { int i[2];};oznaczałoby to, że C i C ++ muszą generować różne wartości. (Podczas gdy DSP z sizeof (int) == 1, może wygenerować tę samą wartość).
Martin Bonner wspiera Monikę
464

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ływanie funkcji niezadeklarowanych:

#include <stdio.h>

struct f { int x; };

int main() {
    f();
}

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

W C ++ nic to nie wydrukuje, ponieważ tymczasowe fjest tworzone i niszczone, ale w C90 będzie drukowane, helloponieważ funkcje można wywoływać bez zadeklarowania.

W przypadku, gdy zastanawiasz się nad fdwukrotnym użyciem nazwy , standardy C i C ++ wyraźnie na to zezwalają i aby stworzyć obiekt, o którym musisz powiedzieć, struct faby ujednoznacznić, jeśli chcesz strukturę, lub zrezygnować, structjeśli chcesz funkcji.

Seth Carnegie
źródło
7
Ściśle mówiąc pod C, to się nie skompiluje, ponieważ deklaracja „int f ()” jest po definicji „int main ()” :)
Sogartar
15
@Sogartar, naprawdę? codepad.org/STSQlUhh Kompilatory C99 dadzą ci ostrzeżenie, ale nadal pozwolą ci je skompilować.
jrajav
22
@Sogartar w funkcjach C może być niejawnie zadeklarowany.
Alex B
11
@AlexB Nie w C99 i C11.
6
@jrajav To nie są kompilatory C99. Kompilator C99 wykrywa niezadeklarowane identyfikatory jako błąd składniowy. Kompilator, który tego nie robi, jest albo kompilatorem C89, albo standardem wstępnym, albo innym rodzajem niezgodnego kompilatora.
430

W przypadku C ++ vs. C90 istnieje co najmniej jeden sposób uzyskania różnych zachowań, które nie są zdefiniowane w implementacji. C90 nie ma komentarzy jednowierszowych. Przy odrobinie staranności możemy go użyć do stworzenia wyrażenia o zupełnie innych wynikach w C90 i C ++.

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

W C ++ wszystko od //końca do końca jest komentarzem, więc działa to tak:

int a = 10 + 3;

Ponieważ C90 nie ma komentarzy jednowierszowych, tylko /* comment */komentarz jest. Pierwsza /i 2obie są częściami inicjalizacji, więc dochodzi do:

int a = 10 / 2 + 3;

Tak więc poprawny kompilator C ++ da 13, ale ściśle poprawny kompilator C90 8. Oczywiście, właśnie wybrałem tutaj dowolne liczby - możesz użyć innych liczb, które uważasz za stosowne.

Jerry Coffin
źródło
34
WHOA to oszałamiające !! Ze wszystkich możliwych rzeczy nigdy bym nie pomyślał, że komentarze mogą zostać wykorzystane do zmiany zachowań haha. +1
541686,
89
nawet bez 2, odczytałby jako 10 / + 3prawidłowy (unary +).
Benoit
12
Teraz dla zabawy, zmodyfikuj go tak, aby zarówno C, jak i C ++ obliczały różne wyrażenia arytmetyczne oceniające do tego samego wyniku.
Ryan C. Thompson
21
@RyanThompson Trivial. s /
2/1
4
@ Mehrdad Czy się mylę lub komentarze dotyczą preprocesora? Należy je zatem wykluczyć jako możliwą odpowiedź na twoje pytanie! ;-)
Ale
179

C90 vs. C ++ 11 ( intvs. double):

#include <stdio.h>

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

W C autooznacza zmienną lokalną. W C90 można pominąć typ zmiennej lub funkcji. Domyślnie jest to int. W C ++ 11 autooznacza coś zupełnie innego, mówi kompilatorowi, aby wywnioskował typ zmiennej z wartości użytej do jej zainicjowania.

rozpoznany
źródło
10
C90 ma auto?
Seth Carnegie
22
@SethCarnegie: Tak, to klasa pamięci; tak dzieje się domyślnie, gdy go pominiesz, więc nikt go nie użył i zmienili jego znaczenie. Myślę, że to intdomyślnie. To jest sprytne! +1
541686,
5
C11 nie ma niejawnego int.
R .. GitHub ZATRZYMAJ LÓD
23
@KeithThompson Ah, myślę, że masz na myśli wywnioskowane int. Mimo to, w prawdziwym świecie, gdzie jest mnóstwo starszego kodu, a lider rynku nadal nie wdrożył C99 i nie ma na to zamiaru, mówienie o „przestarzałej wersji C” jest absurdalne.
Jim Balter
11
„Każda zmienna MUSI mieć jawną klasę pamięci. Naprawdę, najwyższe kierownictwo”.
btown
120

Kolejny przykład, o którym jeszcze nie wspominał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;
}

To wypisuje „fałsz” w C i „prawda” w C ++ - W C każde niezdefiniowane makro ma wartość 0. W C ++ istnieje 1 wyjątek: „prawda” oznacza 1.

godlygeek
źródło
2
Ciekawy. Czy ktoś zna uzasadnienie tej zmiany?
antred
3
ponieważ „prawda” jest słowem kluczowym / prawidłową wartością, więc jest przetwarzana na prawdziwą jak każda „prawdziwa wartość” (tak jak każda dodatnia liczba całkowita). Nadal możesz zrobić #define true false, aby drukować "false" również w C ++;)
CoffeDeveloper
22
#define true false ಠ_ಠ
Bryan Boettcher
2
@DarioOO nie spowoduje, że taka redefinicja spowoduje UB?
Ruslan,
3
@DarioOO: Tak, mylisz się. Ponowna definicja słów kluczowych jest niedozwolona, ​​kara pozostawiona losowi (UB). Preprocesor jest odrębną fazą kompilacji, która nie wytrzymuje.
Deduplicator,
108

Zgodnie ze standardem C ++ 11:

za. Operator przecinka wykonuje konwersję wartości lvalue na 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, aw C będzie to sizeof(char*).

b. W C ++ typem modułu wyliczającego jest jego wyliczenie. W C typ modułu wyliczającego to 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ównysizeof(E) .

do. W C ++ funkcja zadeklarowana z pustą listą parametrów nie przyjmuje żadnych argumentów. W C pusta lista parametrów oznacza, że ​​liczba i typ parametrów funkcji jest nieznany.

   int f();           // int f(void) in C++
                      // int f(*unknown*) in C
Kirill Kobelev
źródło
Pierwszy jest również zdefiniowany w implementacji, jak Alexey's. Ale +1.
Seth Carnegie
1
@Seth, wszystkie powyższe materiały pochodzą bezpośrednio z załącznika C.1 normy C ++ 11.
Kirill Kobelev
Tak, ale wciąż jest zdefiniowane w implementacji. sizeof(char*)może wynosić 100, w którym to przypadku pierwszy przykład dałby takie same obserwowalne zachowanie w C i C ++ (tj. chociaż metoda uzyskiwania sbyłaby inna, sostatecznie byłaby równa 100). OP wspomniał, że tego rodzaju zachowanie zdefiniowane w ramach implementacji było w porządku, ponieważ chciał po prostu uniknąć odpowiedzi prawników językowych, więc pierwszy z nich jest w porządku z jego wyjątkiem. Ale drugi jest w każdym razie dobry.
Seth Carnegie
5
Jest łatwa poprawka - wystarczy zmienić przykład na:char arr[sizeof(char*)+1]; int s = sizeof(0, arr);
Mankarse
5
Aby uniknąć różnic zdefiniowanych w implementacji, możesz również użyć void *arr[100]. W tym przypadku element ma taki sam rozmiar jak wskaźnik do tego samego elementu, więc dopóki są 2 lub więcej elementów, tablica musi być większa niż adres pierwszego elementu.
finnw
53

Ten program drukuje 1w C ++ i 0C:

#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ż double abs(double)w C ++ występuje przeciążenie, więc abs(0.6)wraca, 0.6podczas gdy w C zwraca z 0powodu niejawnej konwersji podwójnej na int przed wywołaniem int abs(int). W C musisz używać fabsdo pracy double.

Pavel Chikulaev
źródło
5
musiał debugować kod innej osoby z tym problemem. Och, jak mi się podobało. W każdym razie twój program drukuje również 0 w C ++. C ++ musi użyć nagłówka „cmath” patrz porównanie pierwszy powrót 0 ideone.com/0tQB2G 2. drugi powrót 1 ideone.com/SLeANo
CoffeDeveloper
Cieszę się / przepraszam, że nie jestem jedynym, który znajduje tę różnicę podczas debugowania. Właśnie przetestowany w VS2013, pusty z tylko plikiem z tą zawartością wyświetli 1, jeśli rozszerzenie to .cpp, i 0, jeśli rozszerzenie ma rozszerzenie .c. Wygląda na to, że <matematyka.h> jest zawarta pośrednio w VS.
Pavel Chikulaev,
I wygląda na to, że w VS C ++ <math.h> zawiera elementy C ++ w globalnej przestrzeni nazw, gdzie w przypadku GCC tak nie jest. Nie jest jednak pewne, które zachowanie jest standardowe.
Pavel Chikulaev
2
Ten konkretny przykładowy kod jest zależny od implementacji: stdlib.hdefiniuje tylko abs(int)i abs(long); wersja abs(double)jest zadeklarowana przez math.h. Więc ten program może nadal wywoływać abs(int)wersję. Jest to szczegół implementacji, czy stdlib.hrównież powoduje math.hwłączenie. (Myślę, że byłby to błąd, gdyby abs(double)został wywołany, ale inne aspekty math.hnie zostały uwzględnione).
MM
1
Drugi problem polega na tym, że chociaż standard C ++ wydaje się mówić, że włączenie <math.h>obejmuje również dodatkowe przeciążenia; w praktyce okazuje się, że wszystkie główne kompilatory nie uwzględniają tych przeciążeń, chyba że <cmath>użyta zostanie forma .
MM
38
#include <stdio.h>

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

W C drukuje to bez względu na wartość sizeof(int)obecnego systemu, który jest zwykle 4w większości powszechnie używanych dziś systemów.

W C ++ musi to być wydrukowane 1.

Adam Rosenfield
źródło
3
Tak, właściwie znałem tę sztuczkę, ponieważ „c” jest intem w C i char w C ++, ale nadal dobrze jest mieć go tutaj wymienionym.
Sean
9
To byłoby interesujące pytanie do wywiadu - szczególnie dla osób, które umieściły eksperta c / c ++ w swoich życiorysach
Martin Beckett
2
Jednak trochę podstępny. Celem sizeof jest to, abyś nie musiał dokładnie wiedzieć, jak duży jest typ.
Dana the Sane
13
W C wartość jest zdefiniowana jako implementacja, a 1 jest możliwością. (W C ++ musi wydrukować 1, jak podano.)
programista Windows
3
W rzeczywistości ma nieokreślone zachowanie w obu przypadkach. %dnie jest właściwym specyfikatorem formatu size_t.
R .. GitHub ZATRZYMAJ LÓD
37

Kolejna sizeofpułapka: wyrażenia logiczne.

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

Jest równy sizeof(int)w C, ponieważ wyrażenie jest typu int, ale zwykle ma 1 w C ++ (choć nie jest to wymagane). W praktyce są prawie zawsze różne.

Alex B.
źródło
6
One !powinny być wystarczająco dla bool.
Alexey Frunze
4
!! jest int operatora logicznego przetwarzania :)
EvilTeach
1
sizeof(0)jest 4w C i C ++, ponieważ 0jest wartością całkowitą. sizeof(!0)jest 4w C i 1C ++. Logiczne NIE działa na operandach typu bool. Jeśli wartość int 0jest niejawnie konwertowana na false(wartość bool), to jest odwracana, w wyniku czego true. Zarówno truei falsesą rvalues bool w C ++ i sizeof(bool)jest 1. Jednak w C !0ocenia się na 1, co jest wartością typu int. Język programowania C domyślnie nie ma typu danych bool.
Galaxy
26

Język programowania C ++ (wydanie trzecie) 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. Struktury itp. Ukrywają rzeczy poza zasięgami, jak w twoim przykładzie.

derobert
źródło
25

Stary kasztan, który zależy od kompilatora C, nie rozpoznaje komentarzy na końcu C ++ ...

...
int a = 4 //* */ 2
        +2;
printf("%i\n",a);
...
dmckee --- były kot moderator
źródło
21

Kolejny wymieniony przez C ++ Standard:

#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)); 
}
Johannes Schaub - litb
źródło
więc masz różnice w wypełnianiu?
v.oddou
ah przepraszam mam to, jest inny xna górze. myślałem, że powiedziałeś „tablica a”.
v.oddou
20

Funkcje wbudowane w C domyślnie mają zasięg zewnętrzny, gdzie nie działają tak jak w C ++.

Kompilacja dwóch następujących plików razem spowoduje wydrukowanie „Jestem inline” w przypadku GNU C, ale nic w przypadku 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");
} 

Ponadto C ++ domyślnie traktuje dowolne constglobalne, staticchyba że jest to jawnie zadeklarowane extern, w przeciwieństwie do C, w którym externdomyślna jest wartość C.

fkl
źródło
Naprawdę tak nie uważam. Prawdopodobnie nie trafiłeś w sedno. Nie chodzi o definicję struktury st, która jest po prostu używana do tego, aby kod był poprawny c ++. Chodzi o to, że podkreśla różne zachowanie funkcji inline w c vs. c ++. To samo dotyczy zewnętrznego. Żadne z nich nie jest omawiane w żadnym z rozwiązań.
fkl
2
Jakie jest inne zachowanie funkcji wbudowanych i externktóre zostało tutaj wykazane?
Seth Carnegie
Jest napisane dość wyraźnie. „Funkcje inline domyślnie w zakresie c są zewnętrznymi zakresami, gdy nie są tak jak w c ++ (kod to pokazuje). Również C ++ domyślnie traktuje każdą stałą globalną jako zakres pliku, chyba że jest jawnie zadeklarowany jako zewnętrzny, w przeciwieństwie do C, w którym domyślny jest zewnętrzny. Podobny można dla tego utworzyć przykład ”. Jestem zdziwiony - czy to nie jest zrozumiałe?
fkl
12
@fayyazkl Przedstawione zachowanie wynika tylko z różnicy wyszukiwania ( struct funvs fn) i nie ma nic wspólnego z tym, czy funkcja jest wbudowana. Wynik jest identyczny, jeśli usuniesz inlinekwalifikator.
Alex B
3
W ISO C program ten jest źle sformułowany: inlinezostał dodany dopiero w C99, ale w C99 fun()nie można go wywołać bez prototypu w zakresie. Zakładam więc, że ta odpowiedź dotyczy tylko GNU C.
MM
16
struct abort
{
    int x;
};

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

Zwraca kod zakończenia 0 w C ++ lub 3 w C.

Tej sztuczki można by prawdopodobnie użyć do zrobienia czegoś bardziej interesującego, ale nie mogłem wymyślić dobrego sposobu na stworzenie konstruktora, który byłby przyjemny dla C. Próbowałem zrobić podobnie nudny przykład z konstruktorem kopiowania, który pozwoliłby na argument być przekazywane, choć w raczej nieprzenośny sposób:

struct exit
{
    int x;
};

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

    exit(code);

    return 0;
}

VC ++ 2005 odmówił jednak skompilowania tego w trybie C ++, narzekając na to, jak redefiniowano „kod wyjścia”. (Wydaje mi się, że jest to błąd kompilatora, chyba że nagle zapomniałem, jak programować.) Został zakończony z kodem wyjścia procesu 1, gdy został skompilowany jako C.


źródło
Twój drugi przykład z użyciem exit nie kompiluje się na gcc lub g ++, niestety. To jednak dobry pomysł.
Sean
1
exit(code)najwyraźniej jest poprawną deklaracją zmiennej codetypu exit. (Zobacz „najbardziej dokuczliwa analiza”, która jest inną, ale podobną kwestią).
user253751
16
#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 wypisuje 128( 32 * sizeof(double)) po kompilacji za pomocą kompilatora C ++ i 4po kompilacji za pomocą kompilatora C.

Wynika to z faktu, że C nie ma pojęcia rozdzielczości zakresu. W C struktury zawarte w innych strukturach wchodzą w zakres struktury zewnętrznej.

wefwefa3
źródło
Ten jest interesujący! (Myślę, że masz na myśli 32*sizeof(double)raczej niż 32 :))
user1354557
3
zwróć uwagę, że otrzymujesz UB, drukując za size_tpomocą%d
phuclv
7

Nie zapomnij o różnicy między globalnymi przestrzeniami nazw C i C ++. Załóżmy, że masz plik 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 teraz, że masz pliki main.c i main.cpp, które wyglądają tak:

extern void foo(int);

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

Po skompilowaniu jako C ++ użyje symbolu w globalnej przestrzeni nazw C ++; w C użyje 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
użytkownik23614
źródło
Masz na myśli specyfikację połączenia?
user541686,
mangling nazwy. Nazwy C ++ mają prefiksy i sufiksy, a C nie
CoffeDeveloper
Zmiana nazwy nie jest częścią specyfikacji C ++. Czy jest to zabronione w C?
skyking
5
Jest to niezdefiniowane zachowanie (wielokrotna definicja foo). Nie ma oddzielnych „globalnych przestrzeni nazw”.
MM
4
int main(void) {
    const int dim = 5; 
    int array[dim];
}

Jest to dość szczególne, ponieważ jest poprawne w C ++ oraz w C99, C11 i C17 (choć opcjonalne w C11, C17); ale nie dotyczy C89.

W C99 + tworzy tablicę o zmiennej długości, która ma swoje specyficzne cechy w porównaniu z normalnymi tablicami, ponieważ ma typ środowiska wykonawczego zamiast typu kompilacji i sizeof arraynie jest wyrażeniem stałym w liczbach całkowitych w C. W C ++ typ jest całkowicie statyczny.


Jeśli spróbujesz dodać tutaj inicjator:

int main(void) {
    const int dim = 5; 
    int array[dim] = {0};
}

jest poprawny w C ++, ale nie w C, ponieważ tablice o zmiennej długości nie mogą mieć inicjatora.

Antti Haapala
źródło
0

Dotyczy to wartości i wartości w C i C ++.

W języku programowania C zarówno operatory wstępnej, jak i końcowej operacji zwracają wartości, a nie wartości. Oznacza to, że nie mogą znajdować się po lewej stronie =operatora przypisania. Obie te instrukcje 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 */

Jednak w C ++ operator wstępnej inkrementacji zwraca wartość , podczas gdy operator po inkrementacji zwraca wartość. Oznacza to, że wyrażenie z operatorem wstępnego przyrostu można umieścić 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 inkrementuje zmienną i zwraca zmienną taką, jaka była wcześniej inkrementacją. To właściwie tylko wartość. Poprzednia wartość zmiennej a jest tymczasowo kopiowana do rejestru, a następnie jest zwiększana. Ale poprzednia wartość a jest zwracana przez wyrażenie, jest to wartość. Nie reprezentuje już bieżącej zawartości zmiennej.

Wstępna inkrementacja najpierw inkrementuje zmienną, a następnie zwraca zmienną taką, jaka była po inkrementacji. W takim przypadku nie musimy przechowywać starej wartości zmiennej w rejestrze tymczasowym. Po prostu pobieramy nową wartość zmiennej po jej zwiększeniu. Tak więc przyrost wstępny zwraca wartość, zwraca samą zmienną. Możemy użyć przypisania tej wartości do czegoś innego, to jest jak poniższa instrukcja. Jest to domyślna konwersja wartości do wartości.

int x = a;
int x = ++a;

Ponieważ wstępny przyrost zwraca wartość, możemy również mu ​​coś przypisać. Poniższe dwa stwierdzenia są identyczne. W drugim zadaniu najpierw zwiększa się a, a następnie nową wartość zastępuje 2.

int a;
a = 2;
++a = 2;  // Valid in C++.
Galaktyka
źródło
3
Nie ma tutaj „ważnego w C”.
o11c
0

Puste struktury mają rozmiar 0 w C i 1 w C ++:

#include <stdio.h>

typedef struct {} Foo;

int main()
{
    printf("%zd\n", sizeof(Foo));
    return 0;
}
Daniel Frużyński
źródło
1
Nie, różnica polega na tym, że C nie ma pustych struktur, z wyjątkiem rozszerzenia kompilatora, tzn. Ten kod nie pasuje „jest poprawny zarówno w C, jak i C ++”
Antti Haapala