Umieszczenie gwiazdki w deklaracjach wskaźników

95

Niedawno zdecydowałem, że muszę w końcu nauczyć się C / C ++ i jest jedna rzecz, której nie rozumiem, jeśli chodzi o wskaźniki, a dokładniej ich definicję.

A co z tymi przykładami:

  1. int* test;
  2. int *test;
  3. int * test;
  4. int* test,test2;
  5. int *test,test2;
  6. int * test,test2;

Otóż, według mojego zrozumienia, pierwsze trzy przypadki robią to samo: Test nie jest intem, ale wskaźnikiem do jednego.

Drugi zestaw przykładów jest nieco bardziej skomplikowany. W przypadku 4 zarówno test, jak i test2 będą wskaźnikami do int, podczas gdy w przypadku 5 tylko test jest wskaźnikiem, podczas gdy test2 jest „prawdziwą” liczbą int. A co z przypadkiem 6? Tak samo jak w przypadku 5?

Michael Stum
źródło
10
W C / C ++ białe spacje nie zmieniają znaczenia.
Sulthan
19
7. int*test;?
Jin Kwon
3
+1, ponieważ pomyślałem tylko o zapytaniu o 1 - 3. Czytanie tego pytania nauczyło mnie czegoś o 4 - 6, o czym nigdy nie pomyślałem.
vastlysuperiorman
@Sulthan To prawda w 99% przypadków, ale nie zawsze. Przychodzi mi do głowy typ szablonów w wymaganiach dotyczących miejsca na typy szablonów (przed C ++ 11). W musiał być napisany tak, aby nie być traktowane jako prawo przesunięcia. Foo<Bar<char>>>>> >
AnorZaken,
3
@AnorZaken Masz rację, to dość stary komentarz. Istnieje wiele sytuacji, w których spacja zmieni znaczenie, na przykład ++operator inkrementacji nie może zostać podzielony spacją, identyfikatorów nie można podzielić spacją (a wynik może być nadal prawidłowy dla kompilatora, ale z niezdefiniowanym zachowaniem w czasie wykonywania). Dokładne sytuacje są bardzo trudne do zdefiniowania, biorąc pod uwagę bałagan składniowy, jakim jest C / C ++.
Sulthan

Odpowiedzi:

128

4, 5 i 6 to to samo, tylko test to wskaźnik. Jeśli potrzebujesz dwóch wskazówek, powinieneś użyć:

int *test, *test2;

Lub jeszcze lepiej (żeby wszystko było jasne):

int* test;
int* test2;
Milan Babuškov
źródło
3
Czyli przypadek 4 jest właściwie śmiertelną pułapką? Czy istnieje specyfikacja lub dalsze lektury, które wyjaśniają, dlaczego int * test, test2 sprawia, że ​​pierwsza zmienna jest wskaźnikiem?
Michael Stum
8
@ Michael Stum To C ++, więc czy naprawdę uważasz, że istnieje logiczne wytłumaczenie?
Joe Phillips
6
Przeczytaj K&R (język programowania C). Wyjaśnia to wszystko bardzo jasno.
Ferruccio,
8
Przypadki 4, 5 i 6 to „śmiertelne pułapki”. Jest to jeden z powodów, dla których wiele głupców w stylu C / C ++ sugeruje tylko jedną deklarację na instrukcję.
Michael Burr
14
Biała spacja nie ma znaczenia dla kompilatora C (ignoruje preprocesor). Więc bez względu na to, ile spacji jest lub nie ma między gwiazdką a otoczeniem, ma dokładnie to samo znaczenie.
ephemient
45

Puste miejsca wokół gwiazdek nie mają znaczenia. Wszystkie trzy oznaczają to samo:

int* test;
int *test;
int * test;

int *var1, var2” To zła składnia, która ma po prostu zmylić ludzi i należy jej unikać. Rozszerza się do:

int *var1;
int var2;
Ates Goral
źródło
16
Nie zapomnij int*test.
Mateen Ulhaq
2
Przestrzeń przed lub za gwiazdką to tylko kwestia estetyki. Jednak standard Google Coding jest zgodny z int *test( google-styleguide.googlecode.com/svn/trunk/ ... ). Po prostu bądź konsekwentny
@SebastianRaschka Przewodnik po stylach Google C ++ wyraźnie zezwala na umieszczanie dowolnej gwiazdki. Być może zmieniło się, odkąd to przeczytałeś.
Jared Beck
33

Wiele wytycznych dotyczących kodowania zaleca deklarowanie tylko jednej zmiennej w wierszu . Pozwala to uniknąć nieporozumień, jakie miałeś przed zadaniem tego pytania. Większość programistów C ++, z którymi pracowałem, wydaje się trzymać się tego.


Na marginesie wiem, ale coś, co uznałem za przydatne, to czytanie deklaracji wstecz.

int* test;   // test is a pointer to an int

Zaczyna to działać bardzo dobrze, zwłaszcza gdy zaczynasz deklarować wskaźniki const i trudno jest dowiedzieć się, czy to wskaźnik jest stała, czy też to, na co wskazuje wskaźnik, jest stała.

int* const test; // test is a const pointer to an int

int const * test; // test is a pointer to a const int ... but many people write this as  
const int * test; // test is a pointer to an int that's const
Scott Langham
źródło
Chociaż opcja „jedna zmienna w wierszu” wydaje się przydatna, nadal nie udało nam się całkowicie rozwiązać sytuacji, w której gwiazdka znajduje się bardziej w lewo lub bardziej w prawo. Jestem całkiem pewien, że w kodzie na wolności przeważa jeden wariant; trochę tak, jak niektóre kraje jeżdżą po prawej stronie, a inne w złym kierunku, na przykład Wielka Brytania. ;-)
shevy
Niestety z moich wypraw na dziko widzę wiele obu stylów. W moim zespole używamy teraz formatu clang w uzgodnionym przez nas stylu. Oznacza to przynajmniej, że cały kod tworzony przez nasz zespół ma ten sam styl, w którym znajdują się białe znaki.
Scott Langham
33

Użyj „Reguły spirali zgodnej z ruchem wskazówek zegara”, aby pomóc w analizowaniu deklaracji C / C ++;

Należy wykonać trzy proste kroki:

  1. Rozpoczynając od nieznanego elementu, poruszaj się po spirali / zgodnie z ruchem wskazówek zegara; napotkając następujące elementy, zamień je na odpowiednie angielskie stwierdzenia:

    [X] lub [] : Array X size of ... or Array undefined size of ...

    (type1, type2): funkcja przekazująca typ1 i zwracająca typ2 ...

    *: wskaźnik (i) do ...

  2. Rób to po spirali / zgodnie z ruchem wskazówek zegara, aż wszystkie żetony zostaną zakryte.
  3. Zawsze najpierw rozwiązuj wszystko w nawiasach!

Ponadto deklaracje powinny być w miarę możliwości w oddzielnych oświadczeniach (co jest prawdą w większości przypadków).

Michael Burr
źródło
4
Przykro mi to mówić, ale wygląda to zniechęcająco i dość okropnie.
Joe Phillips
7
tak, ale wydaje się całkiem dobrym wyjaśnieniem niektórych bardziej skomplikowanych konstrukcji
Michael Stum
@ d03boy: Nie ma wątpliwości - deklaracje C / C ++ mogą być koszmarem.
Michael Burr,
2
„Spirala” nie ma żadnego sensu, a tym bardziej „zgodnie z ruchem wskazówek zegara”. Wolałbym nazwać to „regułą prawa-lewa”, ponieważ składnia nie sprawia, że ​​wyglądasz prawy-dolny-lewy-górny, tylko prawy-lewy.
Ruslan,
Nauczyłem się tego jako zasady „prawa-lewa-prawa”. Ludzie C ++ często lubią udawać, że wszystkie informacje o typie znajdują się po lewej stronie, co prowadzi int* x;raczej do stylu niż do int *x;stylu tradycyjnego . Oczywiście odstępy nie mają znaczenia dla kompilatora, ale mają wpływ na ludzi. Odmowa właściwej składni prowadzi do reguł stylu, które mogą denerwować i wprawiać czytelników w zakłopotanie.
Adrian McCarthy
12

Jak wspomnieli inni, 4, 5 i 6 są takie same. Często ludzie używają tych przykładów, aby argument, że *należy do zmiennej, a nie do typu. Chociaż jest to kwestia stylu, toczy się dyskusja, czy należy o tym myśleć i pisać w ten sposób:

int* x; // "x is a pointer to int"

lub w ten sposób:

int *x; // "*x is an int"

FWIW Jestem w pierwszym obozie, ale inni argumentują za drugą formą, ponieważ (w większości) rozwiązuje ona ten konkretny problem:

int* x,y; // "x is a pointer to int, y is an int"

który może wprowadzać w błąd; zamiast tego napisałbyś albo

int *x,y; // it's a little clearer what is going on here

lub jeśli naprawdę potrzebujesz dwóch wskazówek,

int *x, *y; // two pointers

Osobiście mówię, że trzymaj to jedną zmienną w wierszu, wtedy nie ma znaczenia, jaki styl preferujesz.

huskerchad
źródło
6
to jest fałszywe, jak nazywasz to int *MyFunc(void)? a *MyFuncjest funkcją zwracającą int? Nie. Oczywiście powinniśmy napisać int* MyFunc(void)i powiedzieć, że MyFuncjest to funkcja zwracająca int*. Dla mnie jest to jasne, reguły analizowania gramatyki C i C ++ są po prostu nieprawidłowe dla deklaracji zmiennych. powinny zawierać kwalifikację wskaźnika jako część typu wspólnego dla całej sekwencji przecinków.
v.oddou
1
Ale *MyFunc() jestint . Problem ze składnią C polega na mieszaniu składni przedrostka i postfiksa - gdybyśmy użyli tylko przyrostka, nie byłoby zamieszania.
Antti Haapala
1
Pierwszy obóz walczy ze składnią języka, prowadząc do mylących konstrukcji, takich jak int const* x;, które uważam za równie mylące jak a * x+b * y.
Adrian McCarthy
11
#include <type_traits>

std::add_pointer<int>::type test, test2;
fredoverflow
źródło
#include <windows.h>LPINT test, test2;
Stefan Dragnev
5

W punktach 4, 5 i 6 testjest zawsze wskaźnikiem i test2nie jest wskaźnikiem. Białe znaki (prawie) nigdy nie mają znaczenia w C ++.

1800 INFORMACJE
źródło
3

Uzasadnienie w C jest takie, że deklarujesz zmienne w sposób, w jaki ich używasz. Na przykład

char *a[100];

mówi, że *a[42]będzie char. I a[42]wskaźnik postaci. A zatema jest tablicą wskaźników znaków.

Dzieje się tak, ponieważ pierwotni autorzy kompilatorów chcieli używać tego samego parsera do wyrażeń i deklaracji. (Niezbyt rozsądny powód do wyboru projektu językowego)

Michel Billaud
źródło
Jednak pisząc char* a[100]; również wnioskuje, że *a[42];będzie to chari a[42];wskaźnik znaku.
yyny,
Cóż, wszyscy wyciągamy te same wnioski, tylko kolejność jest inna.
Michel Billaud
Cytuj: „mówi, że * a [42] będzie znakiem. A [42] wskaźnikiem znaku”. Czy na pewno nie jest na odwrót?
deLock
Jeśli wolisz inny sposób, powiedz, że a[42]jest charwskaźnikiem i *a[42]jest znakiem.
Michel Billaud
3

Moim zdaniem odpowiedź brzmi OBIE, w zależności od sytuacji. Ogólnie rzecz biorąc, IMO, lepiej jest umieścić gwiazdkę obok nazwy wskaźnika, a nie typu. Porównaj np .:

int *pointer1, *pointer2; // Fully consistent, two pointers
int* pointer1, pointer2;  // Inconsistent -- because only the first one is a pointer, the second one is an int variable
// The second case is unexpected, and thus prone to errors

Dlaczego drugi przypadek jest niespójny? Bo np. int x,y;Deklaruje dwie zmienne tego samego typu, ale typ jest wymieniany tylko raz w deklaracji. Stwarza to precedens i oczekiwane zachowanie. I int* pointer1, pointer2;jest z tym niespójny, ponieważ deklaruje pointer1jako wskaźnik, alepointer2 jest zmienną całkowitą. Są wyraźnie podatne na błędy, dlatego należy ich unikać (umieszczając gwiazdkę obok nazwy wskaźnika, a nie typu).

Jednak istnieją pewne wyjątki , gdzie może nie być w stanie umieścić gwiazdkę obok nazwy obiektu (a tam gdzie ma to znaczenie, gdzie można umieścić go) bez uzyskania niepożądanego rezultatu - na przykład:

MyClass *volatile MyObjName

void test (const char *const p) // const value pointed to by a const pointer

Wreszcie, w niektórych przypadkach może być prawdopodobnie bardziej zrozumiałe umieszczenie gwiazdki obok nazwy typu , np .:

void* ClassName::getItemPtr () {return &item;} // Clear at first sight

deLock
źródło
2

Powiedziałbym, że początkową konwencją było umieszczenie gwiazdy po stronie nazwy wskaźnika (po prawej stronie deklaracji

Możesz przestrzegać tych samych zasad, ale nie jest to wielka sprawa, jeśli umieścisz gwiazdki po stronie czcionki. Pamiętaj, że spójność jest ważna, więc zawsze, ale gwiazda po tej samej stronie, niezależnie od tego, którą stronę wybierzesz.

TheDrev
źródło
Cóż - wydaje się, że parser dopuszcza oba warianty, ale jeśli Dennis i Linus twierdzą, że powinien być po prawej stronie, jest to dość przekonujące. Ale nadal brakuje nam jakiegoś uzasadnienia, a także wyjaśnienia, dlaczego tak się dzieje. To trochę jak sytuacja tab w porównaniu z przestrzenią - z tym wyjątkiem, że jedna została rozwiązana, ponieważ ludzie, którzy używają spacji zamiast tabulatorów, zarabiają więcej pieniędzy, według StackOverflow ... :-)
shevy
1

Wskaźnik jest modyfikatorem typu. Najlepiej czytać je od prawej do lewej, aby lepiej zrozumieć, jak gwiazdka modyfikuje tekst. „int *” można odczytać jako „wskaźnik do int”. W wielu deklaracjach należy określić, że każda zmienna jest wskaźnikiem, w przeciwnym razie zostanie utworzona jako zmienna standardowa.

1,2 i 3) Test jest typu (int *). Białe znaki nie mają znaczenia.

4,5 i 6) Test jest typu (int *). Test2 jest typu int. Znów białe znaki są nieistotne.

Ron Warholic
źródło
1

Ta układanka składa się z trzech elementów.

Po pierwsze, białe znaki w C i C ++ zwykle nie mają znaczenia poza oddzieleniem sąsiednich tokenów, które w przeciwnym razie są nie do odróżnienia.

Na etapie przetwarzania wstępnego tekst źródłowy jest dzielony na sekwencję tokenów - identyfikatorów, znaków przestankowych, literałów numerycznych, literałów łańcuchowych itp. Ta sekwencja tokenów jest później analizowana pod kątem składni i znaczenia. Tokenizer jest „chciwy” i zbuduje najdłuższy ważny token, jaki jest możliwy. Jeśli napiszesz coś w stylu

inttest;

tokenizer widzi tylko dwa tokeny - identyfikator, inttestpo którym następuje znak interpunkcyjny ;. Na inttym etapie nie rozpoznaje się jako oddzielnego słowa kluczowego (dzieje się to później w procesie). Tak więc, aby wiersz był odczytywany jako deklaracja liczby całkowitej o nazwie test, musimy użyć białych znaków do oddzielenia tokenów identyfikujących:

int test;

*Postać nie jest częścią żadnego identyfikatora; jest to osobny token (interpunkcja). Więc jeśli piszesz

int*test;

kompilator widzi 4 oddzielne znaki - int, *, test, i ;. W związku z tym białe znaki nie są znaczące w deklaracjach wskaźników i we wszystkich

int *test;
int* test;
int*test;
int     *     test;

są interpretowane w ten sam sposób.


Drugim elementem układanki jest sposób działania deklaracji w C i C ++ 1 . Deklaracje są podzielone na dwie główne części - sekwencję specyfikatorów deklaracji ( specyfikatory klasy pamięci, specyfikatory typu, kwalifikatory typów itp.), Po których następuje rozdzielona przecinkami lista (prawdopodobnie zainicjowanych) deklaratorów . W deklaracji

unsigned long int a[10]={0}, *p=NULL, f(void);

specyfikatory deklaracji są unsigned long inti declarators są a[10]={0}, *p=NULLi f(void). Do wprowadza declarator nazwa rzeczy są zadeklarowane ( a, p, if ) wraz z informacjami o tablicowości, wskaźnikach i funkcjach tej rzeczy. Deklarator może również mieć skojarzony inicjator.

Typ ato „10-elementowa tablica unsigned long int”. Ten typ jest w pełni określony przez połączenie specyfikatorów deklaracji i deklaratora, a wartość początkowa jest określana za pomocą inicjatora ={0}. Podobnie, typ pjest „wskaźnikiem do unsigned long int” i ponownie ten typ jest określany przez kombinację specyfikatorów deklaracji i deklaratora, i jest inicjowany NULL. A typem fjest „funkcja powracająca unsigned long int” z tego samego powodu.

To jest klucz - nie ma specyfikatora typu „wskaźnik do” , tak jak nie ma specyfikatora typu „tablica”, tak jak nie ma specyfikatora typu „zwracającego funkcję”. Nie możemy zadeklarować tablicy jako

int[10] a;

ponieważ operand klasy [] operatora to anie int. Podobnie w deklaracji

int* p;

operand *jestp , nie int. Ale ponieważ operator pośredni jest jednoargumentowy, a białe znaki nie są znaczące, kompilator nie będzie narzekał, jeśli napiszemy go w ten sposób. Jednak zawsze jest interpretowane jako int (*p);.

Dlatego jeśli piszesz

int* p, q;

operand * jest p, więc zostanie zinterpretowany jako

int (*p), q;

Tak więc wszystkie

int *test1, test2;
int* test1, test2;
int * test1, test2;

zrób to samo - we wszystkich trzech przypadkach test1jest operandem *i dlatego ma typ „wskaźnika do”int ”, natomiast test2ma typ int.

Deklaratory mogą być dowolnie złożone. Możesz mieć tablice wskaźników:

T *a[N];

możesz mieć wskaźniki do tablic:

T (*a)[N];

możesz mieć funkcje zwracające wskaźniki:

T *f(void);

możesz mieć wskaźniki do funkcji:

T (*f)(void);

możesz mieć tablice wskaźników do funkcji:

T (*a[N])(void);

możesz mieć funkcje zwracające wskaźniki do tablic:

T (*f(void))[N];

możesz mieć funkcje zwracające wskaźniki do tablic wskaźników do funkcji zwracających wskaźniki do T:

T *(*(*f(void))[N])(void); // yes, it's eye-stabby.  Welcome to C and C++.

a następnie masz signal:

void (*signal(int, void (*)(int)))(int);

co brzmi jak

       signal                             -- signal
       signal(                 )          -- is a function taking
       signal(                 )          --   unnamed parameter
       signal(int              )          --   is an int
       signal(int,             )          --   unnamed parameter
       signal(int,      (*)    )          --   is a pointer to
       signal(int,      (*)(  ))          --     a function taking
       signal(int,      (*)(  ))          --       unnamed parameter
       signal(int,      (*)(int))         --       is an int
       signal(int, void (*)(int))         --     returning void
     (*signal(int, void (*)(int)))        -- returning a pointer to
     (*signal(int, void (*)(int)))(   )   --   a function taking
     (*signal(int, void (*)(int)))(   )   --     unnamed parameter
     (*signal(int, void (*)(int)))(int)   --     is an int
void (*signal(int, void (*)(int)))(int);  --   returning void
    

a to ledwo zarysowuje powierzchnię tego, co jest możliwe. Ale zwróć uwagę, że rodzaj tablicy, wskaźnik i funkcja są zawsze częścią deklaratora, a nie specyfikatora typu.

Jedna rzecz, na którą należy zwrócić uwagę - constmożna modyfikować zarówno typ wskaźnika, jak i typ wskazany:

const int *p;  
int const *p;

Oba powyższe są deklarowane pjako wskaźnik do const intobiektu. Możesz napisać nową wartość, aby pustawić ją tak, aby wskazywała na inny obiekt:

const int x = 1;
const int y = 2;

const int *p = &x;
p = &y;

ale nie możesz pisać do wskazanego obiektu:

*p = 3; // constraint violation, the pointed-to object is const

Jednak,

int * const p;

deklaruje pjako constwskaźnik do wartości innej niż stała int; możesz napisać do rzeczy, na którą pwskazuje

int x = 1;
int y = 2;
int * const p = &x;

*p = 3;

ale nie możesz ustawić pwskazywania na inny obiekt:

p = &y; // constraint violation, p is const

To prowadzi nas do trzeciego elementu układanki - dlaczego deklaracje są tak skonstruowane.

Celem jest, aby struktura deklaracji ściśle odzwierciedlała strukturę wyrażenia w kodzie („deklaracja naśladuje użycie”). Na przykład, załóżmy, że mamy tablicę wskaźników do intnamed api chcemy uzyskać dostęp do intwartości wskazywanej przez i-ty element. Dostęp do tej wartości uzyskalibyśmy w następujący sposób:

printf( "%d", *ap[i] );

Ekspresja *ap[i] jest typu int; w związku z tym deklaracja apjest zapisana jako

int *ap[N]; // ap is an array of pointer to int, fully specified by the combination
            // of the type specifier and declarator

Deklarator *ap[N]ma taką samą strukturę jak wyrażenie *ap[i]. Operatory *i []zachowują się w taki sam sposób w deklaracji, jak w wyrażeniu - []mają wyższy priorytet niż jednoargumentowe *, więc operand *is ap[N](jest analizowany jako *(ap[N])).

Jako inny przykład załóżmy, że mamy wskaźnik do tablicy o intnazwie named pai chcemy uzyskać dostęp do wartości i'-tego elementu. Napisalibyśmy to jako

printf( "%d", (*pa)[i] );

Typ wyrażenia (*pa)[i]to int, więc deklaracja jest zapisywana jako

int (*pa)[N];

Ponownie, obowiązują te same zasady pierwszeństwa i kojarzenia. W tym przypadku nie chcemy wyłuskiwać i'-tego elementu pa, chcemy uzyskać dostęp do i' -tego elementu, na który pa wskazuje , więc musimy jawnie zgrupować* operator zpa .

Te *, []i ()operatorzy są częścią wyrażenia w kodzie, więc wszystkie one są częścią declarator w deklaracji. Deklarator mówi, jak używać obiektu w wyrażeniu. Jeśli masz taką deklarację int *p;, która mówi ci, że wyrażenie *pw twoim kodzie zwróci intwartość. Jako rozszerzenie mówi ci, że wyrażenie pzwraca wartość typu „wskaźnik do int”, lub int *.


A co z takimi rzeczami, jak obsada i sizeofwyrażenia, gdzie używamy rzeczy takich jak (int *)lub sizeof (int [10])lub takich rzeczy? Jak czytam coś takiego

void foo( int *, int (*)[10] );

Nie ma deklaratora, *czy []operatory i nie modyfikują typu bezpośrednio?

Cóż, nie - nadal istnieje deklarator, tylko z pustym identyfikatorem (znany jako abstrakcyjny deklarator ). Jeśli będziemy reprezentować pusty identyfikator z Î symbolu, to możemy czytać takie rzeczy jak (int *λ), sizeof (int λ[10])oraz

void foo( int *λ, int (*λ)[10] );

i zachowują się dokładnie tak, jak każda inna deklaracja. int *[10]reprezentuje tablicę 10 wskaźników, podczas gdy int (*)[10]reprezentuje wskaźnik do tablicy.


A teraz uparta część tej odpowiedzi. Nie podoba mi się konwencja C ++ deklarowania prostych wskaźników jako

T* p;

i uważaj to za złą praktykę z następujących powodów:

  1. Nie jest to zgodne ze składnią;
  2. Wprowadza zamieszanie (o czym świadczy to pytanie, wszystkie duplikaty tego pytania, pytania o znaczenie T* p, q;, wszystkie duplikaty tych pytań itp.);
  3. To nie jest wewnętrznie spójne - deklarowanie tablicy wskaźników jako T* a[N]asymetryczne w użyciu (chyba że masz zwyczaj pisania * a[i]);
  4. Nie można go zastosować do typów wskaźnika do tablicy lub wskaźnika do funkcji (chyba że utworzysz typedef tylko po to, abyś mógł T* pczysto zastosować konwencję, która ... nie );
  5. Powód takiego postępowania - „podkreśla to, że obiekt jest wskazany” - jest fałszywy. Nie można go zastosować do tablic lub typów funkcji i myślę, że te cechy są równie ważne, aby je podkreślić.

W końcu oznacza to po prostu zdezorientowane myślenie o tym, jak działają systemy typów w dwóch językach.

Istnieją dobre powody, aby zgłaszać pozycje osobno; obejście złej praktyki ( T* p, q;) nie jest jednym z nich. Jeśli poprawnie napiszesz swoje deklaratory ( T *p, q;), jest mniej prawdopodobne, że wprowadzisz zamieszanie.

Uważam to za celowe pisanie wszystkich prostych forpętli jako

i = 0;
for( ; i < N; ) 
{ 
  ... 
  i++ 
}

Składniowo poprawne, ale mylące, a zamiar prawdopodobnie zostanie źle zinterpretowany. Jednak T* p;konwencja ta jest zakorzeniona w społeczności C ++ i używam jej we własnym kodzie C ++, ponieważ spójność w całym kodzie jest dobra, ale za każdym razem, gdy to robię, swędzi mnie.


  1. Będę używać terminologii C - terminologia C ++ jest trochę inna, ale koncepcje są w dużej mierze takie same.
John Bode
źródło
-3

Dobra zasada praktyczna: wiele osób wydaje się rozumieć te pojęcia przez: W C ++ wiele znaczeń semantycznych jest wyprowadzanych z lewego wiązania słów kluczowych lub identyfikatorów.

Weź na przykład:

int const bla;

Stała dotyczy słowa „int”. To samo dotyczy gwiazdek wskaźników, mają one zastosowanie do słowa kluczowego po lewej stronie. A rzeczywista nazwa zmiennej? Tak, to zostało zadeklarowane przez to, co z niego zostało.

mstrobl
źródło
1
To nie odpowiada na pytanie. Co gorsza, jeśli spróbujemy wywnioskować z niego odpowiedź, oznacza to, że gwiazdka wiąże się z typem po lewej stronie, co, jak wszyscy powiedzieli, jest fałszywe. Wiąże się z pojedynczą nazwą zmiennej po jej prawej stronie.
underscore_d