Perché gli indirizzi di argc e argv sono separati da 12 byte?


40

Ho eseguito il seguente programma sul mio computer (Intel a 64 bit con Linux).

#include <stdio.h>

void test(int argc, char **argv) {
    printf("[test] Argc Pointer: %p\n", &argc);
    printf("[test] Argv Pointer: %p\n", &argv);
}

int main(int argc, char **argv) {
    printf("Argc Pointer: %p\n", &argc);
    printf("Argv Pointer: %p\n", &argv);
    printf("Size of &argc: %lu\n", sizeof (&argc));
    printf("Size of &argv: %lu\n", sizeof (&argv));
    test(argc, argv);
    return 0;
}

L'output del programma era

$ gcc size.c -o size
$ ./size
Argc Pointer: 0x7fffd7000e4c
Argv Pointer: 0x7fffd7000e40
Size of &argc: 8
Size of &argv: 8
[test] Argc Pointer: 0x7fffd7000e2c
[test] Argv Pointer: 0x7fffd7000e20

La dimensione del puntatore &argvè di 8 byte. Mi aspettavo che l'indirizzo argcfosse, address of (argv) + sizeof (argv) = 0x7ffed1a4c9f0 + 0x8 = 0x7ffed1a4c9f8ma tra loro c'è un padding di 4 byte. Perché è così?

La mia ipotesi è che potrebbe essere dovuto all'allineamento della memoria, ma non sono sicuro.

Noto lo stesso comportamento anche con le funzioni che chiamo.


15
Perchè no? Potrebbero essere separati da 174 byte. Una risposta dipenderà dal sistema operativo in uso e / o da una libreria wrapper per la quale è stata impostata main.
aschepler

2
@aschepler: non dovrebbe dipendere da alcun wrapper per il quale è installato main. In C, mainpuò essere chiamato come una funzione regolare, quindi deve ricevere argomenti come una funzione regolare e deve obbedire all'ABI.
Eric Postpischil,

@aschelper: noto lo stesso comportamento anche per altre funzioni.
letmutx,

4
È un interessante "esperimento mentale", ma in realtà non c'è niente che dovrebbe essere più di un "mi chiedo perché". Questi indirizzi possono cambiare a seconda del sistema operativo, del compilatore, della versione del compilatore, dell'architettura del processore e non dovrebbero in alcun modo dipendere dalla "vita reale".
Neil

2
il risultato di sizeof deve essere stampato usando%zu
phuclv il

Risposte:


61

Sul tuo sistema, i primi pochi argomenti interi o puntatori vengono passati nei registri e non hanno indirizzi. Quando si prendono i loro indirizzi con &argco &argv, il compilatore deve fabbricare gli indirizzi scrivendo il contenuto del registro in posizioni di stack e fornendo gli indirizzi di tali posizioni di stack. Nel fare ciò, il compilatore sceglie, in un certo senso, qualunque posizione dello stack sia conveniente per questo.


6
Si noti che ciò può accadere anche se vengono passati in pila ; il compilatore non ha l'obbligo di utilizzare lo slot dei valori in entrata nello stack come memoria per gli oggetti locali in cui vanno i valori. Potrebbe avere senso farlo se la funzione alla fine sta per chiamare la coda e ha bisogno dei valori correnti di questi oggetti per produrre gli argomenti in uscita per la chiamata della coda.
R .. GitHub smette di aiutare ICE l'

10

Perché gli indirizzi di argc e argv sono separati da 12 byte?

Dal punto di vista dello standard linguistico, la risposta è "nessuna ragione particolare". C non specifica né implica alcuna relazione tra gli indirizzi dei parametri della funzione. @EricPostpischil descrive cosa sta probabilmente accadendo nella tua particolare implementazione, ma quei dettagli sarebbero diversi per un'implementazione in cui tutti gli argomenti vengono passati nello stack e questa non è l'unica alternativa.

Inoltre, ho difficoltà a trovare un modo in cui tali informazioni possano essere utili all'interno di un programma. Ad esempio, anche se "sai" che l'indirizzo di argvè di 12 byte prima dell'indirizzo di argc, non esiste ancora un modo definito per calcolare uno di quei puntatori dall'altro.


7
@ R..GitHubSTOPHELPINGICE: il calcolo dell'uno dall'altro è parzialmente definito, non ben definito. Lo standard C non è rigoroso su come uintptr_tviene eseguita la conversione e certamente non definisce le relazioni tra gli indirizzi dei parametri o dove vengono passati gli argomenti.
Eric Postpischil,

6
@ R..GitHubSTOPHELPINGICE: il fatto che è possibile effettuare il round trip significa che g (f (x)) = x, dove x è un puntatore, f è convert-pointer-to-uintptr_t e g è convert-uintptr_t-to -pointer. Matematicamente e logicamente, non implica che g (f (x) +4) = x + 4. Ad esempio, se f (x) erano x² e g (y) erano sqrt (y), quindi g (f (x)) = x (per x non negativo reale), ma g (f (x) +4) ≠ x + 4, in generale. Nel caso di puntatori, la conversione in uintptr_tpotrebbe fornire un indirizzo nei 24 bit alti e alcuni bit di autenticazione negli 8 bit bassi. Quindi aggiungendo 4 basta rovinare l'autenticazione; non aggiorna ...
Eric Postpischil,

5
... i bit dell'indirizzo. Oppure la conversione in uintptr_t potrebbe fornire un indirizzo di base nei 16 bit alti e uno scostamento nei 16 bit bassi e l'aggiunta di 4 ai bit bassi potrebbe portare nei bit alti, ma il ridimensionamento è errato (perché l'indirizzo rappresentato non è base • 65536 + offset ma piuttosto è base • 64 + offset, come in alcuni sistemi). Molto semplicemente, il risultato uintptr_tottenuto da una conversione non è necessariamente un indirizzo semplice.
Eric Postpischil,

4
@ R..GitHubSTOPHELPINGICE dalla mia lettura dello standard, c'è solo una debole garanzia che (void *)(uintptr_t)(void *)psarà paragonabile a (void *)p. E vale la pena notare che il comitato ha commentato quasi questo problema esatto, concludendo che "le implementazioni ... possono anche trattare i puntatori basati su origini diverse come distinti anche se sono bitally identici ".
Ryan Avella,

5
@ R..GitHubSTOPHELPINGICE: Mi dispiace, mi sei perso il fatto che stavi aggiungendo un valore calcolato come la differenza di due uintptr_tconversioni di indirizzi anziché una diversa di puntatori o una distanza "nota" in byte. Certo, è vero, ma come è utile? Rimane vero che "non esiste ancora un modo definito per calcolare uno di quei puntatori dall'altro" come afferma la risposta, ma che il calcolo non calcola bda ama piuttosto calcola bda entrambi ae b, poiché bdeve essere usato nella sottrazione per calcolare l'importo aggiungere. Il calcolo l'uno dall'altro non è definito.
Eric Postpischil,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.