RISOLTO Puntatori

Stato
Discussione chiusa ad ulteriori risposte.

Matteo34

Nuovo Utente
104
3
CPU
i5-10500 3.2Ghz
Dissipatore
Non specificato
Scheda Madre
Non specificata
HDD
M.2 251GB e M.2 500GB
RAM
16GB DDR4 2666mhz
GPU
Grafica Intel® UHD 630
Audio
Non specificata
Monitor
1920x1080 27"
PSU
Non specificato
Case
Non specificato
Periferiche
Nono specificato
Net
Eolo
OS
Ubuntu
Ho questa domanda che mi tormenamta da mesi e adesso avró finalmente una risposta, su internet non ho trovato risposta e soprattutto non ho avuto modo di chiedere al mio professore.
La domanda è la seguente:

Visto che un Array viene gestito dal compilatore C come un puntatore ai primi n byte dell'Array allocati in memoria, volevo sapere se questo puntatore viene allocato in memoria come un semplice puntatore e quindi occuppa spazio oppure viene gestito da qualche registro, in giro ho sentito che non alloca spazio in memoria, ma in questi modo non sarebbe possibile tenere traccia degli indirizzi dell'Array, e quindi sarebbe impossibile accedere alla prossima locazione con (indirizzo prima cella + byte)?
Ditemi se sbaglio e vorrei un chiarimento su ciò grazie??
 
Ultima modifica:

angelo0001

Utente Èlite
3,741
594
CPU
AMD Ryzen 7 5700X
Dissipatore
Noctua NH-D15
Scheda Madre
Gigabyte AX370 Gaming K7
HDD
Crucial P5 plus 1TB + 750GB SSD SATA + 14TB HDD
RAM
16GB DDR4 G-Skiill Trident Z RGB 3200Mhz CL16
GPU
Zotac RTX 3060 TI Twin Edge
Audio
Integrata
Monitor
Xiaomi Mi Monitor 2K 1440P@165HZ
PSU
EVGA Supernova 650G2
Case
NZXT H440 Red
Net
EOLO 100
OS
Windows 11
Ho questa domanda che mi tormenamta da mesi e adesso avró finalmente una risposta, su internet non ho trovato risposta e soprattutto non ho avuto modo di chiedere al mio professore.
La domanda è la seguente:

Visto che un Array viene gestito dal compilatore C come un puntatore ai primi n byte dell'Array allocati in memoria, volevo sapere se questo puntatore viene allocato in memoria come un semplice puntatore e quindi occuppa spazio oppure viene gestito da qualche registro, in giro ho sentito che non alloca spazio in memoria, ma in questi modo non sarebbe possibile tenere traccia degli indirizzi dell'Array, e quindi sarebbe impossibile accedere alla prossima locazione con (indirizzo prima cella + byte)?
Ditemi se sbaglio e vorrei un chiarimento su ciò grazie??
Intendi se dichiari un array del tipo int V[10]? In c?
In tal caso V è un puntatore come gli altri però è costante, cioè non puoi alterare il valore di V, che punterà sempre alla testa dell'array.
 

Matteo34

Nuovo Utente
104
3
CPU
i5-10500 3.2Ghz
Dissipatore
Non specificato
Scheda Madre
Non specificata
HDD
M.2 251GB e M.2 500GB
RAM
16GB DDR4 2666mhz
GPU
Grafica Intel® UHD 630
Audio
Non specificata
Monitor
1920x1080 27"
PSU
Non specificato
Case
Non specificato
Periferiche
Nono specificato
Net
Eolo
OS
Ubuntu
Intendi se dichiari un array del tipo int V[10]? In c?
In tal caso V è un puntatore come gli altri però è costante, cioè non puoi alterare il valore di V, che punterà sempre alla testa dell'array.
Ok grazie, vale la stessa cosa per le funzioni, l'indirizzo della prima istruzione della funzione, puntata dalla funzione, viene allocato dentro un puntatore con il nome della funzione e alloca spazio?

Esempio:
Codice:
void foo (void)
{
    printf("Hello");
}

int main(void)
{
    //punatatore a funzione foo
    void (*ptrf)(void) = foo;

    //stampo indirizzo dove è contenuta //prima istruzione funzione foo
    printf("%p", ptrf);

    /*stampo il valore puntato da foo,
   quindi la prima istruzione della     funzione*/
printf("%d", *foo);

return 0;

Quando vado a stampare il contenuto dell'indirizzo puntato da foo(l'istruzione)
mi viene stampato a schermo l'indirizzo in cui è allocata l'istruzione (sotto forma di intero)e quindi il contenuto del punatatore a funzione, perché non mi viene visualizzato il contenuto di foo?
 

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,222
1,853
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
Tu vedi l'indirizzo (virtuale) della funzione, che intendi per "contenuto"?
Se vai a guardare la memoria puntata vedrai i byte che compongono l'istruzione.
 

Matteo34

Nuovo Utente
104
3
CPU
i5-10500 3.2Ghz
Dissipatore
Non specificato
Scheda Madre
Non specificata
HDD
M.2 251GB e M.2 500GB
RAM
16GB DDR4 2666mhz
GPU
Grafica Intel® UHD 630
Audio
Non specificata
Monitor
1920x1080 27"
PSU
Non specificato
Case
Non specificato
Periferiche
Nono specificato
Net
Eolo
OS
Ubuntu
Tu vedi l'indirizzo (virtuale) della funzione, che intendi per "contenuto"?
Se vai a guardare la memoria puntata vedrai i byte che compongono l'istruzione.
Se il nome della funzione punta al contenuto, quando lo provo a stampare con la pirntf mi stampa l'indirizzo del contenuto è quindi quello della prima istruzione funzione
Post unito automaticamente:

Se il nome della funzione punta al contenuto, quando lo provo a stampare con la pirntf mi stampa l'indirizzo del contenuto è quindi quello della prima istruzione funzione
Ho provato
 

pabloski

Utente Èlite
2,868
916
Se il nome della funzione punta al contenuto, quando lo provo a stampare con la pirntf mi stampa l'indirizzo del contenuto è quindi quello della prima istruzione funzione

Un puntatore ad una struttura complessa ( funzione, array, struct o che altro ) è l'indirizzo dove inizia l'area di memoria allocata a quella struttura.

Per una funzione, è l'indirizzo dove si trova la prima istruzione della funzione. Per un array, è l'indirizzo del primo byte dell'array. E tieni presente che non salva nessun'altra informazione.

Riguardo il dove vengono salvati i puntatori, ovviamente in memoria. Non esiste che si possa occupare un registro per tutta la durata del programma. Quel registro non potrebbe essere usato per nient'altro. E di registri, almeno nell'architettura x86, ce ne sono pochi.

I registri sono usati esclusivamente per contare dati che saranno usati a brevissimo. E vengono riutilizzati di continuo per altri dati.
 

Matteo34

Nuovo Utente
104
3
CPU
i5-10500 3.2Ghz
Dissipatore
Non specificato
Scheda Madre
Non specificata
HDD
M.2 251GB e M.2 500GB
RAM
16GB DDR4 2666mhz
GPU
Grafica Intel® UHD 630
Audio
Non specificata
Monitor
1920x1080 27"
PSU
Non specificato
Case
Non specificato
Periferiche
Nono specificato
Net
Eolo
OS
Ubuntu
Un puntatore ad una struttura complessa ( funzione, array, struct o che altro ) è l'indirizzo dove inizia l'area di memoria allocata a quella struttura.

Per una funzione, è l'indirizzo dove si trova la prima istruzione della funzione. Per un array, è l'indirizzo del primo byte dell'array. E tieni presente che non salva nessun'altra informazione.

Riguardo il dove vengono salvati i puntatori, ovviamente in memoria. Non esiste che si possa occupare un registro per tutta la durata del programma. Quel registro non potrebbe essere usato per nient'altro. E di registri, almeno nell'architettura x86, ce ne sono pochi.

I registri sono usati esclusivamente per contare dati che saranno usati a brevissimo. E vengono riutilizzati di continuo per altri dati.
Grazie, un ultima domanda, per quale motivo se stampo il contenuto dell'indirizzo puntato da foo(quindi la prima istruzione della funzione foo), mi stampa l'indirizzo e non l'istruzione?

Esempio:
Codice:
void foo(void)
{
    printf("Hello");
}

int main(void)
{
    printf("%p", foo); // stampo indirizzo

    printf("%d", *foo); //stampa indirizzo ma in intero

    return 0;
}
 

pabloski

Utente Èlite
2,868
916
Grazie, un ultima domanda, per quale motivo se stampo il contenuto dell'indirizzo puntato da foo(quindi la prima istruzione della funzione foo), mi stampa l'indirizzo e non l'istruzione?

Perchè stampa il contenuto della variabile puntatore, che è un indirizzo. Nel puntatore non c'è l'istruzione, ma c'è la locazione di memoria dove andare a pescarla.
 

Matteo34

Nuovo Utente
104
3
CPU
i5-10500 3.2Ghz
Dissipatore
Non specificato
Scheda Madre
Non specificata
HDD
M.2 251GB e M.2 500GB
RAM
16GB DDR4 2666mhz
GPU
Grafica Intel® UHD 630
Audio
Non specificata
Monitor
1920x1080 27"
PSU
Non specificato
Case
Non specificato
Periferiche
Nono specificato
Net
Eolo
OS
Ubuntu

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,222
1,853
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
@Matteo34

Una cosa che puoi fare per accedere al contenuto della funzione (i bytes delle istruzioni) è quella di usare un puntatore a un char, castando a char* la funzione.

C:
#include <stdio.h>
#include <stdint.h>

void print_hello() {
    printf("Hello World");
}

int main() {
    printf("Address of function: %p\n", &print_hello);
    
    char *func = (char*) &print_hello;
    
    int index = 0;
    uint8_t byte;
    
    do {
        byte = (uint8_t) *(func+index); 
        printf("%X ", byte);
        index++;
    } while(byte != 0xC3);
    
    return 0;
}

In output ti restituisce:

Codice:
55 89 E5 83 EC 18 C7 4 24 44 40 40 0 E8 1A 10 0 0 90 C9 C3

0xC3 è l'istruzione RET.
Visto da Olly:

mem_olly.png



In merito alla tua domanda invece dipende. Se viene usata una locazione di memoria si trova sullo stack, e di norma l'accesso avviene usando il registro EBP.

Considera questo codice, giusto per fare un esempio:
C:
#include <stdio.h>
#include <stdint.h>

int sum_array(int *array, int size) {
    int sum = 0;
    
    for(int i=0; i<size; i++)
        sum += array[i];
    
    return sum;
}

int main() {
    int array[10] = {1,2,3,4,5,6,7,8,9,9};
    
    printf("Sum: %d\n", sum_array(array,10));
    
    return 0;
}

Riporto dell'assembly altrimenti sarebbe impossibile spiegare cosa accade ?
Siccome è un array inizializzato staticamente, si trova tutto sullo stack:
Codice:
00401609  MOV DWORD PTR SS:[LOCAL.10],1
00401611  MOV DWORD PTR SS:[LOCAL.9],2
00401619  MOV DWORD PTR SS:[LOCAL.8],3
00401621  MOV DWORD PTR SS:[LOCAL.7],4
00401629  MOV DWORD PTR SS:[LOCAL.6],5
00401631  MOV DWORD PTR SS:[LOCAL.5],6
00401639  MOV DWORD PTR SS:[LOCAL.4],7
00401641  MOV DWORD PTR SS:[LOCAL.3],8
00401649  MOV DWORD PTR SS:[LOCAL.2],9
00401651  MOV DWORD PTR SS:[LOCAL.1],9
00401659  MOV DWORD PTR SS:[LOCAL.15],0A
00401661  LEA EAX,[LOCAL.10]
00401665  MOV DWORD PTR SS:[LOCAL.16],EAX

L'array qui inizia da LOCAL.10 (che è in realtà ESP+0x3C) e termina a LOCAL.1, e viene inizializzato int per int (DWORD). A parte questo, in LOCAL.15 viene memorizzata la dimensione (il secondo parametro della funzione, 10 elementi). LOCAL.15 è ESP+4.

LOCAL.10 è l'elemento all'indice 0, il cui indirizzo viene memorizzato in EAX (LEA EAX,[LOCAL.10]).
Quindi in questo caso viene usato un registro e subito dopo viene assegnato alla locazione LOCAL.16 (ESP). In sostanza è il parametro della funzione (l'array).

In soldoni: vengono memorizzati sullo stack, sono i parametri passati alla funzione.

La funzione che effettua la somma è questa:
Codice:
004015C0  PUSH EBP
004015C1  MOV EBP,ESP
004015C3  SUB ESP,10
004015C6  MOV DWORD PTR SS:[LOCAL.1],0
004015CD  MOV DWORD PTR SS:[LOCAL.2],0
004015D4  JMP SHORT 004015EE
004015D6  /MOV EAX,DWORD PTR SS:[LOCAL.2]
004015D9  |LEA EDX,[EAX*4]
004015E0  |MOV EAX,DWORD PTR SS:[ARG.1]
004015E3  |ADD EAX,EDX
004015E5  |MOV EAX,DWORD PTR DS:[EAX]
004015E7  |ADD DWORD PTR SS:[LOCAL.1],EAX
004015EA  |ADD DWORD PTR SS:[LOCAL.2],1
004015EE  |MOV EAX,DWORD PTR SS:[LOCAL.2]
004015F1  |CMP EAX,DWORD PTR SS:[ARG.2]
004015F4  \JL SHORT 004015D6
004015F6  MOV EAX,DWORD PTR SS:[LOCAL.1]
004015F9  LEAVE
004015FA  RETN

Lasciando perdere i dettagli, l'accesso a ogni singolo elemento avviene qui:

Codice:
004015D6  |> /8B45 F8              /MOV EAX,DWORD PTR SS:[LOCAL.2]
004015D9  |. |8D1485 00000000      |LEA EDX,[EAX*4]
004015E0  |. |8B45 08              |MOV EAX,DWORD PTR SS:[ARG.1]
004015E3  |. |01D0                 |ADD EAX,EDX
004015E5  |. |8B00                 |MOV EAX,DWORD PTR DS:[EAX]
004015E7  |. |0145 FC              |ADD DWORD PTR SS:[LOCAL.1],EAX

LOCAL.2 è l'indice (l'indice i del for), quindi viene calcolato EAX*4 e il risultato è salvato poi in EDX. Non è altro che un calcolo per ottenere l'indice dell'elemento: i=0 -> 0*4, i=1 -> 1*4, i=2 -> 2*4...
ARG.1 è il primo parametro, l'indirizzo dell'array. A questo indirizzo viene sommato il valore di EDX, così da ottenere l'elemento in quella posizione.
Il risultato è l'elemento alla posizione i dell'array; LOCAL.1 è la variabile somma.

L'accesso non avviene mai usando solo ed esclusivamente lo stack (posto che anche l'indirizzo degli elementi sullo stack è mantenuto in un registro), ma inevitabilmente come dice pabloski risiedono in memoria e spostati nei registri in casi specifici.
 
  • Mi piace
Reazioni: Andretti60

Matteo34

Nuovo Utente
104
3
CPU
i5-10500 3.2Ghz
Dissipatore
Non specificato
Scheda Madre
Non specificata
HDD
M.2 251GB e M.2 500GB
RAM
16GB DDR4 2666mhz
GPU
Grafica Intel® UHD 630
Audio
Non specificata
Monitor
1920x1080 27"
PSU
Non specificato
Case
Non specificato
Periferiche
Nono specificato
Net
Eolo
OS
Ubuntu
@Matteo34

Una cosa che puoi fare per accedere al contenuto della funzione (i bytes delle istruzioni) è quella di usare un puntatore a un char, castando a char* la funzione.

C:
#include <stdio.h>
#include <stdint.h>

void print_hello() {
    printf("Hello World");
}

int main() {
    printf("Address of function: %p\n", &print_hello);
  
    char *func = (char*) &print_hello;
  
    int index = 0;
    uint8_t byte;
  
    do {
        byte = (uint8_t) *(func+index);
        printf("%X ", byte);
        index++;
    } while(byte != 0xC3);
  
    return 0;
}

In output ti restituisce:

Codice:
55 89 E5 83 EC 18 C7 4 24 44 40 40 0 E8 1A 10 0 0 90 C9 C3

0xC3 è l'istruzione RET.
Visto da Olly:

Visualizza allegato 396349



In merito alla tua domanda invece dipende. Se viene usata una locazione di memoria si trova sullo stack, e di norma l'accesso avviene usando il registro EBP.

Considera questo codice, giusto per fare un esempio:
C:
#include <stdio.h>
#include <stdint.h>

int sum_array(int *array, int size) {
    int sum = 0;
  
    for(int i=0; i<size; i++)
        sum += array[i];
  
    return sum;
}

int main() {
    int array[10] = {1,2,3,4,5,6,7,8,9,9};
  
    printf("Sum: %d\n", sum_array(array,10));
  
    return 0;
}

Riporto dell'assembly altrimenti sarebbe impossibile spiegare cosa accade ?
Siccome è un array inizializzato staticamente, si trova tutto sullo stack:
Codice:
00401609  MOV DWORD PTR SS:[LOCAL.10],1
00401611  MOV DWORD PTR SS:[LOCAL.9],2
00401619  MOV DWORD PTR SS:[LOCAL.8],3
00401621  MOV DWORD PTR SS:[LOCAL.7],4
00401629  MOV DWORD PTR SS:[LOCAL.6],5
00401631  MOV DWORD PTR SS:[LOCAL.5],6
00401639  MOV DWORD PTR SS:[LOCAL.4],7
00401641  MOV DWORD PTR SS:[LOCAL.3],8
00401649  MOV DWORD PTR SS:[LOCAL.2],9
00401651  MOV DWORD PTR SS:[LOCAL.1],9
00401659  MOV DWORD PTR SS:[LOCAL.15],0A
00401661  LEA EAX,[LOCAL.10]
00401665  MOV DWORD PTR SS:[LOCAL.16],EAX

L'array qui inizia da LOCAL.10 (che è in realtà ESP+0x3C) e termina a LOCAL.1, e viene inizializzato int per int (DWORD). A parte questo, in LOCAL.15 viene memorizzata la dimensione (il secondo parametro della funzione, 10 elementi). LOCAL.15 è ESP+4.

LOCAL.10 è l'elemento all'indice 0, il cui indirizzo viene memorizzato in EAX (LEA EAX,[LOCAL.10]).
Quindi in questo caso viene usato un registro e subito dopo viene assegnato alla locazione LOCAL.16 (ESP). In sostanza è il parametro della funzione (l'array).

In soldoni: vengono memorizzati sullo stack, sono i parametri passati alla funzione.

La funzione che effettua la somma è questa:
Codice:
004015C0  PUSH EBP
004015C1  MOV EBP,ESP
004015C3  SUB ESP,10
004015C6  MOV DWORD PTR SS:[LOCAL.1],0
004015CD  MOV DWORD PTR SS:[LOCAL.2],0
004015D4  JMP SHORT 004015EE
004015D6  /MOV EAX,DWORD PTR SS:[LOCAL.2]
004015D9  |LEA EDX,[EAX*4]
004015E0  |MOV EAX,DWORD PTR SS:[ARG.1]
004015E3  |ADD EAX,EDX
004015E5  |MOV EAX,DWORD PTR DS:[EAX]
004015E7  |ADD DWORD PTR SS:[LOCAL.1],EAX
004015EA  |ADD DWORD PTR SS:[LOCAL.2],1
004015EE  |MOV EAX,DWORD PTR SS:[LOCAL.2]
004015F1  |CMP EAX,DWORD PTR SS:[ARG.2]
004015F4  \JL SHORT 004015D6
004015F6  MOV EAX,DWORD PTR SS:[LOCAL.1]
004015F9  LEAVE
004015FA  RETN

Lasciando perdere i dettagli, l'accesso a ogni singolo elemento avviene qui:

Codice:
004015D6  |> /8B45 F8              /MOV EAX,DWORD PTR SS:[LOCAL.2]
004015D9  |. |8D1485 00000000      |LEA EDX,[EAX*4]
004015E0  |. |8B45 08              |MOV EAX,DWORD PTR SS:[ARG.1]
004015E3  |. |01D0                 |ADD EAX,EDX
004015E5  |. |8B00                 |MOV EAX,DWORD PTR DS:[EAX]
004015E7  |. |0145 FC              |ADD DWORD PTR SS:[LOCAL.1],EAX

LOCAL.2 è l'indice (l'indice i del for), quindi viene calcolato EAX*4 e il risultato è salvato poi in EDX. Non è altro che un calcolo per ottenere l'indice dell'elemento: i=0 -> 0*4, i=1 -> 1*4, i=2 -> 2*4...
ARG.1 è il primo parametro, l'indirizzo dell'array. A questo indirizzo viene sommato il valore di EDX, così da ottenere l'elemento in quella posizione.
Il risultato è l'elemento alla posizione i dell'array; LOCAL.1 è la variabile somma.

L'accesso non avviene mai usando solo ed esclusivamente lo stack (posto che anche l'indirizzo degli elementi sullo stack è mantenuto in un registro), ma inevitabilmente come dice pabloski risiedono in memoria e spostati nei registri in casi specifici.
Cavolo, non so su che architettura sia stato generato quel codice Assembly ma sul mio PC è completamente differente (in effetti è normale).
Comunque tutto chiaro ma non ho ancora capito per quale motivo, non mi stampa il contenuto dell indirizzo puntato dal puntatore, quando lo passo come parametro alla printf()
 

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,222
1,853
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
Io ho compilato con GCC per x86 (MinGW in realtà, sono sotto Windows). Tu con cosa stai compilando?

C:
#include <stdio.h>

void foo(void)
{
    printf("Hello");
}

int main(void)
{
    printf("%p\n", foo); // stampo indirizzo

    printf("%X", *foo); //stampa indirizzo ma in intero

    return 0;
}

Con questo ottieni:
Codice:
004015C0
4015C0

Se invece ti aspetti come contenuto quello che ho mostrato sopra, allora avviene perchè quando compili il codice generato è identico.
Mi spiego meglio:

Codice:
004015E3  |.  C74424 04 C01 MOV DWORD PTR SS:[LOCAL.3],004015C0 
004015EB  |.  C70424 4A4040 MOV DWORD PTR SS:[LOCAL.4],OFFSET 004040 
004015F2  |.  E8 D50F0000   CALL <JMP.&msvcrt.printf>               
004015F7  |.  C74424 04 C01 MOV DWORD PTR SS:[LOCAL.3],004015C0      
004015FF  |.  C70424 4E4040 MOV DWORD PTR SS:[LOCAL.4],OFFSET 004040 
00401606  |.  E8 C10F0000   CALL <JMP.&msvcrt.printf>

Inoltre ciò che fai è anche scorretto: lo specificatore di formato %p accetta un puntatore a void. %d tratta invece il valore come intero.

Anche compilando con MSVC++ la situazione è identica:
Codice:
00331020  /$  55            PUSH EBP
00331021  |.  8BEC          MOV EBP,ESP
00331023  |.  68 00103300   PUSH 00331000        
00331028  |.  68 08903400   PUSH OFFSET 00349008                  
0033102D  |.  E8 5E000000   CALL 00331090  
00331032  |.  83C4 08       ADD ESP,8
00331035  |.  68 00103300   PUSH 00331000                         
0033103A  |.  68 0C903400   PUSH OFFSET 0034900C                
0033103F  |.  E8 4C000000   CALL 00331090                          
00331044  |.  83C4 08       ADD ESP,8
00331047  |.  33C0          XOR EAX,EAX
00331049  |.  5D            POP EBP
0033104A  \.  C3            RETN

Ciò che devi fare è questo:
C:
    printf("%d", ((uint8_t*)foo)[0]); //stampa indirizzo ma in intero

Questo produce:

Codice:
00DB1000
85
(notare che 85d è 0x55, il primo byte)

E infatti l'assembly è completamente differente da quello mostrato sopra:
Codice:
00DB1020  /$  55            PUSH EBP
00DB1021  |.  8BEC          MOV EBP,ESP
00DB1023  |.  68 0010DB00   PUSH 00DB1000   
00DB1028  |.  68 0890DC00   PUSH OFFSET 00DC9008          
00DB102D  |.  E8 6E000000   CALL 00DB10A0              
00DB1032  |.  83C4 08       ADD ESP,8
00DB1035  |.  B8 01000000   MOV EAX,1
00DB103A  |.  6BC8 00       IMUL ECX,EAX,0
00DB103D  |.  81C1 0010DB00 ADD ECX,00DB1000         
00DB1043  |.  0FB611        MOVZX EDX,BYTE PTR DS:[ECX]
00DB1046  |.  52            PUSH EDX                      
00DB1047  |.  68 0C90DC00   PUSH OFFSET 00DC900C       
00DB104C  |.  E8 4F000000   CALL 00DB10A0   
00DB1051  |.  83C4 08       ADD ESP,8
00DB1054  |.  33C0          XOR EAX,EAX
00DB1056  |.  5D            POP EBP
00DB1057  \.  C3            RETN

La parte interessante è:
Codice:
00DB1035  |.  B8 01000000   MOV EAX,1
00DB103A  |.  6BC8 00       IMUL ECX,EAX,0
00DB103D  |.  81C1 0010DB00 ADD ECX,00DB1000          
00DB1043  |.  0FB611        MOVZX EDX,BYTE PTR DS:[ECX]

L'indirizzo che compare è l'entry point della funzione; ECX è uguale a 0 (indice 0), quindi viene sommato questo valore all'indirizzo; il risultato è l'indirizzo al quale si accede, ma solo al byte e non a tutta la DWORD (MOVZX = estende con zero EDX).

Puoi anche evitare il "sinctactic sugar":
C:
    printf("%d", ((uint8_t*)(foo+0))); //stampa indirizzo ma in intero


Se invece vuoi proprio solo il medesimo indirizzo ma in decimale, allora tutto ciò che ho detto è stato inutile (?) e ti basta fare:
C:
    printf("%d", &foo); //stampa indirizzo ma in intero

Output:
Codice:
002D1000
2953216
 

Matteo34

Nuovo Utente
104
3
CPU
i5-10500 3.2Ghz
Dissipatore
Non specificato
Scheda Madre
Non specificata
HDD
M.2 251GB e M.2 500GB
RAM
16GB DDR4 2666mhz
GPU
Grafica Intel® UHD 630
Audio
Non specificata
Monitor
1920x1080 27"
PSU
Non specificato
Case
Non specificato
Periferiche
Nono specificato
Net
Eolo
OS
Ubuntu
Io ho compilato con GCC per x86 (MinGW in realtà, sono sotto Windows). Tu con cosa stai compilando?

C:
#include <stdio.h>

void foo(void)
{
    printf("Hello");
}

int main(void)
{
    printf("%p\n", foo); // stampo indirizzo

    printf("%X", *foo); //stampa indirizzo ma in intero

    return 0;
}

Con questo ottieni:
Codice:
004015C0
4015C0

Se invece ti aspetti come contenuto quello che ho mostrato sopra, allora avviene perchè quando compili il codice generato è identico.
Mi spiego meglio:

Codice:
004015E3  |.  C74424 04 C01 MOV DWORD PTR SS:[LOCAL.3],004015C0
004015EB  |.  C70424 4A4040 MOV DWORD PTR SS:[LOCAL.4],OFFSET 004040
004015F2  |.  E8 D50F0000   CALL <JMP.&msvcrt.printf>              
004015F7  |.  C74424 04 C01 MOV DWORD PTR SS:[LOCAL.3],004015C0     
004015FF  |.  C70424 4E4040 MOV DWORD PTR SS:[LOCAL.4],OFFSET 004040
00401606  |.  E8 C10F0000   CALL <JMP.&msvcrt.printf>

Inoltre ciò che fai è anche scorretto: lo specificatore di formato %p accetta un puntatore a void. %d tratta invece il valore come intero.

Anche compilando con MSVC++ la situazione è identica:
Codice:
00331020  /$  55            PUSH EBP
00331021  |.  8BEC          MOV EBP,ESP
00331023  |.  68 00103300   PUSH 00331000       
00331028  |.  68 08903400   PUSH OFFSET 00349008                 
0033102D  |.  E8 5E000000   CALL 00331090 
00331032  |.  83C4 08       ADD ESP,8
00331035  |.  68 00103300   PUSH 00331000                        
0033103A  |.  68 0C903400   PUSH OFFSET 0034900C               
0033103F  |.  E8 4C000000   CALL 00331090                         
00331044  |.  83C4 08       ADD ESP,8
00331047  |.  33C0          XOR EAX,EAX
00331049  |.  5D            POP EBP
0033104A  \.  C3            RETN

Ciò che devi fare è questo:
C:
    printf("%d", ((uint8_t*)foo)[0]); //stampa indirizzo ma in intero

Questo produce:

Codice:
00DB1000
85
(notare che 85d è 0x55, il primo byte)

E infatti l'assembly è completamente differente da quello mostrato sopra:
Codice:
00DB1020  /$  55            PUSH EBP
00DB1021  |.  8BEC          MOV EBP,ESP
00DB1023  |.  68 0010DB00   PUSH 00DB1000  
00DB1028  |.  68 0890DC00   PUSH OFFSET 00DC9008         
00DB102D  |.  E8 6E000000   CALL 00DB10A0             
00DB1032  |.  83C4 08       ADD ESP,8
00DB1035  |.  B8 01000000   MOV EAX,1
00DB103A  |.  6BC8 00       IMUL ECX,EAX,0
00DB103D  |.  81C1 0010DB00 ADD ECX,00DB1000        
00DB1043  |.  0FB611        MOVZX EDX,BYTE PTR DS:[ECX]
00DB1046  |.  52            PUSH EDX                     
00DB1047  |.  68 0C90DC00   PUSH OFFSET 00DC900C      
00DB104C  |.  E8 4F000000   CALL 00DB10A0  
00DB1051  |.  83C4 08       ADD ESP,8
00DB1054  |.  33C0          XOR EAX,EAX
00DB1056  |.  5D            POP EBP
00DB1057  \.  C3            RETN

La parte interessante è:
Codice:
00DB1035  |.  B8 01000000   MOV EAX,1
00DB103A  |.  6BC8 00       IMUL ECX,EAX,0
00DB103D  |.  81C1 0010DB00 ADD ECX,00DB1000         
00DB1043  |.  0FB611        MOVZX EDX,BYTE PTR DS:[ECX]

L'indirizzo che compare è l'entry point della funzione; ECX è uguale a 0 (indice 0), quindi viene sommato questo valore all'indirizzo; il risultato è l'indirizzo al quale si accede, ma solo al byte e non a tutta la DWORD (MOVZX = estende con zero EDX).

Puoi anche evitare il "sinctactic sugar":
C:
    printf("%d", ((uint8_t*)(foo+0))); //stampa indirizzo ma in intero


Se invece vuoi proprio solo il medesimo indirizzo ma in decimale, allora tutto ciò che ho detto è stato inutile (?) e ti basta fare:
C:
    printf("%d", &foo); //stampa indirizzo ma in intero

Output:
Codice:
002D1000
2953216
Ok tutto chiaro, Grazie a tutti
 

Andretti60

Utente Èlite
6,440
5,091
@DispatchCode attento: il fatto che un vettore locale inizializzato non significa che sia nello stack, lo standard non lo dice e quindi dipende dal compilatore. Potrebbe benissimo essere nello heap (il che evita di essere caricato ogni volta che la funzione viene richiamata) e venga usato un puntatore nello stack per accedere ai suoi elementi.
 
  • Mi piace
Reazioni: DispatchCode

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,222
1,853
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
Stato
Discussione chiusa ad ulteriori risposte.

Ci sono discussioni simili a riguardo, dai un'occhiata!

Entra

oppure Accedi utilizzando
Discord Ufficiale Entra ora!

Discussioni Simili