Dziwne rezultaty odczytywania adresów pamięci poprzez wskaźnik na tablicę

0
#include <iostream>

using namespace std;

int main()
{
    char tab[]={1,2,3,4,5,6,7,8,9};

    cout << "tab = " << hex << tab << endl;
    cout << "*tab= " << hex << *tab << endl;
    cout << "(short*)tab = " << hex << (short*)tab << endl;
    cout << "*((short*)tab) = " << hex << *((short*)tab) << endl;
    cout << "*((short*)tab+2) = " << hex << *((short*)tab+2) << endl;

    return 0;
}

Dany jest powyższy kod w QT Creator 4.6.2, kompilowany przez MinGW_32bit standardowy z instalatora. Otrzymuję dziwne wyniki:

(char*)tab = (tutaj pojawia się kilka znaczków ASCII)	Ŕţa
*tab= (tutaj pojawia się "buźka" ASCII)
(short*)tab = 0x61fe97
*((short*)tab) = 201
*((short*)tab+2) = 605

Czy mogę kogoś mądrzejszego ode mnie poprosić o kilka wyjaśnień, mianowicie:

  • Skąd te dziwne "adresy" ASCII zamiast normalnego adresu jak po zrzutowaniu na short w trzecim przypadku?
  • Z czego wynika zakres adresów? Jestem przyzwyczajony do adresów 64 albo 32 bitowych, a ten powyższy 0x61fe97 wygląda na 24 bitowy? Czy źle liczę?
  • Dlaczego późniejsze odczytywanie wartości z adresów daje wyniki "czytane od końca"? Jak dla mnie po rzutowaniu wskaźnika char na short, wyniki w hexie powinny być 102 i 506 (i nie powinny mieć też 0x na początku?).
  • Podobne problemy z odczytywaniem pamięci w QT Creator miałem już wcześniej, czy jest jakiś problem w samym programie?
1

operator << jest przeciążony w specjalny sposób dla const char *!
Generalnie chodzi o to, by zachować kompatybilność z C-stringami.
Czyli wszystkie inne wskaźniki wypisują adres.
A wskaźniki typu const char * lub typu który domyślnie jest konwertowany do const char * powoduje wypisanie ciągu C-string pod wskazanym adresem.
Chodzi o to by działało coś takiego:

cout << "jakis napis\n"; // to jest typu const char [13] następuje niejawna konwersja do `const char *
char napis[] = "inny napis"; // to jest typu char [11]
cout << napis << endl; // następuje niejawna konwersja do `const char *

Powyżej oczekiwany jest że nastąpi wypisanie napisu, a nie jego adresu.

Manipulator hex nie ma wpływu na działanie wypisania const char *.
Nie ma też wpływu na sposób wypisywania zwykłego adresu pamięci.

Natomiast to:

    cout << "*((short*)tab) = " << hex << *((short*)tab) << endl;
    cout << "*((short*)tab+2) = " << hex << *((short*)tab+2) << endl;

To tak naprawdę UB, które wypisze dwa bajty w postaci szesnastkowej.
Jako,że używasz maszyny z małą endianą wypisuje się 0201 (pierwsze dwa bajty)
Oraz 605 (5 i 6 bajt bo +2 przesówa wskaźnik typu short * czyli efektywnie +4 bajty).

Program, który "potrzebuje odczytywać adresy pamięci" to zły program ponieważ nie daje użytkownikowi żadnej wartości (chyba, że ktoś jest assemblerowym guru).

0
MarekR22 napisał(a):

Natomiast to:

    cout << "*((short*)tab) = " << hex << *((short*)tab) << endl;
    cout << "*((short*)tab+2) = " << hex << *((short*)tab+2) << endl;

To tak naprawdę UB, które wypisze dwa bajty w postaci szesnastkowej.
Jako,że używasz maszyny z małą endianą wypisuje się 0201 (pierwsze dwa bajty)
Oraz 605 (5 i 6 bajt bo +2 przesówa wskaźnik typu short * czyli efektywnie +4 bajty).

Myślałem, że QT Creator zainstalowany "standardowo" na zwykłym Win10 będzie standardowo pracował w dużej endianie (i odnośnie 3 przypadku miał "normalne" 32/64-bitowe adresy), nie wiem czy jednak coś popsułem przy instalacji, czy da się to gdzieś ustawić?

Program, który "potrzebuje odczytywać adresy pamięci" to zły program ponieważ nie daje użytkownikowi żadnej wartości (chyba, że ktoś jest assemblerowym guru).

To tylko ćwiczenia dla lepszego zrozumienia wskaźników i pamięci :)

1

Qt Creator to IDE, nie ma on znaczenia dla kompilatora. Big endian nie jest używany na x86 ani x64, więc nie wiadomo skąd takie przypuszczenia.

0

Endianę definiuje używany procesor.
Obecnie większość procesorów używa małej endiany, w drugiej kolejności procesory (np ARM) mają opcję konfiguracyjną na endianę (i są ustawiane na małą endianę), a procesorów z dużą endianą jest mało.

0

Cały czas myślałem, że na popularnych architekturach jest używany big endian tak jak w normalnej matematyce :P Teraz wszystko ma sens, dałbym wam łapkę w górę ale nie mogę niezarejestrowany :)

Jeszcze tylko zastanawia mnie skąd się bierze "dziwny" 24-bitowy adres pamięci w 3 przypadku, chyba pierwszy raz widzę taki w QT Creator, w innych środowiskach chyba zawsze był przynajmniej 32-bitowy.

0

A skąd pewność, że ten adres ma 24 bity? Zapewne wiodące cyfry są zerami! A wynika to z miejsca ulokowania stosu.
Dopisz:

cout << "*((short*)tab+2) = " << hex << *((short*)tab+2) << " " << sizeof(short*) << endl;

Zapewne pojawi się 4 (jeśli kompilujesz do architektury 32 bitów).

0

Rzeczywiście 4, wydawało mi się, że przy zapisie z 0x na początku te zera i tak zostałyby wyświetlone i byłoby 0x00cośtam.

To teraz już wiem wszystko, dzięki za pomoc!

0

Możesz jeszcze spróbować umieścić napis w innych miejscach pamięci i zobaczyć co się stanie.

#include <iostream>
#include <string>
#include <iomanip>
 
using namespace std;
 
string globalSmall("small");
string globalLong("ten napis musi być dlug z powodu Small String Optimization");
 
#define ADRESS_INFO(x) \
    cout << "        " #x " Location: " << &x << '\n'; \
    cout << #x ".c_str() Location: " << (void*)x.c_str() << '\n'

int main()
{
    string localSmall("maly");
    string localLong("ten napis musi być dlug z powodu Small String Optimization");
 
    ADRESS_INFO(globalSmall);
    ADRESS_INFO(globalLong);
    ADRESS_INFO(localSmall);
    ADRESS_INFO(localLong);

    globalSmall = "maly";
    globalLong = "inny napis dlugi napis SSO";
    ADRESS_INFO(globalSmall);
    ADRESS_INFO(globalLong);
    
    return 0;
}

https://wandbox.org/permlink/cy3BiK9r48sr0TWb

0
        globalSmall Location: 0x406038
globalSmall.c_str() Location: 0x406040
        globalLong Location: 0x406050
globalLong.c_str() Location: 0x9c1108
        localSmall Location: 0x61fe84
localSmall.c_str() Location: 0x61fe8c
        localLong Location: 0x61fe6c
localLong.c_str() Location: 0x9c1150
        globalSmall Location: 0x406038
globalSmall.c_str() Location: 0x406040
        globalLong Location: 0x406050
globalLong.c_str() Location: 0x9c1108

Takie wychodzi, ale nie wiem co o tym myśleć, poza tym, że globalne stringi zostały na swoim adresie nawet po zmianie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1