Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

Wat is nou het verschil tussen een pointer en een reference?

Pagina: 1
Acties:

  • st0p
  • Registratie: April 2004
  • Laatst online: 19-07-2024
Naar aanleiding van [url="http://http://gathering.tweakers.net/forum/list_messages/1289640"]dit[/url] draadje. Ik heb al meerdere malen gelezen op internet dat pointers in c++ fundamenteel verschillen van c#. Over het algemeen werd dit gezegd door mensen waarvan ik vermoed dat ze beter kunnen programeren dan ik, dus ze zullen wel gelijk hebben.

Desalniettemin, in de zeer beperkte tijd dat ik wat met c++ heb gespeeld gebruikte ik ze eigenlijk alleen voor pass-by-reference calls naar functies.

Natuurlijk is een reference in .net meer dan een pointer (denk aan garbage collection). Ook heb je volgens mij in c++ pointers naar functies, die in .net vervangen zijn door delegates (kan me in deze stelling vergissen). Maar wat is op een meer fundamenteel, semantisch niveau nou eigenlijk het verschil?

  • Marcj
  • Registratie: November 2000
  • Laatst online: 12:39
Een pointer in C++ is in principe gewoon een geheugenadres. Aangezien data en code beide gewoon in het geheugen staan kun je naar beide pointers laten wijzen. Hierbij is in C++ wel enige vorm van type-safety aanwezig (je kunt niet zomaar een int* naar een vector* omzetten zonder casten), maar je kunt ook gewoon rekenen met pointers. Dit doe je bijvoorbeeld als je een array probeert af te lopen. Hierbij kun je dus per ongeluk (bijv. door een bug) in een stuk geheugen terecht komen die je niet wilde aanspreken.

In C# (en andere OO-talen) is een reference onder water nog steeds een pointer, maar in de programeertaal worden er veel meer restricties op gelegd. Het voornaamste is dat je hierbij een reference alleen kunt wijzigen door deze naar een ander object laat wijzen (assignment), en niet door x bytes er bij op te tellen oid. Verder is de typesafety veel strikter, waarbij het onmogelijk wordt gemaakt om een verkeerd type object er aan toe te wijzen.

In C# heb je delagates die je idd kunt vergelijken met een pointer naar een functie, maar hierbij geldt ook dat je deze alleen kunt wijzigen door een assignment.

ps. In C# bestaan de oude pointers ook nog steeds (in unsafe blokken), maar dit is niet aan te bevelen.

  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

st0p schreef op zondag 27 april 2008 @ 18:51:
Maar wat is op een meer fundamenteel, semantisch niveau nou eigenlijk het verschil?
Je moet references en pointers eigenlijk niet met elkaar vergelijken; het zijn concepten op verschillende niveaus. Je zou kunnen zeggen dat references geimplementeerd zijn middels pointers. In zekere zin betekent dat dat references pointers zijn, maar in diezelfde zin zijn strings reeksen bytes en zijn bytes reeksen spanningsverschillen in de hardware. Het zijn representaties van elkaar en de woorden voor de verschillende presentaties worden in verschillende contexten gebruikt.

"Wat is het verschil tussen een reference en een reeks spanningsverschillen?" is ook geen betekenisvolle vraag.

[ Voor 22% gewijzigd door Confusion op 27-04-2008 20:46 ]

Wie trösten wir uns, die Mörder aller Mörder?


  • st0p
  • Registratie: April 2004
  • Laatst online: 19-07-2024
Als je het zo brengt, is een reference dan iets als een extreem slimme pointer? (oftewel een uitgebreide smart pointer?)

  • Marcj
  • Registratie: November 2000
  • Laatst online: 12:39
st0p schreef op zondag 27 april 2008 @ 20:59:
Als je het zo brengt, is een reference dan iets als een extreem slimme pointer? (oftewel een uitgebreide smart pointer?)
Zo zou je het kunnen zien, maar het idee er achter is compleet anders. Bij C(++) is een pointer echt een verwijzing naar een stuk geheugen (waarvan je hopelijk weet wat voor type het is), maar bij C# is een reference een verwijzing naar een object-instantie. In het laatste geval denk je net iets abstracter over je data.

Maar ja, met een uitgebreide smart pointer zou een reference ook in C++ geimplementeerd kunnen worden.

  • Sebazzz
  • Registratie: September 2006
  • Laatst online: 18-11 19:30

Sebazzz

3dp

Ook nog een verschil: Zover ik weet kan je van een reference niet het geheugenadres achterhalen, maar van een poniter wel (C++: &pointer)

[ Voor 2% gewijzigd door Sebazzz op 27-04-2008 21:11 . Reden: staat onzin ]

[Te koop: 3D printers] [Website] Agile tools: [Return: retrospectives] [Pokertime: planning poker]


  • Herko_ter_Horst
  • Registratie: November 2002
  • Niet online
Marcj schreef op zondag 27 april 2008 @ 21:07:
[...]
In het laatste geval denk je net iets abstracter over je data.
Dit vind ik het belangrijkste verschil. Een reference is m.i. een abstractie van een pointer, waarbij je je niet meer druk maakt hoe dat nou geimplementeerd is, maar om wat het voorstelt, namelijk een handle (naar een object).

"Any sufficiently advanced technology is indistinguishable from magic."


  • st0p
  • Registratie: April 2004
  • Laatst online: 19-07-2024
Marcj schreef op zondag 27 april 2008 @ 21:07:
[...]

Zo zou je het kunnen zien, maar het idee er achter is compleet anders. Bij C(++) is een pointer echt een verwijzing naar een stuk geheugen (waarvan je hopelijk weet wat voor type het is), maar bij C# is een reference een verwijzing naar een object-instantie. In het laatste geval denk je net iets abstracter over je data.
Dit is een duidelijke uitleg, dankje!

Verwijderd

Marcj schreef op zondag 27 april 2008 @ 21:07:
[...]
Bij C(++) is een pointer echt een verwijzing naar een stuk geheugen (waarvan je hopelijk weet wat voor type het is)
Is dat zo? Het is een tijdje geleden dat ik het bestudeerd heb, maar ik meen me te herinneren dat de C++ language specification helemaal niet voorschrijft dat een pointer (de basic, bald pointer) ook daadwerkelijk -echt- direct naar een geheugen adres wijst. Ik zal de spec er weer eens op na moeten slaan maar ik meen toch echt te herinneren dat het implementatie afhankelijk is.

  • Laurens-R
  • Registratie: December 2002
  • Laatst online: 29-12-2024
Verwijderd schreef op dinsdag 29 april 2008 @ 18:26:
[...]
Is dat zo? Het is een tijdje geleden dat ik het bestudeerd heb, maar ik meen me te herinneren dat de C++ language specification helemaal niet voorschrijft dat een pointer (de basic, bald pointer) ook daadwerkelijk -echt- direct naar een geheugen adres wijst. Ik zal de spec er weer eens op na moeten slaan maar ik meen toch echt te herinneren dat het implementatie afhankelijk is.
Zo word het wel op universiteiten en hogescholen uitgelegd... (om even als student te spreken)

Daarnaast is het zo (zoals al eerder gezegd) dat je ook rekenkundige operaties kan loslaten op je pointers, om door een bepaald stuk werkgeheugen te browsen. Dan mag je toch wel hopen dat je met echte adressen aan het werken bent. Wat echter wel zo is, is dat de adressen die je terug krijgt naar alle waarschijnlijkheid paged zijn en zodoende niet echte fysieke adressen bevatten. Maar dat ligt meer aan het OS, dan aan de C(++) spec.

Verwijderd

EvilB2k schreef op donderdag 01 mei 2008 @ 21:02:
[...]


Zo word het wel op universiteiten en hogescholen uitgelegd... (om even als student te spreken)

Daarnaast is het zo (zoals al eerder gezegd) dat je ook rekenkundige operaties kan loslaten op je pointers, om door een bepaald stuk werkgeheugen te browsen. Dan mag je toch wel hopen dat je met echte adressen aan het werken bent.
Dat hoeft niet perse.

Even helemaal los van paging in je OS; zou de C++ runtime in principe 2 opeenvolgende adressen kunnen hebben die eigenlijk op verschillende plekken staan. Zodra de user dan de operator ++ op de pointer uitvoert, kan de C++ runtime deze naar het 'zogenaamde' opeenvolgende adres mappen.

De vraag is dus eigenlijk of het volgende altijd gegarandeerd waar is voor elke C++ implementatie:

1) Cast een Object* naar een integer. Voer hier de rekenkundige operatie op uit. Cast de integer terug naar een Object*. Wijst de pointer nu naar het zelfde Object als wanneer je de rekenkundige operatie direct op de Object* had uitgevoerd?

2) Cast een Object* naar een integer. Geef deze integer door aan een stuk assembly code dat in dezelfde process draait. Als deze assembly code de integer direct als geheugen adres gebruikt, krijgt het dan gegarandeerd de eerste byte van het Object terug waar Object* naar wees?

Indien de C++ language spec absoluut garandeert dat deze 2 dingen ongeacht de C++ implementatie altijd moeten werken, dan is het inderdaad zo dat C++ pointers direct corresponderen met fysieke geheugen adressen.

Zoals gezegd, ik weet het niet meer zeker, maar volgens mij geeft C++ je niet deze garantie.

  • Laurens-R
  • Registratie: December 2002
  • Laatst online: 29-12-2024
Verwijderd schreef op vrijdag 02 mei 2008 @ 11:02:
[...]

1) Cast een Object* naar een integer. Voer hier de rekenkundige operatie op uit. Cast de integer terug naar een Object*. Wijst de pointer nu naar het zelfde Object als wanneer je de rekenkundige operatie direct op de Object* had uitgevoerd?

2) Cast een Object* naar een integer. Geef deze integer door aan een stuk assembly code dat in dezelfde process draait. Als deze assembly code de integer direct als geheugen adres gebruikt, krijgt het dan gegarandeerd de eerste byte van het Object terug waar Object* naar wees?
Zoals gezegd, ik weet het niet meer zeker, maar volgens mij geeft C++ je niet deze garantie.
Misschien begrijp ik je verkeerd.... maar als je een pointer cast naar een ander type blijft het gewoon een 32 (of 64) bits adresverwijzing. Neem de volgende pointer:

code:
1
int *pointervar;


Daarmee zeg je in feite: pointervar is een adres waar een integer staat opgeslagen. Als ik dat cast naar een SpecialObject (Wat dus resulteert in SpecialObject *pointervar), dan zal het nog steeds hetzelfde geheugenadres zijn, maar zal de compiler het zo intreperteren dat er op dat adres een SpecialObject staat. De waarde van de pointer zou niet mogen veranderen als je het simpelweg cast naar een ander type, omdat de inhoudelijke data nog steeds op dezelfde plek staat.

Ook leuk: in C++ kan je er ook voor kiezen om geen type aan je pointer te geven. Dan maak je gebruik van een void pointer (void *pointervar). In dit geval zal je wel gebruik moeten maken van casting, om de data alsnog te kunnen interpreteren. Zou ik dan niet gegarandeerd de eerste byte terug krijgen van de originele void pointer, zou ik een probleem hebben.

Als ik de pointer int *pointervar gebruik, kan ik ook vervolgens zeggen:

code:
1
2
3
int *pointervar = (int*)malloc(sizeof(int) * 20); //een pointer naar een ruimte ter grote van 20 ints.

int *var = &pointervar[2];


Deze code zal misschien niet helemaal prettig werken, omdat het geheugen nog niet geinitialiseerd is, maar het gaat om het idee. Hier reserveer ik geheugen ter grote van 20 ints en kan door gebruik te maken van square brackets, het adres achterhalen dat zich 2 integers verderop begint (in het geval van 2 x 32 bit integers, dus het adres van 64 bits verderop in het geheugen). Let ook op, dat ik het cast naar een int pointer, omdat malloc een void pointer retouneerd.

Ik kan bijvoorbeeld ook dit doen:

code:
1
2
3
unsigned char *pointervar = (unsigned char*)malloc(sizeof(unsigned char) * 20); //een pointer naar een ruimte ter grote van 20 unsigned chars.

unsigned char *var = &pointervar[2];


Hier zal je stappen van 8 bits maken. Dus in dit geval zal ik het adres van 2 * 8 bits verderop terug krijgen.

Dit hangt ook enorm af van de memory management strategie achter malloc. Maar je kan (volgens mij) er wel van uit gaan dat malloc over het algemeen continue blokken reserveert.

Ik heb een tijdje terug voor de hobby een mini os'je gemaakt. Ik heb daar testimplementaties van gemaakt met behulp van zowel de GNU als de MS compiler. En bij beide kon ik uitgaan van het gedrag wat ik zojuist beschreven heb. (Wat het schrijven van een malloc functie wel gemakkelijker maakte). Als de compiler namelijk zelf de doorverwijzing intern zou beheren, zou ik nooit een goede memory manager kunnen schrijven (in de vorm van malloc of whatever).

Om even een vergelijking met C# te maken. Normaal gezien kan ik daar dit geintje niet uithalen. (Wellicht in een unsafe context? Iemand?). Omdat daar gebruik wordt gemaakt van references, kan je het wel naar een ander object laten wijzen, maar kan je niet direct aan de pointer zelf gaan sleutelen.

edit:

Ik heb ook nog een tutorial gevonden voor je die dit allemaal uitlegd: http://www.cplusplus.com/doc/tutorial/pointers.html Hierin wordt ook ingegaan op andere onderwerpen zoals pointer arithmatics etc.

edit 2:

Hier een testappje van de voorgaande concepten. Let ook op de testpointer in de loop... het adres wordt hier direct toegewezen aan de pointer. Je ziet ook dat alle element adressen precies 4 bytes van elkaar verwijderd zijn (een 32bits integer dus).

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include "stdio.h"
#include "stdlib.h"

int main(int argc, char *argv[])
{
    int *pointervar = (int*)malloc(sizeof(int) * 20);

    printf("Start van geheugenblok: %p\n\n", pointervar);

    for (int i = 0; i < 20; i++)
    {
        pointervar[i] = i;
        int * testpointer = &pointervar[i];
        printf("waarde bij adres %p: %d\n", &pointervar[i], pointervar[i]);
        printf("testpointer waarde: %p\n\n", testpointer);
    }

    getchar();

    return 0;
}

[ Voor 39% gewijzigd door Laurens-R op 02-05-2008 14:39 ]


  • flowerp
  • Registratie: September 2003
  • Laatst online: 11-09 18:20
EvilB2k schreef op vrijdag 02 mei 2008 @ 11:51:
De waarde van de pointer zou niet mogen veranderen als je het simpelweg cast naar een ander type, omdat de inhoudelijke data nog steeds op dezelfde plek staat.
Dat is niet wat boven in twijfel wordt getrokken. De case is dat een pointer naar een INTEGER (int) wordt gecast, niet naar een pointer naar een integer (int*) en ook niet naar een pointer naar een ander type.

In de computer is een geheugen adres gewoon een getal. Je hebt bijvoorbeeld geheugen adres 100, of geheugen adres 104.

Als je nu een object alloceert in C++ en dat komt fysiek in je virtuele geheugen (dat klinkt tegenstrijdig ja ;) ) op adres 100 terecht, is de waarde van je pointer dan ook echt exact 100? Is het gegarandeerd dat dit waar is voor elke compiler?

Kan ik op deze int (NIET int* dus!!!) berekeningen uitvoeren, terug casten naar een pointer, en dan een zinvol resultaat verwachten?

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • Laurens-R
  • Registratie: December 2002
  • Laatst online: 29-12-2024
flowerp schreef op vrijdag 02 mei 2008 @ 22:21:
[...]
Als je nu een object alloceert in C++ en dat komt fysiek in je virtuele geheugen (dat klinkt tegenstrijdig ja ;) ) op adres 100 terecht, is de waarde van je pointer dan ook echt exact 100? Is het gegarandeerd dat dit waar is voor elke compiler?
Misschien zit ik er helemaal naast... en begrijp ik je niet correct. Maar dit lijkt me eerder een kwestie van memory management op OS niveau? (Deze gaat tenslotte over de manier waarop geheugen gemapped wordt). Misschien wel een hele kromme omweg, maar ik moet in staat zijn om met een compiler een OS van de grond af aan te kunnen maken (gcc bijvoorbeeld). Dan is er niet eens altijd per definitie sprake van een virtueel geheugen (paging etc). Dan kan de compiler daar ook niet van uit gaan.

Maar goed ik weet niet of dit in de specs is opgenomen dus alsnog is dit gespeculeer :P

  • flowerp
  • Registratie: September 2003
  • Laatst online: 11-09 18:20
EvilB2k schreef op vrijdag 02 mei 2008 @ 23:02:
[...]
Misschien zit ik er helemaal naast... en begrijp ik je niet correct.
Ik weet het niet. In C++ kun je natuurlijk wel het geheugen beheren, het enige waar het hier om gaat is of de bits waaruit een pointer bestaat ook 100% gegarandeerd direct het fysieke memory adres zijn of niet.

Als ik b.v. het volgende stukje code draai:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

int main(int argc, char* argv[]) {
    
    int array[3];

    array[0] = 1;
    array[1] = 2;
    array[2] = 3;

    int address1 = reinterpret_cast<int>(&array[0]);
    int address2 = reinterpret_cast<int>(&array[1]);
    int address3 = reinterpret_cast<int>(&array[2]);
    std::cout << "address1:" << address1 << " adress2:" << address2 << " adress3:" << address3;
}


Dan krijg ik met VS 2003 onder Windows (32 bits) het volgende terug:
address1:1244876 adress2:1244880 adress3:1244884
gcc 4.1 onder Linux (32 bits) geeft het volgende:
address1:-1075485828 adress2:-1075485824 adress3:-1075485820
De 3 getallen staan wel telkens op offset 4 van elkaar, maar zijn dit echt de geheugen adressen binnen het proces? gcc geeft negatieve getallen, dus het zijn in ieder geval niet direct geheugen adressen. M.a.w. woorden, ik kan die -1075485828 niet aan een stukje assembly code geven die binnen het zelfde process draait en dan verwachten dat ik iets terug krijg.

Nu moet ik wel toegeven dat mijn C++ en x86 assembly kennis erg verwatert is de afgelopen jaren :'(

edit:

Bij het toevoegen van stukjes inline assembly, direct onder het laatste statement van de code boven, kan ik de int waardes wel degelijk gebruiken om direct de waarden uit de array te benaderen. Dit is natuurlijk geen bewijs dat de standaard de garantie geeft dat het altijd werkt, maar toch:

Voor VS:

C++:
1
2
3
4
5
6
7
8
9
10
    int result = 0;

    __asm {        
        mov eax, address1
        mov eax, [eax]
        mov result, eax
    }

    std::cout << "result: " << result << std::endl;
}


En voor gcc:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
        int result = 0;

        asm (
                "movl %1, %%eax;"
                "movl  (%%eax), %%eax;"
                "movl %%eax, %0;"
                :"=r"(result)
                :"r"(address1)
                :"%eax"
        );

        std::cout << "result: " << result << std::endl;


Beiden geven 1, 2, of 3 weer bij gebruik van resp. variable address1, address2 or address3.

offtopic:
Wat heeft gcc toch nog steeds een vreselijke syntax voor inline asm :(

[ Voor 25% gewijzigd door flowerp op 03-05-2008 00:45 ]

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • Laurens-R
  • Registratie: December 2002
  • Laatst online: 29-12-2024
flowerp schreef op vrijdag 02 mei 2008 @ 23:19:
offtopic:
Wat heeft gcc toch nog steeds een vreselijke syntax voor inline asm :(
offtopic:
Dat is ook 1 van de redenen waarom ik de MS compiler prefereer boven die van GCC... Die AT&T syntax van GCC is gewoon zoooo slecht te lezen t.o.v. de Intel syntax.
...Bij het toevoegen van stukjes inline assembly, direct onder het laatste statement van de code boven, kan ik de int waardes wel degelijk gebruiken om direct de waarden uit de array te benaderen. Dit is natuurlijk geen bewijs dat de standaard de garantie geeft dat het altijd werkt, maar toch...
Interresante bevindingen! :) Wellicht dat ik ergens dit weekend wat tijd vind om zelf ook nog wat testjes te doen.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 08:45

.oisyn

Moderator Devschuur®

Demotivational Speaker

Wat een ontzettend zinloze discussie. Het verschil tussen een pointer en een reference is namelijk afhankelijk van de definitie die je er zelf aan hangt. En aangezien verschillende talen het al niet eens kunnen worden, valt er dus ook weinig te zeggen over enig verschil, zonder een specifieke taal in de context van de discussie op te nemen. En die zie ik niet in de topicstart.

In C++ wordt een pointer gedeclareerd met * en een reference met &. Met pointers kun je rekenen pointers kun je naar een ander object laten wijzen. Een reference is na de initialisatie van die reference eigenlijk gewoon een alias voor dat object waar hij aan refereert.

In .Net is een pointer gelijk aan de C++ pointer in unsafe code. Een reference is dan weer een C++-achtig pointer type die je naar objecten kunt laten wijzen en de verwijzing later aan kunt passen, maar waar je weer niet mee kunt rekenen.

In PHP heb je geen pointers, maar is elke variabele weer een reference, namelijk eentje naar een specifieke locatie een tabel waar alle waarden opgeslagen staan.

Er bestaat geen algemene symantische betekenis voor pointer of reference. In feite zijn het zelfs dezelfde dingen. Ze verwijzen allebij. Meer kun je er niet over zeggen.

Tot slot over pointers in C++ nog even dit: in de praktijk zijn het idd gewoon geheugenadressen, maar dit hoeft niet. De standaard zegt ook alleen maar als je pointers naar int en weer terug naar hetzelfde type cast, dat ze dan gelijk blijven (zolang alle bits van de pointer in het int-type past). En dat pointers die verschillend zijn, ook verschillend zijn in hun int representatie (en andersom). Maar een reinterpret_cast naar een ander pointer-type is undefined behaviour, en met de int-representatie kun je ook niet rekenen zoals je met de pointer-representatie kan. En de int-representatie hoeft ook geen weerspiegeling te zijn van het daadwerkelijke geheugenadres (een conforme implementatie kan net zo goed een XOR doen met 2342384723 bij elke cast van/naar integer type)

[ Voor 28% gewijzigd door .oisyn op 03-05-2008 14:59 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • flowerp
  • Registratie: September 2003
  • Laatst online: 11-09 18:20
.oisyn schreef op zaterdag 03 mei 2008 @ 14:45:
Er bestaat geen algemene symantische betekenis voor pointer of reference. In feite zijn het zelfs dezelfde dingen. Ze verwijzen allebij. Meer kun je er niet over zeggen.
Amen! _/-\o_ Ongeveer het uitgangspunt waarop dit topic denk ik gebaseerd was: flowerp in "Wat kan je nu allemaal met deze talen ?"
En de int-representatie hoeft ook geen weerspiegeling te zijn van het daadwerkelijke geheugenadres (een conforme implementatie kan net zo goed een XOR doen met 2342384723 bij elke cast van/naar integer type)
Ik vermoedde dat ook al, maar mijn C++ kennis was helaas net even iets te ver weggezakt om dit met zekerheid te stellen. Bedankt voor de opheldering.

(wat ik me trouwens ook nog kan herinneren is dat er altijd uitgelegd wordt dat wanneer je een pointer zelf naar cout stuurt ( cout << &someObj ), dat hetgeen uitgeprint wordt implementatie afhankelijk is)

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.

Pagina: 1