• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

C++ referencja i podwójne gwiazdki

Object Storage Arubacloud
+2 głosów
7,811 wizyt
pytanie zadane 14 lipca 2016 w C i C++ przez itcloud Gaduła (3,380 p.)
edycja 14 lipca 2016 przez Patrycjerz

Temat wskaźników mam opanowany. Tzn. nie jest dla mnie czarną magią zapis typu:

#include <iostream>
using namespace std;

//test na referencje
void test_refe(int &r){
    r = 10;
}

//test na pointer
void test_point(int *p){

    *p = 666;
}

int main() {

    //test dot. referencji przekazanej do funkcji - zmienna sie zmienila!
    int r = 5;
    test_refe(r);
    cout << "r=" << r << endl;


    //test dot. wskaznika
    int x = 5;
    int *ptr = &x;
    test_point(ptr);
    cout << "p=" << x << endl;
    cout << "*ptr=" << *ptr << endl;
    cout << "ptr=" << ptr << endl;
    cout << "x=" << x << endl;
    return 0;

}

Czyli w skrócie widzę to tak:

1. Referencja to taki alias na zmienną. Jeśli wykorzystuję ją jako argument funkcji, to nie ma problemu z kopiowaniem zmiennej przy wejściu do funkcji, a potem przy wychodzeniu ze względu na to, że wartość zmiennej była widziana tylko lokalnie w funkcji, tracę informacje o operacjach wykonywanych na zmiennej wewnątrz funkcji, chyba że ta funkcja zwróci mi końcowy wynik (return), ale tu ma miejsce niepotrzebne kopiowanie danych.

2. Wskażnik (pojedyncza gwiazdka) - występuje przy deklarowaniu zmiennej i oznacza adres w pamięci, pod którym jest ta zmienna. A co z podwójnymi gwiazdkami, tym bardziej przekazywanymi jako argument w funkcji? **p ?  To inaczej referencja?

Jakby ktoś mi to mógł "łopatologicznie" wytłumaczyć, bo coś tam wiem ale przy czytaniu obcego kodu się gubię mimo wszystko.

2 odpowiedzi

+8 głosów
odpowiedź 14 lipca 2016 przez Sebastian Fojcik Nałogowiec (43,040 p.)
edycja 1 grudnia 2017 przez Sebastian Fojcik

wskaźnik na zmienną typu int.

int * wsk;

wskaźnik na wskaźnik typu int.

int ** wsk2;

Z powyższej definicji wynika, że wsk2 możesz pokazać na wskaźnik wsk. Ale uwaga: nie pokazujesz na to, na co pokazuje wsk. Pokazujesz na sam wskaźnik.

wsk2 = &wsk;

Czyli zmienna wskaźnikowa wsk2 przechowuje... adres wskaźnika.
Aby dostać się do wskaźnika zapisujesz:

*wsk2;

Aby dostać się do zmiennej, na którą pokazuje wskaźnik wsk, zapisujesz:

**wsk2;

Taki wskaźnik na wskaźnik ma zastosowania przeważnie przy tablicach i nie ma nic wspólnego z referencją.

Spróbujmy stworzyć dynamicznie dwuwymiarową talicę o nazwie tablica.

[int] [int] [int]
[int] [int] [int]
[int] [int] [int]

Zauważ, że są tutaj 3 tablice 3-elementowe. Dokładniej: 3 wiersze. Jeśli rozumiesz wskaźniki, to stwórzmy jeden taki wiersz za pomocą wskaźnika:

int * wiersz1 = new int[ 3 ];

[int] [int] [int]

Ale zauważ, że tablica tablica ma 2 wymiary. W pierwszym wybierasz wiersz, w drugim kolumnę. Tak więc nasza zmienna tablica jest w rzeczywistości: wskaźnikiem na tablice 3-elementowe. Wykorzystując wiedzę, którą podałem Ci wcześniej napiszmy wskaźnik na tablicę wiersz1.

int ** tablica;

Dobrze, mamy wskaźnik, którym możemy pokazać na tablicę, czyli inaczej: na zmienną typu: int *.
Skoro mamy wskaźnik na int *, to zróbmy tablicę zmiennych typu int*. Można? Oczywiście! Wcześniej mieliśmy wskaźnik na int i zrobiliśmy tablicę int'ów. Teraz też zadziała.

tablica= new int*[ 3 ];

Wygląda super, prawda? :-) Zmienna tablica jest w tym momencie taką tablicą:

[int*] [int*] [int*]

Ale hej! Mieliśmy zrobić tablicę 3 x 3 z intów tak jak wyżej! Co to jest?
Spokojnie, już kończymy.
Przypatrz się dokładnie powyższej tablicy. Są w niej wskaźniki. A z każdego wskaźnika możesz utworzyć tablicę (new).
Do poszczególnych wskaźników odnosimy się jak w normalnej tablicy:
tablica[ 0 ];
tablica[ 1 ];
tablica[ 2 ];

No to robimy tablice 3-elementowe.

tablica[ 0 ] = new int[ 3 ];
tablica[ 1 ] = new int[ 3 ];
tablica[ 2 ] = new int[ 3 ];

Oto co otrzymaliśmy (obróciłem tablicę dla czytelności):

[int*] --> [int] [int] [int]
[int*] --> [int] [int] [int]
[int*] --> [int] [int] [int]

Mamy tablicę trzech wskaźników, do których odnosimy się: tablica[ ... ] przy czym każdy wskaźnik jest tablicą int. I teraz chcemy się odnieść do pierwszego elementu w pierwszym wierszu. W takim wypadku bierzemy pierwszy wskaźnik i pierwszy element z tablicy. W kodzie:

tablica[ 0 ][ 0 ];

Ale pamiętajmy, że nazwa tablicy jest wskaźnikiem na pierwszy element, dlatego poniższy zapis oznacza dokładnie to samo:

**tablica;

można też tak:
(*tablica)[ 0 ];
albo tak:
*(tablica[ 0 ]);

Jak chcesz być śmieszkiem, to możesz zamienić 0 i tablica miejscami :-D

*( 0[ tablica ] );

albo

0[ (*tablica) ];

Jeśli myślisz, że żartuję, to nie. Jestem śmiertelnie poważny!
( tab[ 5 ] oznacza to samo co 5[ tab ] ) C++ <3
Wyjaśnienie dlaczego to działa podaję w komentarzu, bo już mi tu forum krzyczy, że limit znaków :D

Oczywiście NIKT tak się nie odnosi do tablic, ale jeśli dobrze zrozumiałeś temat, to powinieneś wiedzieć dlaczego tak się da.

To był tylko przykład zastosowania wskaźnika na wskaźnik. Są oczywiście inne przykłady. Nie chcę tutaj wyjeżdżać z obiektowością, bo pewnie jej nie znasz, ale napiszę dla potomnych.
Czasami możemy chcieć, aby klasa przechowywała wskaźnik na wskaźnik innej klasy, aby niejako "podglądać" na co aktualnie on pokazuje. Wskaźnikiem bazowym można pokazywać na cokolwiek, a my mając wskaźnik na ten wskaźnik możemy zawsze wiedzieć gdzie aktualnie "patrzy" klasa :-)

Jeśli masz jeszcze jakieś pytania i wątpliwości, to pisz. Różnica pomiędzy referencjami a wskaźnikami to bardzo ciekawy temat, ale boję się, że mnie zaraz forum ograniczy w ilości znaków ;-)

Pozdrawiam.

#EDIT:
Aaaa zaryzykuję. Różnice między referencją a wskaźnikiem, lecimy :-)

1* Każda referencja musi być zainicjalizowana. Przykład referencji do zmiennej:

int zmienna = 5;
int & referencja = zmienna
;

Od teraz nazwa "referencja" jest inną nazwą na zmienną "zmienna". Możesz to sprawdzić wypisując adres obydwu zmiennych (będzie taki sam):

cout << &zmienna << endl << &referencja << endl;

Wnioski nasuwają się same. Referencja jest na stałe przypisana do jednej zmiennej. Nie można jej "przestawić". W funkcjach ma szczególne zastosowanie i to zastosowanie doskonale znasz.

2* Nie można stworzyć referencji do referencji w taki sposób:
int && ref2 = ref1; // błąd!
Ale można w taki:

int zmienna = 5;
int & ref1 = zmienna;
int & ref2 = ref1;

3* Wskaźniki przechowują adres zmiennej, tak więc sam wskaźnik też jest zmienną i zajmuje pamięć. Jerzy Grębosz w swojej książce Symfonia C++ pisał, że najczęściej wskaźnik jest rozmiaru int, czyli zajmuje 4 bajty, ale to zależy od kompilatora.

4* Wskaźnik można przestawiać na dowolną zmienną, która jest jego typu, a przy pomocy rzutowania można pokazać na praktycznie dowolne miejsce w pamięci wskaźnikiem dowolnego typu (wskaźnikiem na bool pokazać ostream cout'a. Tak, to jest możliwe :-) Nie wierzysz? Proszę:

bool * wsk = reinterpret_cast<bool*>(&cout);

(zamiast tego reinterpret_cast<bool*> można napisać po prostu (bool*), ale tak jest bardziej profesjonalnie, o ile w przypadku pokazywania wskaźnikiem bool na cout'a można w ogóle mówić o "profesjonalności" XD)
Oczywiście na litość anielską NIGDY tego nie rób. To już podchodzi pod szarlataństwo i tacy programiści trafiają na stertę ;-)

Są również wskaźniki typu void i one z założenia mogą pokazywać na wszystko bez żadnego rzutowania, ale żeby posłużyć się tą wartością, na którą pokazują trzeba już rzutować. Przykład:

int a = 5;
void * wsk = &a;

cout << *(int*)wsk << endl;
cout << *reinterpret_cast<int*>(wsk) << endl;

Obydwie opcje są poprawne. Druga bardziej profesjonalna, choć i tak wiem którą wybierzesz...

5* Można stworzyć wskaźnik na dowolny typ. A że wskaźnik również jest typem, to wskaźnik na wskaźnik jest jak najbardziej prawidłowy. Można stworzyć wskaźnik na typ enum, a... nawet na cout'a. Zróbmy to! :-D

ostream * wskaznik = &cout;
*wskaznik << "Moj wlasny cout!!!" << endl;

Gdybyś użył w taki sposób powyższego wskaźnika bool, to... cóż... najwyżej wysadzisz komputer :-)
Dostrzegasz to piękno C++ teraz?

6* Można zrobić wskaźnik na funkcję. Przykład:

void funkcja()
{
	cout << "FUNKCJA!" << endl;
}

int main()
{
	void( *wsk )() = funkcja;

	wsk(); // wywołanie
}

Referencję do funkcji również można utworzyć:

void( &wsk )() = funkcja;

Można sprawdzić wypisując adres funkcji i referencji. Tak... funkcja ma adres &funkcja ;-)

7* Ale to nie koniec zabawy! Możesz stworzyć rzecz jasna referencję na cout'a i posługiwać się własną nazwą strumienia:

ostream & ref = cout;
ref << "Woooho" << endl;

8* I na koniec jeszcze coś fajowego. Referencja na wskaźnik! Nie jest ją łatwo stworzyć, a wiedza taka nie jest dostępna w żadnej książce. To wiedza zakazana, więc ćśśśś i do dzieła!

int * wsk;
auto & ref = wsk;
cout << &wsk << endl << &ref << endl;

Syntax C++ nie pozwala "normalnie" stworzyć referencji do wskaźnika, ale można to obejść stosując automatyczny dobór typów. Przykład wyżej pokazuje, że obydwie nazwy są tym samym miejscem w pamięci.

Zdaję sobie sprawę, że mogłem Ci zrobić mielonkę z mózgu, ale nie obawiaj się. Nie wszystko musisz zrozumieć. Kiedyś tu wrócisz i się będziesz śmiał.
Możesz tak dalej eksperymentować. Troszkę wkroczyliśmy w szarlataństwo, ale to niiic. Baw się dalej, a jak mi się coś przypomni, to tu dopiszę. Trzymaj się ;-)

komentarz 14 lipca 2016 przez Sebastian Fojcik Nałogowiec (43,040 p.)

Możesz oczywiście pytać jeśli coś jest niejasne w tym co napisałem. Tylko proszę, nie zaczynaj nowego tematu, bo się uruchomię i naprawdę przekroczę limit znaków laugh

komentarz 14 lipca 2016 przez itcloud Gaduła (3,380 p.)
O matko, pozwól że wydrukuję to co napisałeś i się z tym prześpię. Dzięki wielkie. Jednak nie bardzo opanowałem jeszcze temat.
komentarz 14 lipca 2016 przez Sebastian Fojcik Nałogowiec (43,040 p.)
edycja 14 lipca 2016 przez Sebastian Fojcik
Poprawiłem kilka literówek i małe pomyłki w kodzie. Popraw sobie punkt 1* i 6*, bo tam zmienił się kod ;-)

Wiedza, którą tutaj przedstawiłem nie jest jakoś specjalnie zaawansowana. Wystarczy, że bardzo dobrze zrozumiesz CZYM jest wskaźnik. Musisz pamiętać, że wskaźnik to normalna zmienna, tylko służy do zadań specjalnych. Tyle :-)

PS. Jak już zrozumiesz ideę wskaźników, to poczytaj czym jest rzutowanie w C++. To Ci otworzy nowe możliwości, pole do popisu i... będziesz mógł się świetnie bawić, np. trollować kompilator oszukując go na dziesiątki sposobów.
komentarz 15 lipca 2016 przez itcloud Gaduła (3,380 p.)
edycja 15 lipca 2016 przez itcloud
Kopalnia wiedzy, musze to wszystko przegryźć, zacząłem od prostszych do ogarnięcia postów kolegi Muzyka, ale jeszcze się po trosze gubię, bo chyba jednak muszę przejść do zadań praktycznych, żeby się z tym oswoić. Na chwilę obecną przy prostych pytaniach testowych dot. wskaźników/referencji raczej nie miałbym problemów, ale jak juz jest pokazany jakiś dłuższy kod, to zaczynaja się schody. Albo taki zapis, który podałeś:

int a = 5;void * wsk = &a;

cout << *(int*)wsk << endl;

"Czytać" to umiem, ale żebym na to wpadł przy okazji pisania jakiegoś programiku to juz gorzej. Ja np. tę końcówkę napisałbym tak intuicyjnie, że (int) nie miałby w srodku gwiazdki, ale domyślam się, że jak rzutuję nie zmienną, a zmienną wskaźnikową, to musi być ta gwiazdka.

A co do obiektowości - podstawy mam. Kiedyś siedziałem mocno w php, co prawda w v4 z elementami obiektowości, ale zawsze coś. Zresztą, ten operator 'new' którego też tutaj używałeś to już elementy obiektowości, czyż nie ? Możesz jeszcze coś więcej napisać w tym temacie, chętnie poczytam(y).

ps. jeszcze raz dzięki dobry człowieku:) Wydrukowałem sobie Twojego posta.
komentarz 15 lipca 2016 przez Sebastian Fojcik Nałogowiec (43,040 p.)
edycja 15 lipca 2016 przez Sebastian Fojcik

Poćwicz w praktyce wskaźniki oraz operatory gwiazdka (*) i ampersand (&). Na nich opierają się wskaźniki. Reszta rzeczy, której możesz nie znać, to rzutowanie wskaźników. No... i być może z referencją Cię zaskoczyłem, bo do tej pory mogłeś myśleć, że referencja występuje tylko jako argument funkcji.

Jeśli chodzi o kod, który sprawiłby Ci problem ze zrozumieniem:

int a = 5;
void * wsk = &a;

cout << *(int*)wsk << endl;

Oczywiście to był przykład dydaktyczny i tak nie powinno się pisać kodu. Sam wskaźnik void* jest dosyć... specyficzny, bo nie zawiera informacji o typie. Czasami taka informacja nie jest potrzebna, np. gdy operujemy na samych bitach. Osobiście nigdy tego wskaźnika nie użyłem, ale jako programista C++ muszę go znać i pokazałem Ci go jako ciekawostkę.

Mówisz, że napisałbyś intuicyjnie (int) ? W takim razie nie do końca zrozumiałeś czym jest typ wskaźnikowy. Chciałem wypisać wartość, na którą pokazuje wskaźnik. Używamy zatem *wskaźnik. To jest jasne. Wskaźnik void* pokazuje na int, ale cout tego nie wie. Zatem musisz potraktować (rzutować) wskaźnik jakby był wskaźnikiem na int. I teraz podstawowe pytania do Ciebie (sam na nie odpowiem):
Jakiego typu jest zmienna int?
int
Jakiego typu jest wskaźnik na int?
int*
Zatem my chcemy "zrobić" ze wskaźnika void*, wskaźnik int*. Musimy zatem rzutować w taki sposób: (int*)wsk; w nawiasie zawsze stoi typ docelowy.

Ale uwaga: rzutowanie w taki sposób (int)wsk również byłoby poprawne. Zastanów się jednak, co w tym momencie robisz. Rzutujesz wskaźnik (typ wskaźnikowy void*) na typ int. To znaczy, że adres pamięci, który przechowuje wskaźnik jest traktowany jak zwyczajna liczba całkowita.

Popatrz jaki jest efekt rzutowania wskaźnika na int.
Mamy wskaźnik int * wsk = &a; adres, na który pokazuje wskaźnik to (załóżmy): 196250.

cout << wsk << endl;   // wypisze 196250
cout << wsk+1 << endl; // wypisze 196254

cout << (int)wsk << endl;   // wypisze 196250
cout << (int)wsk+1 << endl; // wypisze 196251


Rzutowanie jest bardzo ważnym rozdziałem w programowaniu. W C++ można rzutować przy pomocy nawiasów albo operatorów. Są takie cztery:
static_cast< >( ) <-- rzutowanie zwykłych typów
reinterpret_cast< >( ) <-- rzutowanie wskaźników
const_cast< >( ) <-- ignorowanie (lub nadanie) przydomka const
dynamic_cast< >( ) <-- rzutowanie polimorficzne. Bardzo ważny operator i ma szczególnie duże zastosowanie w polimorfizmie.

Rzutowanie to dosyć "nietypowa" operacja w programowaniu, dlatego używanie tych długich i wyróżniających się w kodzie operatorów jest jak najbardziej polecane.

Pytasz, czy użycie przeze mnie operatora new, to już obiektowość. Raczej nie... operator ten po prostu zleca alokację pamięci i zwraca adres. Element obiektowości, to korzystanie ze strumieni cin oraz cout. To już jest obiektowość pełną gębą, choć jeszcze tego nie dostrzegasz. To właśnie dzięki ich obiektowej naturze udało mi się zrobić referencję i wskaźnik do cout nieco wcześniej. Kiedyś dowiesz się... że cout, to zwykła zmienna :-)

Jeszcze raz powracając do kodu, który sprawił Ci kłopot. Nie martw się. To jest wskaźnik void i pewnie nigdy w życiu z niego nie skorzystasz. Jeśli jednak zrozumiesz wskaźniki w 100%, to bez żadnego problemu będziesz potrafił przeczytać kod z nimi zawarty.

Jeśli jakiś kod / jakaś instrukcja jest dla Ciebie niejasna, to możesz ją tutaj wysłać. Rozbijemy na czynniki pierwsze :-)

PS. Zapisy "wskaźnik int" czy "wskaźnik typu int" oznaczają po prostu wskaźnik, który ja specjalnie dla zobrazowania dla Ciebie nazywałem "wskaźnik int*", aby podkreślić, że ta zmienna (wskaźnik) jest typu "int*".

PSS. Wielkie niedopatrzenie z mojej strony. Przy tworzeniu tablicy zmiennych typu int* napisałem gwiazdkę po złej stronie. W odpowiedzi było napisane:
tablica= new *int[ 3 ];
a powinno być oczywiście:
tablica= new int*[ 3 ];

komentarz 16 lipca 2016 przez itcloud Gaduła (3,380 p.)
Jeszcze nie przetrawiłem wszystkiego, co napisałeś, ale myślę że do poniedziałku dam radę. Na razie ćwiczę w code blocks i czytam kolejne Twoje fragmenty :)

Mozesz jeszcze na koniec napisać mi, czy te wskaźniki, rzutowanie wskażników, wskaźnik na wskaźnik.. to takie akademickie dysputy, temat do ogarnięcia pod kątem ew. testów na rozmowach rekrutacyjnych, czy w praktyce to się wykorzystuje?

Bo referencje jako argumenty funkcji czy wskażniki jako argumenty funkcji to na pewno i to "czuję", ale reszta to dla mnie czarna magia, nawet nie wiem jakie konkretne przykłady mógłbym testować, żeby sobie poćwiczyć.

Mój plan - owszem - jest taki, żeby powoli ale dokładnie opanować podstawy podstaw i dopiero zabierać sie za biblioteki, jakieś frameworki bo to myślę że opanuję, tym bardziej że mam doświadczenie z php i mysql-em. I widzisz, w web developerce znam konkretne przykłady: wiem, że interakcja z użytkownikiem opiera się na GET i POST poprzez formularze, wiem, jak zapisywać dane uzyskane od usera do bazy, jak je wyciągać, fetchować, jak używać regexpy itp, nawet coś na miarę szablonów sobie porobiłem, że o elementach obiektowości przy obsłudze bazy nie wspomnę. A przy c++ i tych podstawach, kompletnie nie mogę sobie wyobrazić jak te akademickie dysputy wykorzystać w praktyce :D
komentarz 23 września 2017 przez daniel1806 Obywatel (1,780 p.)
Drogi kolego, MEGA dobra odpowiedź. Też mnie nurtowało to co autora pytania a Ty wyczerpałeś temat całkowicie. Idealnie wyjaśnione. Dziękuję :)
komentarz 1 grudnia 2017 przez Sebastian Fojcik Nałogowiec (43,040 p.)
edycja 1 grudnia 2017 przez Sebastian Fojcik

Dzięki za wszystkie opinie i uwagi do tego "artykułu". Dołączam dla nienasyconych wiedzą o wskaźnikach:

DODATEK dla ciekawskich (Dlaczego tab[ 5 ] to to samo co 5[ tab ] i dlaczego to działa?)

Odpowiedź jest banalna. To wina... przemienności dodawania! Ale jak to? Co ma dodawanie do tablic. Ano to:

Jak chcesz się dostać do szóstego elementu tablicy, to bierzesz pierwszy element i przeskakujesz o 5 elementów w prawo, czyli w kodzie:

*( tab + 5 );  // albo tak
tab[ 5 ]       // albo tak

no ale chwila, czy z matematycznego punktu widzenia tab + 5 to nie to samo co 5 + tab? Już teraz chyba wiesz do czego zmierzam, prawda? Oczywiście poniższe zapisy oznaczają dokładnie to samo co te wyżej:

*( 5 + tab );  // przemienne dodawanie
5[ tab ];      // dziwne, ale prawdziwe i działa ;)

fajne nie? C++ to czasami dziwny język :P

Polecam wszystkim zrozumienie wskaźników! Można się z nimi świetnie bawić jak się nie ma rodzeństwa, znajomych czy coś ;-)

+1 głos
odpowiedź 14 lipca 2016 przez Michał Muzyka Pasjonat (24,080 p.)

referencja to jakby pokazanie funkcji że ta zmienna ma nie być kopiowana do funkcji tylko ma być używana normalnie czyli wszystkie działania na tej zmiennej zmieniają jej wartość, czyli jak mamy funkcje:
 

void sum(int &suma, int a, int b)
{
  suma = a+b;
}

to znaczy to że zmienna  którą wyślemy do funkcji (suma) zmieni swoją prawdziwą wartość (kompilator nie wyśle kopii tylko tą zmienną), 

a zapis
 

int **wskaznik;

to tak jakby wskaźnik na wskaźnik, umożliwia on tworzenie 2 wymiarowych tablic dynamicznych, o tak

int **wsk;

wsk = new int *[5];

for(int i=0; i<5; ++i)
     wsk[i] = new int [5];

//-------------------------------------

for(int i=0; i<5; ++i)
    delete[] wsk[i];

delete [] wsk;

 

komentarz 15 lipca 2016 przez Michał Muzyka Pasjonat (24,080 p.)
wsk[i] = new int [5];

tak w tym miejscu tworzysz 5 tablice 5 intów
ale tak jakby na jednej gałęzi
komentarz 15 lipca 2016 przez itcloud Gaduła (3,380 p.)
edycja 15 lipca 2016 przez itcloud

Próbowałem przeanalizować sobie Twój scenariusz:

1010101 11011010 10101010 10101010 01101101
tworzenie zmiennej char (1 bajtowa), czyli ona ma bity:
10101011
00001010  (wartosc 10 nadana w miejsce powyzszej zmiennej)
czyli pamiec:

00001010 1011010 10101010 10101010 01101101
no i tutaj mi się nie zgadza. U mnie to co wyżej, u Ciebie:

"a jak stworzysz potem wskaźnik na char
to z tej pamięci 00001010 11011010 10101010 10101010 01101101"

Chyba drobna pomyłka, zgadza się?

"a gdy ten wskaźnik bedzie wskazywał na tamtego chara to jego wartość to będzie 1 (pierwszy adres) czyli 00000001" - czyli w tym przypadku liczymy adresy od 1 i są one uzależnione od zmiennych, które przechowują?

komentarz 15 lipca 2016 przez Michał Muzyka Pasjonat (24,080 p.)
1. nie rozumiem gdzie jest błąd,

2.też nie rozumiem, ale każdy bajt w pamięci ma swój adres (nie wiem czy adresy liczone są od 0 czy od 1 ale na pewno żaden wskaźnik nie ma wartości 0)

czyli jak mamy inta (4 bajty) to jego adres to adres pierwszego bajta jaki go przechowuje, i nie wiem czy dobrze rozumiem ale wskaźnik wskazuje na zmienną swojego typu
komentarz 15 lipca 2016 przez itcloud Gaduła (3,380 p.)
Różnice wyboldowałem, po prostu coś mi się zaczeło to nie zgadzać. Ale może być tak, że ja tego nie kumam jednak.

Fajny ten przykład z rozpisaniem bitów w pamięci. Jednak mi się wydaje - pamiętając jeszcze assemblera i mapę pamięci z 6051 - że "jak mamy inta (4 bajty) to jego adres to adres pierwszego bajta jaki go przechowuje" - ogólnie wiem o co Ci chodzi, ale dotąd myślałem, ze pod pojęciem adres jest coś typu 0xAACCB1   (jeśli tu się zaczyna ten int o którym piszesz, to kolejny int będzie o 4 bajty dalej, czyli o 16 bitów dalej - 0xAACCC0)
komentarz 15 lipca 2016 przez Michał Muzyka Pasjonat (24,080 p.)
no mniej więcej, ja jeszcze tego w szkole nie miałem i nawet nie zanosi się żebym za niedługo miał.
Tak to był symulowany przykład, pokazywałem adresy dziesiętnie bo było łatwiej, jednak na ogromną pamięć ram trzeba używać systemu szesnastkowego

w przykładach wydaje mi się że jest dobrze bo:

00001010 11011010 10101010 10101010 01101101

pierwszy oktet to wartość naszego chara a z drugim jeszcze nic nie robiliśmy więc ma wartość taką jaką pozostawił inny program

Podobne pytania

+13 głosów
5 odpowiedzi 332 wizyt
pytanie zadane 23 grudnia 2019 w Offtop przez jaca121212 Nałogowiec (40,760 p.)
+34 głosów
3 odpowiedzi 1,032 wizyt
pytanie zadane 23 grudnia 2017 w Offtop przez niezalogowany
+18 głosów
7 odpowiedzi 479 wizyt
pytanie zadane 10 grudnia 2015 w Nasze projekty przez JimmyTulipanTudeski Bywalec (2,780 p.)

92,583 zapytań

141,434 odpowiedzi

319,669 komentarzy

61,966 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...