So che le funzioni __stdcall non possono avere ellissi, ma voglio essere sicuro che non ci siano piattaforme che supportano le funzioni stdarg.h per chiamare convenzioni diverse da __cdecl o __stdcall.In C++, le funzioni variadiche (quelle con ... alla fine dell'elenco dei parametri) seguono necessariamente la convenzione di chiamata __cdecl?
risposta
La convenzione di chiamata deve essere quella in cui il chiamante cancella gli argomenti dallo stack (perché il destinatario non sa cosa verrà passato).
Ciò non corrisponde necessariamente a ciò che Microsoft chiama "__cdecl". Per esempio, su uno SPARC, normalmente passerà gli argomenti nei registri, perché è così che SPARC è progettato per funzionare: i suoi registri agiscono fondamentalmente come uno stack di chiamate che viene riversato nella memoria principale se le chiamate diventano abbastanza profonde non entreranno più nel registro.
Anche se ne sono meno certo, mi aspetterei all'incirca lo stesso su IA64 (Itanium) - ha anche un enorme set di registri (un paio di centinaia se la memoria serve). Se non sbaglio, è un po 'più permissivo su come usi i registri, ma mi aspetterei che venga usato in modo simile almeno per la maggior parte del tempo.
Perché questo ti interessa? Il punto di utilizzo di stdarg.h e delle sue macro è quello di nascondere le differenze nella convenzione di chiamata dal codice, in modo che possa lavorare con argomenti variabili in modo portabile.
Modifica, in base ai commenti: Ok, ora capisco cosa stai facendo (almeno quanto basta per migliorare la risposta). Dato che hai già (apparentemente) un codice per gestire le variazioni nell'ABI predefinito, le cose sono più semplici. Ciò lascia solo la questione se le funzioni variadic usino sempre "l'ABI di default", qualunque cosa accada per la piattaforma in questione. Con "stdcall" e "default" come le uniche opzioni, penso che la risposta sia sì. Solo per esempio, su Windows, wsprintf
e wprintf
interrompi la regola empirica e usa la convenzione di chiamata di cdecl invece di stdcall.
Intende "piattaforme supportate da MSVC" o come regola generale? Anche se ci si limita alle piattaforme supportate da MSVC, esistono ancora situazioni come IA64 e AMD64 in cui esiste solo una convenzione di chiamata "una" e quella convenzione di chiamata si chiama __stdcall
, ma non è certo la stessa __stdcall
si ottiene su x86
AFAIK, la diversità delle convenzioni di chiamata è unica per DOS/Windows su x86. La maggior parte delle altre piattaforme aveva compilatori dotati di sistema operativo e standardizzazione la convenzione.
il modo più definitivo che è possibile determinare questo è quello di analizzare il Callin g convenzioni. Per le funzioni variadic al lavoro, la vostra convenzione di chiamata ha bisogno di un paio di attributi:
- Il chiamato deve essere in grado di accedere ai parametri che non fanno parte della lista di argomenti variabile da un offset fisso dalla cima della pila . Ciò richiede che il compilatore spingere i parametri nello stack da destra a sinistra. (Questo include cose come il primo parametro
printf
, specifica di formato. Inoltre, l'indirizzo della lista di argomenti variabile stessa devono essere derivati da una posizione nota.) - Il chiamante deve essere responsabile della rimozione dei parametri fuori impilare una volta che la funzione è tornato perché solo il compilatore, mentre genera il codice per il chiamante, sa quanti parametri sono stati inserito nello stack in primo luogo. La stessa funzione variadica non ha questa informazione.
stdcall
non funziona perché il callee è responsabile dello scoppiare i parametri fuori dallo stack. Nei vecchi giorni Windows a 16 bit, pascal
non funzionava perché spingeva i parametri nello stack da sinistra a destra.
Naturalmente, come accennato alle altre risposte, molte piattaforme non offrono alcuna scelta in termini di convenzione di chiamata, rendendo questa domanda irrilevante per quelle.
Si consideri la seguente funzione su un sistema x86:
vuoto qualcosa __stdcall (char *, ...);
La funzione si dichiara come __stdcall, che è una convenzione callee-clean. Ma una funzione variadica non può essere cancellata dal callee dato che il chiamato non sa quanti parametri sono stati passati, quindi non sa quanti ne dovrebbe pulire.
Il compilatore Microsoft Visual Studio C/C++ risolve questo conflitto convertendo silenziosamente la convenzione di chiamata a __cdecl, che è l'unica convenzione di chiamata variadic supportata per le funzioni che non accettano un parametro nascosto.
Perché questa conversione avviene in modo silenzioso anziché generare un avviso o un errore?
La mia ipotesi è che è rendere meno fastidiose le opzioni del compilatore/Gr (imposta la convenzione di chiamata predefinita su __fastcall) e/Gz (imposta la convenzione di chiamata predefinita su __stdcall).
La conversione automatica delle funzioni variadiche in __cdecl significa che è possibile aggiungere l'opzione della riga di comando/Gr o/Gz alle opzioni del compilatore e tutto verrà comunque compilato ed eseguito (solo con la nuova convenzione di chiamata).
Un altro modo di vedere questo non è da pensare al compilatore come conversione __stdcall variadic a __cdecl ma semplicemente dicendo “per le funzioni variadic, __stdcall è chiamante-pulito.”
- 1. Convenzione di chiamata X86_64 personalizzata alla chiamata di funzione C
- 2. Haskell ha funzioni/tuple variadiche?
- 3. Delphi thiscall convenzione di chiamata
- 4. come specificare la convenzione di chiamata lambda vc11
- 5. Qual è la convenzione di chiamata predefinita di una funzione lambda C++?
- 6. Mettere la virgola (,) alla fine dell'array. È una convenzione?
- 7. C++ Stile Convenzione: Nomi dei parametri all'interno Dichiarazione Classe
- 8. funzioni di chiamata sopra la loro dichiarazione
- 9. Come funziona la funzione std :: function sulla convenzione di chiamata?
- 10. Chiamata convenzione su x64
- 11. Chiamata alla stored procedure con parametri
- 12. Esiste una convenzione per le dichiarazioni dei puntatori in C?
- 13. Puntatore di chiamata C++ alla funzione membro
- 14. Rimozione dei numeri alla fine di una stringa C#
- 15. Capire il prologo funzione di chiamata C con __cdecl sulle finestre
- 16. La funzione di chiamata conosce solo i tipi di parametri in fase di esecuzione in C?
- 17. Metodo di chiamata base all'inizio o alla fine del metodo?
- 18. Come trovare la convenzione di chiamata di una dll di terze parti?
- 19. C++ distruttore e chiamata di funzione fine
- 20. Convenzione di nomi per l'eliminazione dei dati in C++
- 21. Convenzione angolare per la dichiarazione dei controllori
- 22. Risoluzione dei parametri in C#
- 23. c/C++ ottimizzazione per variabile costante nelle funzioni di chiamata
- 24. x64 convenzione di chiamata (stack) e varargs
- 25. Come funzionano le parentesi graffe che seguono l'istanziazione dei tratti?
- 26. Esiste una convenzione per denominare le "funzioni private" in bash?
- 27. Controlli dei parametri ripetuti nelle funzioni
- 28. Scorrere fino alla fine di C# DataGridView
- 29. C++ - Spazio dei nomi vs. funzioni statiche
- 30. Esegui diverse funzioni in base alla disequazione dei parametri del modello
Il tuo commento su SPARC si applica a molte se non a tutte le architetture RISC. Dal momento che i registri sono così numerosi, il compilatore usa solo quelli per passare argomenti. Per non parlare, sono più veloci della memoria da un bel po ', esp. dato che probabilmente stai trasmettendo dati che sono già nei registri (anche se penso che sarà necessario inserirli nello stack per ripristinarli se ne avrà bisogno in seguito, ma ...). Ad ogni modo, so che questo è il modo in cui funziona su MIPS e sono abbastanza certo che PPC funzioni allo stesso modo. –
* "Perché questo ti interessa?" * Ben sta lavorando su js-ctypes per Mozilla. Puoi vedere il contesto esatto in https://bugzilla.mozilla.org/show_bug.cgi?id=554790 (cerca "idea pazza") e puoi alcuni documenti che spiegano js-ctypes su https: //developer.mozilla. org/it/JavaScript_code_modules/ctypes.jsm –
Inutile dire che js-ctypes è disponibile solo per codice applicativo e componenti aggiuntivi, non per pagine web. –