Czy wszystkie wskaźniki pochodzące od wskaźników do typów struktur są takie same?

Pytanie

Pytanie, czy wszystkie wskaźniki pochodzące od wskaźników do typów struktur są takie same, nie jest łatwe do odpowiedzi. Uważam, że jest to ważne pytanie z następujących dwóch podstawowych powodów.

A. brak wskaźnika do wskaźnika do "dowolnego" typu niekompletnego lub obiektowego, nakłada ograniczenie na wygodne interfejsy funkcji, takie jak:

int allocate(ANY_TYPE  **p,
             size_t    s);

int main(void)
{
    int *p;
    int r = allocate(&p, sizeof *p);
}

[Pełna próbka kodu ]

Istniejący wskaźnik na "dowolny" niekompletny lub typ obiektu jest jawnie opisany jako:

C99 / C11 §6.3.2.3 p1:

Wskaźnik do void może być zamieniony na lub ze wskaźnika na dowolny niekompletny lub obiektowy Typ. [...]

Wskaźnik uzyskany z istniejącego wskaźnika do "dowolnego" niekompletnego lub typu obiektu, wskaźnik do wskaźnika do unieważnienia, jest ściśle wskaźnikiem do wskaźnika do unieważnienia i nie jest wymagany do zamiany ze wskaźnikiem uzyskanym ze wskaźnika do "dowolnego" niekompletnego lub obiektu Typ.


B. programiści często używają konwencji opartych na założeniach, które nie są wymagane, związanych z uogólnianiem wskaźników, świadomie lub nieświadomie, w zależności od ich doświadczenia z konkretnymi implementacjami. Założenia takie jak możliwość zamiany, możliwość reprezentowania jako liczby całkowite lub współdzielenie wspólnej właściwości: rozmiar obiektu, reprezentacja lub wyrównanie.


Słowa standardu

Według C99 §6.2.5 p27 / C11 §6.2.5 p28:

[...] Wszystkie wskaźniki do typów struktur muszą mieć takie same wymagania dotyczące reprezentacji i wyrównania jak siebie nawzajem. [...]

Po którym następuje C99 TC3 Footnote 39 / C11 Footnote 48:

Te same wymagania dotyczące reprezentacji i wyrównania mają oznaczać wymienność jako argumenty do funkcji, zwracanie wartości z funkcji i członków związków.

Chociaż standard nie mówi: "wskaźnik do typu struktury" i wybrano następujące słowa: "All pointers to structure types", nie określa ono jednoznacznie, czy ma zastosowanie do rekurencyjnego wyprowadzania takich wskaźników. W innych przypadkach, gdy specjalne właściwości wskaźników są wymienione w standardzie, nie określa on jawnie ani nie wspomina o rekurencyjnych derywacjach wskaźników, co oznacza, że albo stosuje się "derywację typu", albo nie - ale nie jest ona wyraźnie wymieniona.

I chociaż frazowanie "wszystkie wskazówki do" przy odwołaniu to types jest używane tylko dwa razy , (dla typów structure i union), w przeciwieństwie do bardziej wyraźnego frazowania: "wskaźnik do", który jest używany w całym standardzie, nie możemy stwierdzić, czy ma zastosowanie do rekurencyjnego wyprowadzenia takich wskaźników.

Author: lhunath, 2014-06-19

2 answers

Tło

Założenie, że standard domyślnie wymaga, aby wszystkie wskaźniki do typów struktur (kompletne, niekompletne, kompatybilne i Niekompatybilne) miały te same wymagania dotyczące reprezentacji i wyrównania, rozpoczęło się w C89 - wiele lat przed tym, jak norma wymagała tego wyraźnie. Uzasadnieniem tego była zgodność niekompletnych typów w oddzielnych jednostkach tłumaczeniowych i chociaż według Komitetu Normalizacyjnego C, pierwotna intencja polegała na umożliwieniu zgodność typu niekompletnego z jego kompletną odmianą, rzeczywiste słowa normy nie opisywały go. Zostało to zmienione w drugim sprostowaniu technicznym do C89, w związku z czym pierwotne założenie stało się konkretyzowane.


Typy kompatybilności i niekompletności

Podczas czytania wytycznych dotyczących kompatybilności i niekompletnych typów, dzięki Mattowi Mcnabbowi, znajdujemy dalszy wgląd w pierwotne założenie C89.

Wyprowadzenie wskaźnika typy obiektowe i niekompletne

C99 / C11 §6.2.5 p1:

Typy są podzielone na typy obiektów, typy funkcji i typy niekompletne.

C99 / C11 §6.2.5 p20:

Typ wskaźnika może być pochodną typu funkcji, typu obiektu lub typu niekompletnego, zwanego typem odniesienia.

C99 / C11 §6.2.5 p22:

Struktura lub typ Unii o nieznanej treści jest typem niekompletnym. Jest zakończona, dla wszystkie deklaracje tego typu, deklarując tę samą strukturę lub tag union z jego definiującą zawartością później w tym samym zakresie.

Co oznacza, że wskaźniki mogą pochodzić zarówno od typów obiektów, jak i typów niekompletnych. Chociaż nie jest sprecyzowane, że niekompletne typy nie są wymagane do uzupełnienia; w przeszłości Komitet odpowiadał w tej sprawie i stwierdził, że brak zakazu jest wystarczający i nie ma potrzeby pozytywnego stwierdzenia.

Następujące pointer to pointer to complete 'struct never_completed', is never completed:

int main(void)
{
    struct never_completed *p;
    p = malloc(1024);
}

[Pełna próbka kodu ]

Kompatybilne typy oddzielnych jednostek translacyjnych

C99 / C11 §6.7.2.3 p4:

Wszystkie deklaracje typów strukturalnych, unijnych lub wyliczonych, które mają ten sam zakres i używają tego samego tagu, deklarują ten sam typ.

C99 / C11 §6.2.7 p1:

Dwa typy mają typ zgodny, jeśli ich typy są to samo. Dwa typy struktur zadeklarowane w oddzielnych jednostkach tłumaczeniowych są kompatybilne, jeśli ich znaczniki (są) tym samym znacznikiem. [przycinany cytat] [...]

Ten akapit ma ogromne znaczenie, pozwolę sobie go podsumować: dwa typy struktur zadeklarowane w oddzielnych jednostkach tłumaczeniowych są kompatybilne, jeśli używają tego samego znacznika. Jeśli oba są wypełnione-ich członkowie muszą być tacy sami (zgodnie z określonymi wytycznymi).

Zgodność wskaźników

C99 §6.7.5.1 p2 / C11 §6.7.6.1 p2:

Aby dwa typy wskaźników były kompatybilne, oba muszą być identycznie kwalifikowane i oba muszą być wskaźnikami do kompatybilnych typów.

Jeśli norma nakazuje, aby dwie struktury w określonych warunkach były kompatybilne w oddzielnych jednostkach translacyjnych, niezależnie od tego, czy są niekompletne czy kompletne, oznacza to, że wskaźniki pochodzące z tych struktur są również kompatybilne.

C99 / C11 §6.2.5 p20:

Dowolna liczba typy pochodne mogą być konstruowane z typów obiektowych, funkcyjnych i niekompletnych [57]}

Te metody konstruowania typów pochodnych mogą być stosowane rekurencyjnie.

I ze względu na fakt, że wyprowadzenie wskaźników jest rekurencyjne, sprawia, że wskaźniki pochodzące od wskaźników do zgodnych typów struktur są ze sobą kompatybilne.

Reprezentacja kompatybilnych typów

C99 §6.2.5 p27 / C11 §6.2.5 p28:

Wskaźniki do wersji kwalifikowanych lub niewykwalifikowanych kompatybilne typy mają takie same wymagania dotyczące reprezentacji i dostosowania.

C99 / C11 §6.3 p2:

Konwersja wartości operandu na zgodny typ nie powoduje zmiany wartości ani reprezentacji.

C99 / C11 §6.2.5 p26:

Kwalifikowane lub niewykwalifikowane wersje typu są odrębnymi typami, które należą do tej samej kategorii typów i mają tę samą reprezentację i wyrównanie wymagania.

Oznacza to, że zgodna implementacja nie może mieć odrębnego osądu dotyczącego wymagań dotyczących reprezentacji i wyrównania wskaźników pochodzących z niekompletnych lub kompletnych typów struktur, ze względu na możliwość, że oddzielna jednostka tłumaczeniowa może mieć zgodny typ, który będzie musiał mieć te same wymagania dotyczące reprezentacji i wyrównania, i wymagane jest zastosowanie tego samego odrębnego osądu z niekompletnym lub kompletnym typem struktury. odmiana tego samego typu konstrukcji.

Następujący wskaźnik do wskaźnika do niekompletny 'struct complete_incomplete':

Struct complete_incomplete * * p;

Jest kompatybilny i ma te same wymagania dotyczące reprezentacji i wyrównania, co następujący wskaźnik do wskaźnika do complete 'struct complete_incomplete':

Struct complete_incomplete { int i;} * * p;


C89 related

Jeśli zastanawiamy się nad przesłanką dotyczącą C89, raport defektu #059 Z Jun 93 ' zapytany:

Obie sekcje nie wymagają wprost, aby niekompletny Typ ostatecznie musiał zostać uzupełniony, ani nie pozwalają jednoznacznie, aby niekompletne typy pozostały niekompletne dla całej jednostki kompilacji. Ponieważ funkcja ta ma znaczenie dla deklaracji prawdziwych nieprzezroczystych typów danych, zasługuje na Wyjaśnienie.

Rozważając wzajemne struktury odniesienia zdefiniowany i zaimplementowany w różnych jednostkach kompilacji sprawia, że idea nieprzezroczystego typu danych jest naturalnym rozszerzeniem niekompletnego typu danych.

Odpowiedź Komitetu brzmiała:

Nieprzezroczyste typy danych zostały rozważone i zatwierdzone przez Komitet przy opracowywaniu standardu C.


Zgodność a zamienność

Omówiliśmy aspekt dotyczący wymagań dotyczących reprezentacji i wyrównania wskaźnika rekurencyjnego w związku z tym, że nie jest to możliwe, nie jest to możliwe, ponieważ nie jest to możliwe, ponieważ nie jest to możliwe.]}

C99 TC3 §6.2.5 p27 Footnote 39 / C11 §6.2.5 p28 Footnote 48:

Te same wymagania dotyczące reprezentacji i wyrównania mają oznaczać wymienność jako argumenty do funkcji, zwracanie wartości z funkcji i członków związków.

Norma mówi, że notatki, przypisy i przykłady są nienormatywne i są " dla informacji tylko".

C99 FOREWORD p6 / C11 FOREWORD p8:

[...] to Przedmowa, wstęp, uwagi, przypisy i przykłady mają również charakter wyłącznie informacyjny.

Szkoda, że ten mylący przypis nigdy nie został zmieniony, ponieważ w najlepszym razie - przypis dotyczy konkretnie typów bezpośrednich, które się do niego odnoszą, więc sformułowanie przypisu tak,jakby-jeśli właściwości "wymagań dotyczących reprezentacji i wyrównania" są bez kontekstu tych konkretnych typów, ułatwia interpretować jako ogólną zasadę dla wszystkich typów, które mają wspólną reprezentację i wyrównanie. Jeśli przypis ma być interpretowany bez kontekstu określonych typów, to oczywiste jest, że tekst normatywny normy go nie implikuje, nawet bez konieczności debatowania nad interpretacją terminu "wymienny".

Zgodność wskaźników z typami struktur

C99 / C11 §6.7.2.3 p4:

Wszystkie deklaracje struktury, typów unijnych lub wyliczonych które mają ten sam zakres i używają tego samego znacznika deklarują ten sam typ.

C99 / C11 §6.2.7 p1:

Dwa typy mają typ zgodny, jeśli ich typy są takie same.

C99 §6.7.5.1 p2 / C11 §6.7.6.1 p2:

Aby dwa typy wskaźników były kompatybilne, oba muszą być identycznie kwalifikowane i oba muszą być wskaźnikami do kompatybilnych typów.

To stwierdza oczywisty wniosek, różne typy struktur są rzeczywiście różnymi typami, a ponieważ są różne są niezgodne. Dlatego też dwa wskaźniki do dwóch różnych i niekompatybilnych typów są również niezgodne, niezależnie od ich reprezentacji i wymagań wyrównania.

Skuteczne typy

C99 / C11 §6.5 p7:

Obiekt ma zapisaną wartość dostępną tylko przez wyrażenie lvalue, które ma jeden z następujących typów:

Typ zgodny z efektywnym typem obiektu

C99 / C11 §6.5 p6:

Efektywnym typem obiektu dla dostępu do jego zapisanej wartości jest zadeklarowany typ obiektu, jeśli istnieje.

Wskaźniki niezgodne nie są 'wymienne' jako argumenty do funkcji, ani jako wartości zwracane z funkcji. Dorozumiane konwersje i określone przypadki specjalne są wyjątkami, a te typy nie są częścią żadnego takiego wyjątku. Nawet jeśli zdecydujemy się dodać nierealistyczny wymóg dla wspomnianego "zamienności" i powiemy, że do jej zastosowania wymagana jest jawna konwersja , a następnie dostęp do przechowywanej wartości obiektu z niezgodnym typem efektywnym łamie reguły typów efektywnych. Aby stało się to rzeczywistością, potrzebujemy nowej właściwości, której obecnie standard nie ma. W związku z tym dzielenie tych samych wymagań dotyczących reprezentacji i dostosowania, a także możliwość wymiany danych, po prostu nie wystarcza.

To pozostawia nas z bycia wymiennymi "członkami związków", i chociaż są one rzeczywiście wymienne jako członkowie Związku-nie ma szczególnego znaczenia.

Oficjalne interpretacje

1. pierwsza "oficjalna" interpretacja należy do członka Komitetu Normalizacyjnego C. Jego interpretacja Dla: "są przeznaczone do implikacji wymienności", jest to, że w rzeczywistości nie oznacza to, że taka wymienność istnieje, ale faktycznie sprawia, że {150]}sugestia {151]} dla niego.

Choć chciałbym, aby stało się to rzeczywistością, nie rozważałbym implementacja, która wzięła sugestię z nienormatywnego przypisu, nie mówiąc już o nierozsądnie niejasnym przypisie, a jednocześnie jest sprzeczna z wytycznymi normatywnymi - za zgodną implementację. To oczywiście sprawia, że program, który wykorzystuje i zależy od takiej "sugestii", jest nie-ściśle zgodny.

2. druga "oficjalna" interpretacja należy do członka / współpracownika Komitetu Normalizacyjnego C, przez jego interpretację przypis nie wprowadza sugestia, a ponieważ (normatywny) tekst normy tego nie implikuje - uważa, że jest to wada normy. Zasugerował nawet zmianę skutecznych zasad postępowania w tej sprawie.

3. trzecia "oficjalna" interpretacja pochodzi z raportu defektu #070 Z grudnia 93"` Został on zapytany, w kontekście C89, czy program, który przekazuje Typ "unsigned int", gdzie typ " int " jest oczekiwany, jako argument do funkcji z nie-prototypowy deklarator, wprowadzający nieokreślone zachowanie.

W C89 jest ten sam przypis, z tą samą domniemaną zamiennością jako argumenty do funkcji, dołączony do:

C89 §3.1.2.5 p2:

Zakres wartości nonnegatywnych typu signed integer jest podzbiorem odpowiadającego typu unsigned integer, a reprezentacja tej samej wartości w każdym typie jest taka sama.

Komitet odpowiedział, że zachęcają realizatorów do pozwól, aby ta wymienność działała, ale ponieważ nie jest to wymóg, sprawia, że program nie jest ściśle zgodny.


Poniższa próbka kodu nie jest ściśle zgodna. '&s1' i 'struct generic **' mają te same wymagania dotyczące reprezentacji i wyrównania, ale mimo to są niekompatybilne. Zgodnie z regułami typów efektywnych uzyskujemy dostęp do przechowywanej wartości obiektu "s1" z niezgodnym typem efektywnym, wskaźnikiem do 'struct generic', podczas gdy jego deklarowany typ, a zatem efektywny, jest wskaźnikiem do 'struct s1'. Aby przezwyciężyć to ograniczenie, mogliśmy użyć wskaźników jako członków związku, ale ta konwencja niszczy cel bycia ogólnym.

int allocate_struct(void    *p,
                    size_t  s)
{
    struct generic **p2 = p;
    if ((*p2 = malloc(s)) == NULL)
        return -1;

    return 0;
}

int main(void)
{
    struct s1 { int i; } *s1;

    if (allocate_struct(&s1, sizeof *s1) != 0)
        return EXIT_FAILURE;
}

[Pełna próbka kodu ]


Poniższy przykład kodu jest ściśle zgodny , aby przezwyciężyć zarówno kwestie typów efektywnych, jak i ogólnych, wykorzystujemy: 1. wskaźnik do void, 2. na wymagania dotyczące reprezentacji i wyrównania wszystkich wskaźników do struktur, oraz 3. uzyskiwanie dostępu do reprezentacji bajtów wskaźnika "ogólnikowo", podczas używania memcpy do kopiowania reprezentacji, bez wpływu na jej efektywny Typ.

int allocate_struct(void    *pv,
                    size_t  s)
{
    struct generic *pgs;

    if ((pgs = malloc(s)) == NULL)
        return -1;

    memcpy(pv, &pgs, sizeof pgs);
    return 0;
}

int main(void)
{
    struct s1 { int i; } *s1;

    if (allocate_struct(&s1, sizeof *s1) != 0)
        return EXIT_FAILURE;
}

[Pełna próbka kodu ]


Wniosek

Wniosek jest taki, że zgodna implementacja musi mieć te same wymagania dotyczące reprezentacji i wyrównania, odpowiednio, dla wszystkich rekurencyjnie wyprowadzonych wskaźników do typy struktur, niezależnie od tego, czy są niekompletne czy kompletne, czy są kompatybilne czy niekompatybilne. Chociaż istotne jest to, czy typy są kompatybilne czy niezgodne, ale ze względu na samą możliwość kompatybilnego typu, muszą one dzielić podstawowe właściwości reprezentacji i wyrównania. Byłoby lepiej, gdybyśmy mogli uzyskać dostęp do wskaźników, które bezpośrednio współdzielą reprezentację i wyrównanie, ale niestety obecne reguły efektywnych typów tego nie wymagają.

 28
Author: Dror K.,
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
2015-12-26 20:16:49

Moja odpowiedź brzmi " nie."

W żadnym standardzie C nie ma sformułowań, które sugerowałyby inaczej. Fakt, że wszystkie wskaźniki do typów struktur mają tę samą reprezentację i wymagania dotyczące wyrównania nie ma wpływu na żaden typ Pochodny.

To ma sens i każda inna rzeczywistość wydaje się być niespójna. Rozważmy alternatywę:

Nazwijmy wymagania wyrównania i reprezentacji dla wskaźników do typów struktur "A". Załóżmy, że każdy "typ rekurencyjnie Pochodny" spełnia wymagania "A".

Nazwijmy wymagania wyrównania i reprezentacji dla wskaźników do typów Unii "B". Załóżmy, że każdy " typ rekurencyjnie Pochodny "spełnia wymagania "B".

Załóżmy, że "A" i " B " nie są tym samym [1]. Ponadto, Załóżmy, że nie mogą być spełnione w tym samym czasie. (Na przykład reprezentacja 4-bajtowa i reprezentacja 8-bajtowa.)

Teraz wywołaj typ z both:

  1. typ z wymaganiami " A "
  2. typ z wymaganiami " B "

Teraz masz typ, którego wymagania są niemożliwe do spełnienia, ponieważ musi spełniać "A "i " B", ale nie mogą być spełnione jednocześnie.

Być może myślisz o rodzajach pochodnych jako o płaskiej linii aż do jednego przodka, ale tak nie jest. Gatunki pochodne mogą mieć wielu przodków. Standardowa definicja "typów pochodnych" omawia to.

[1] choć może to wydawać się nierozsądne, nieprawdopodobne i głupie, jest dozwolone.

 1
Author: synthetel,
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
2015-12-24 06:15:29