2015-07-07 10 views
13

jestem posiada funkcję, która z grubsza wygląda tak:Czy można całkowicie ignorować argumenty variadyczne dla funkcji C?

typedef struct SomeType { 
    ... 
} SomeType; 

void TakesArgs(SomeType *t1, ...) { 
    // iterates through arguments 
} 

// usage: 
TakesArgs(&a, &b, &c); 

Czy muszę uruchamiać żadnych dziwnych ryzyka z pamięci (lub w inny sposób), gdybym zmienić TakesArgs do no-op, a jednocześnie pozostawiając wszystko nazywając kod niezmieniony?

void TakesArgs(SomeType *t1, ...) { 
    return; 
} 

// usage unchanged: 
TakesArgs(&a, &b, &c); 

Innymi słowy, omijając va_list/va_start taniec, który został wykonany w oryginalnej implementacji ma żadnych dziwnych efektów ubocznych?

+0

Funkcje varariadic nie są częścią zarządzania pamięcią. – Olaf

+1

To jest całkowicie bezpieczne. Nie czuję się dobrze, zamieniając to w odpowiedź, ponieważ nie mogę rygorystycznie go poprzeć, ale jest to zdecydowanie bezpieczne. – templatetypedef

+0

Nie sądzę, że będą dziwne efekty uboczne, ponieważ kompilator wygeneruje najwięcej kodu, który zarezerwuje trochę miejsca na stosie dla & a, & b, i & c, a następnie wyskakuje po nim, tak jak normalne wywołanie funkcji (niezależnie liczby przekazanych argumentów). Jednak nie jestem do końca tego pewien. – ra1nmaster

Odpowiedz

8

Tak, to jest całkowicie bezpieczne. Nie musisz czytać wszystkich argumentów variadycznych tylko po to, żeby je przeczytać. Nie musisz nawet uruchamiać sekwencji odczytywania argumentów variadic wewnątrz TakesArgs.

Pod maską zwykle oznacza to, że ciężar wykonania wszelkich zadań związanych z utrzymaniem argumentów jest nadawany kodowi wywołującemu (tak, jak działa w tym, co zwykle określa się jako "tradycyjną" konwencję wywołania C). Pośrednik nie musi nic robić.

+2

@Olaf: Jak to czytasz? –

+2

@Olaf: Jakie konkretnie sformułowanie? – AnT

+0

ISO/IEC 9899: 2011 §7.16.1.3 * 'va_end' * ¶2 mówi: _ Makro' va_end' ułatwia normalny powrót z funkcji, do której zmienna listy argumentów była przywoływana przez rozwinięcie makra 'va_start', lub funkcja zawierająca rozszerzenie makra 'va_copy', która zainicjowała' va_list ap'. Makro 'va_end' z może modyfikować' ap', dzięki czemu nie będzie już można go używać (bez reinicjowania przez makro 'va_start' lub' va_copy'). Jeśli nie ma odpowiedniej inwokacji makra 'va_start' lub' va_copy' lub jeśli makro 'va_end' nie zostanie wywołane przed zwróceniem , zachowanie jest niezdefiniowane. –

1

[edytowane w odpowiedzi na komentarze.]

Istnieją systemy, gdzie domyślnie jest to zadanie wywoływany do pop argumenty. To oczywiście wpędziłoby cię w kłopoty. Ale, że to właśnie z tego powodu, że funkcje zmiennej liczbie argumentów muszą być prawidłowo zadeklarowane i deklaracja musi być prawidłowo widoczne rozmówców. Powodem jest to, że w tych systemach funkcje variadic muszą używać niedomyślnej sekwencji wywołującej, w której wywołujący wyskakuje z argumentów, ponieważ tylko osoba wywołująca może być pewna, ile istnieje. Powinieneś więc być bezpieczny, o ile twoje funkcje są właściwie zadeklarowane.

+0

w '__stdcall', zadaniem callee jest oczyszczenie stosu. To nie jest "z powrotem w dzień", ponieważ '__stdcall' jest nadal szeroko stosowany. W 'cdecl' wywołujący jest odpowiedzialny za czyszczenie stosu. – abelenky

+0

To nie było niewyrobione, ale np. standard dla języków pascalowych. IT był naglący z dobrego powodu. Wadą była osoba, która najpierw musiała podać adres zwrotny. O ile procesor nie ma specjalnej instrukcji return i dealocate, np. jako rodzina 680xx. Zginęło, gdy rosły architektury RISC. – Olaf

+0

Dzięki, obie. Teraz pamiętam. Zaktualizowałem swoją odpowiedź. –

2

Uważam, że jest to bezpieczne, chociaż standard nie jest jednoznaczny pod tym względem.

N1570 7,16 pkt 3 mówi:

Jeśli pożądany jest dostęp do różnych argumentów, wywoływana funkcja deklarują obiekt (ogólnie określane jako ap w tym podrozdziale) o typ va_list.

Uwaga początek tego zdania: „Jeśli jest pożądane dostęp do różnych argumentów”. To oznacza, że ​​jeśli dostęp do różnych argumentów nie jest pożądane, nie ma potrzeby, aby zadeklarować va_list obiekt - co uniemożliwiłoby powołać va_start, va_arg lub va_end.

Możliwe kontrargument jest, że opis va_end (N1570 7.16.1.3) mówi:

Jeśli nie ma odpowiedniego wzywanie va_start lub va_copy makro, lub jeśli va_end makro nie jest wywoływana przed powrotem, zachowanie jest unde zdefiniowane.

Jednakże, biorąc pod uwagę kontekst, wierzę, że nie stosuje się, jeżeli va_end wywoływana jest po va_start jest wywoływana.

+0

re 7.16.1.3, Wierzę, że oznacza to "jeśli nazwiesz' va_end' * bez * wcześniejszego wywołania 'va_start'. – zwol

Powiązane problemy