2016-07-25 71 views
6

Chciałbym wiedzieć, czy następujący kod C przylega do C99 i/lub normy C11 (ów):Iterując argumentów funkcji, używając wskaźnika do pierwszego

void foo(int bar0, int bar1, int bar2) { 
    int *bars = &bar0; 

    printf("0: %d\n1: %d\n2: %d\n", bars[0], bars[1], bars[2]); 
} 

int main(int argc, char **argv) { 
    foo(8, 32, 4); 

    return 0; 
} 

Ten kod kompiluje snippet i działa prawidłowo przy użyciu wizualnego studio 2013 i drukuje:

0: 8
1: 32
2: 4

+1

Czy prosisz o zaspokojenie swojej ciekawości, czy masz problem, który uważasz, że to rozwiąże? – StoryTeller

+0

Po prostu z ciekawości, ponieważ argumenty variadyczne zdają się używać tej techniki do iteracji po jej argumentach. –

Odpowiedz

11

Nie, nie wszędzie blisko.

Standard C nie gwarantuje, że argumenty funkcji są przechowywane w następujących po sobie lokalizacjach pamięci (lub w konkretnym zamówieniu, w tym przypadku). Od kompilatora i/lub platformy (architektura) zależy sposób przekazywania argumentów funkcji do funkcji.

Aby dodać więcej klarowności, nie ma nawet żadnej gwarancji, że argumenty, które mają zostać przekazane, są w ogóle przechowywane w pamięci (na przykład stosie). Mogą również korzystać z rejestrów sprzętowych (, o ile są one odpowiednie,), dla niektórych lub wszystkich parametrów, aby operacje były szybkie. Na przykład,

  • PowerPC

    architektury PowerPC posiada dużą liczbę rejestrów więc większość funkcji można przejść wszystkie argumenty rejestrów połączeń jednym poziomie. [...]

  • MIPS

    Najpowszechniej używane wywołanie Konwencja 32 bitów MIPS jest ø32 ABI, który przechodzi przez pierwsze cztery argumentów funkcji w rejestrach $a0 - $a3; kolejne argumenty są przekazywane na stosie. [...]

  • X86

    Architektura x86 jest stosowany z wieloma różnymi konwencjami telefonicznych. Ze względu na niewielką liczbę rejestrów architektonicznych konwencje wywoływania x86 przeważnie przekazują argumenty na stosie, podczas gdy wartość zwracana (lub wskaźnik do niej) jest przekazywana do rejestru.

i tak dalej. Sprawdź full wiki article here.

Więc w twoim przypadku, bars[0] jest ważny dostęp, ale czy bars[1] i bars[2] są ważne, zależy od bazowego środowiska (platforma/kompilatora), całkowicie. Najlepiej nie polegać na zachowaniu, którego się spodziewasz.

Powiedział, żeby przyczepić, w przypadku, gdy nie zamierzamy używać argumentów (jeśli) przeszedł do main(), można po prostu zmniejszyć podpis int main(void) {.

+0

+1, ale być może należy dodać, że na nowoczesnych architekturach pierwsze parametry funkcji nie są w ogóle zapamiętywane w pamięci, ale przechodzą przez rejestry sprzętowe. I być może także, że to nie kompilator decyduje, ale platforma API. –

+0

@JensGustedt Sir, dodałem trochę więcej informacji na temat tego frontu. Czy teraz jest coś lepszego? –

5

Żaden standard nie obsługuje tego. To bardzo niegrzeczne.

Indeksowanie tablic i arytmetyczna wskaźnik odnoszą się tylko do tablic. (Uwaga mały wyjątek. Można czytać wskaźnik jedna obok tablicy lub skalara, ale nie można szacunek it)

7

Nie jest niezgodny z opublikowanym standardem. Jak przechowywane są argumenty i zmienne lokalne oraz gdzie, do kompilatora. To, co może działać w jednym kompilatorze, może nie działać w innym, a nawet w innej wersji tego samego kompilatora.

Specyfikacja C nie wspomina nawet o stosie, a wszystkie określają reguły dotyczące zakresu.

+0

Funkcje Variadic również wydają się być częścią standardu - va_start, va_end są częścią standardowej biblioteki i właśnie to robią te makra - przechodzenie przez parametry stosu. – MichaelMoser

+3

@MichaelMoser Tak, ale sposób implementacji tych makr nie jest zgodny ze standardem. A posiadanie stosu nie jest wymogiem dla kompilatora C (zwłaszcza, że ​​nie ma go w specyfikacji C), jest to po prostu wygodny szczegół implementacji. –