16

Chciałem zobaczyć, czy mogę zainicjować zmienną globalną zwrócić na siebie:Inicjalizacja danych kołowych w C. Czy ten prawidłowy kod C jest zgodny z jakimkolwiek standardem?

#include <stdio.h> 
struct foo { struct foo *a, *b; } x = { &x, &x }; 
int main() 
{ 
    printf("&x = %p, x.a = %p, x.b = %p\n", &x, x.a, x.b); 
    return 0; 
} 

Ten kod kompiluje i działa zgodnie z oczekiwaniami gcc (wszystkie trzy wskaźniki druku identycznie).

chcę wiedzieć:

  1. Czy to wiarygodne?
  2. Czy to standard?
  3. Czy to jest przenośne?

EDIT: Właśnie w celu wyjaśnienia, mam wątpliwość dostępności adresu x we własnym inicjatora.

+0

Jest jeden problem: '% p' spodziewa się' void * ', ale argumenty są' struct foo * 's. –

+0

@ DanielFischer nie pozwala C cichym konwersjom zi' void * 'z dowolnego innego wskaźnika, aby umożliwić funkcje, takie jak 'malloc' do pracy bez jawnych konwersji? – Matt

+1

Ma, ale tylko przy zadaniu.Przy przekazywaniu rzeczy do' printf', nie ma takiej konwersji (varargs, bez sprawdzania typu w ogóle). Tak więc 'printf' pobiera 'struct foo *' s, ale ciąg formatu wymaga 'void *'. Twój kompilator powinien Cię o tym ostrzec (jeśli podniesiesz poziom ostrzegawczy). Pedantycznie jest to nawet niezdefiniowane zachowanie, ale będzie działać tak samo, jak na większości popularnych platform (standard nie gwarantuje, że wszystkie wskaźniki mają taką samą wielkość i strukturę, ale zazwyczaj mają). –

Odpowiedz

8

to standardowy kod C

Ten punkt mocną standardu pozwala to (kopalni nacisk)..

(C99, 6.2. 1p7) "Znaczniki struktury, zjednoczenia i wyliczenia mają zasięg t kapelusz zaczyna się zaraz po pojawieniu się znacznika w specyfikatorze typu, który deklaruje tag. Każda stała wyliczeniowa ma zasięg, który zaczyna się zaraz po pojawieniu się jej modułu wyliczającego w liście wyliczeniowej. Każdy inny identyfikator ma zasięg, który rozpoczyna się tuż po zakończeniu deklaracji. "

Aby uzyskać więcej informacji, należy pamiętać, że w celu zilustrowania ostatnie zdanie 6.2.1p7, książka "The New C Standard" Derek M. Jones wykorzystuje przykład podobny do Ciebie:

struct T {struct T *m;} x = /* declarator complete here. */ {&x}; 
+0

Dziękuję, to tylko odpowiedź, której szukałem! – Matt

+0

@Matt nie ma za co! – ouah

8

Tak dla wszystkich powyższych. Masz kilka wskazówek, które uruchamiasz z tym samym adresem, więc mają ten sam adres i to samo, co adres, na który je zainicjowałeś.

Co może być ciekawsze, x.a jest również gwarantowane wskazywanie samego siebie (tzn. Pierwszy element w strukturze jest gwarantowany na samym początku struktury, czyli wskaźnik do struktury, przekonwertowany na typ pierwszy element, jest zagwarantowane, aby wskazywał, że pierwszy element

+0

Pytałem, czy adres 'x' jest dostępny w klauzuli inicjalizującej. Masz również rację, że 'xa' wskazuje na to samo miejsce co on, ale typ jest inny' xa' to 'struct foo *' i powinien wskazywać na 'struct foo', który nie jest tego samego typu co on . – Matt

+0

@Matt: Tak, adres jest rwartą (zasadniczo stałą), więc z punktu widzenia programu, nie różni się to zbytnio od "int x = 1234;" –

Powiązane problemy