C++ incorrect char toevoegen aan string

Pagina: 1
Acties:
  • 201 views sinds 30-01-2008
  • Reageer

  • Dragon
  • Registratie: Oktober 2002
  • Laatst online: 23-06 10:04
Ok ik ben nu al een tijd aan het proberen dit in orde te krijgen
code:
1
2
3
4
5
6
7
8
9
10
#include <iostream>
using namespace std;

int main()
{
  const char * recbuffer;
  recbuffer = "NICK The_Dragon" + (char)(10);
  cout << recbuffer << endl;
  return 0;
}

maar als ik het uitvoer dan is de output 'ragon' en niet 'NICK The_Dragon (ASCII10)'. enig idee hoe ik dit moet/kan oplossen en hoe het veroorzaakt wordt

  • hobbit_be
  • Registratie: November 2002
  • Laatst online: 04-07-2025
je kan geen 'const char *' 'optellen' met een Char. Dat is C#, Java of de meeste script talen maar geen C++.

doe dus liever
code:
1
cout << "nick the dragon" << (char)10;


wat je nou met die char cast wil doen snap ik niet? wil je dat er '10' verschijnt dat werkt dat niet of course...

Verwijderd

strcat is dacht ik wat je nodig had. Je kan beter een string gebruiken, werkt beter.

  • Dragon
  • Registratie: Oktober 2002
  • Laatst online: 23-06 10:04
ik wil dus dat er de char komt netzoals (char)(65)=A

  • Rukapul
  • Registratie: Februari 2000
  • Laatst online: 23-08 14:24
Op de een of andere manier zijn het precies de eerste 10 letters die eraf vallen, waarschijnlijk overschrijf je dus ergens een lengte/positie veld.

Compileert bovenstaande code trouwens zonder problemen? Anders ga ik even het stukje in m'n manual over const nog een keertje lezen :)

Je zou kunnen proberen expliciet std::string te gebruiken voor beide strings, deze te concatteneren en dan c_str() te nemen om te zien of je probleem weggaat. Even opzoeken hoe je een ascii waarde in een char string opneemt lijkt me echter nog handiger.

[ Voor 3% gewijzigd door Rukapul op 25-02-2003 12:10 ]


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 13-08 16:46

curry684

left part of the evil twins

hobbit_be schreef op 25 februari 2003 @ 12:06:
je kan geen 'const char *' 'optellen' met een Char. Dat is C#, Java of de meeste script talen maar geen C++.
Dat kan wel, zal in dit geval opleveren dat er 10 bij die pointer wordt opgeteld waardoor er als output "ragon" uitkomt.
wat je nou met die char cast wil doen snap ik niet? wil je dat er '10' verschijnt dat werkt dat niet of course...
Hij zoekt volgens mij "NICK The_Dragon\n" :)

Professionele website nodig?


  • whoami
  • Registratie: December 2000
  • Laatst online: 11:47
[nohtml]
hobbit_be schreef op 25 February 2003 @ 12:06:
Dat is C#, Java of de meeste script talen maar geen C++.
Bedoel je nu dat C# of Java een scripttaal is?

Afaik is dat echter ook niet mogelijk in C#. Een const moet direct geinitialiseerd worden, een 'readonly' kan je declareren zonder initialisatie, en je kan er dan 1x een waarde aan toewijzen.

In C# kan je bv dit niet:
code:
1
2
const string s;
s = "test";

Dit levert volgende compiler error op:
(214): A const field requires a value to be provided
Dit:
code:
1
2
const string s = "test";
s = s + "test";

Geeft volgende compiler error:
(215): The left-hand side of an assignment must be a variable, property or indexer

[ Voor 32% gewijzigd door whoami op 25-02-2003 12:16 ]

https://fgheysels.github.io/


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

C++:
1
2
std::string blaat = "bliep";
blaat += '\n';

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.


  • Dragon
  • Registratie: Oktober 2002
  • Laatst online: 23-06 10:04
laat ik het anders stellen ik heb ASCII code 65 en die wil ik er achter plakken als A (65=A) dat ik dan 'NICK The_DragonA' krijg... terwijl ik alleen de ASCII code heb

[ Voor 29% gewijzigd door Dragon op 25-02-2003 12:20 ]


  • _js_
  • Registratie: Oktober 2002
  • Laatst online: 13-01 07:19
"Nick The_Dragon\x0a" of "Nick The_Drago\012"

\x## is een hexadecimaal nummer, \### is een octaal nummer.

  • Dragon
  • Registratie: Oktober 2002
  • Laatst online: 23-06 10:04
Ok heb nu dit
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <stdio.h>
using namespace std;

int main()
{
  char recbuffer[256];
  strcpy (recbuffer,"NICK Dragon");
  strcat (recbuffer,"\x0a");
  strcat (recbuffer,"\x0d");
  cout << recbuffer << endl;
  return 0;
}

en het lijkt perfect te werken thnx

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 23-08 21:27

Creepy

Tactical Espionage Splatterer

The-Dragon schreef op 25 February 2003 @ 12:34:
Ok heb nu dit
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <stdio.h>
using namespace std;

int main()
{
  char recbuffer[256];
  strcpy (recbuffer,"NICK Dragon");
  strcat (recbuffer,"\x0a");
  strcat (recbuffer,"\x0d");
  cout << recbuffer << endl;
  return 0;
}

en het lijkt perfect te werken thnx
0xa en 0xd.. als in CR + LF?? dan heb je aan een \n genoeg zoals al eerder was uitgelegd.

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • Dragon
  • Registratie: Oktober 2002
  • Laatst online: 23-06 10:04
ja ok maar ik heb het voor meer nodig... :D
dus vandaar dat ik verder vroeg

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

0x0a = \n = LF, dus nu doe je \n\r, wat verkeerd is. Onder windows is dat \r\n, dus 0x0d 0x0a

Maar goed, wat is nou je argument om geen std::string te gebuiken?

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.


  • hobbit_be
  • Registratie: November 2002
  • Laatst online: 04-07-2025
to whoami:

dat van C# wist ik niet : wel proper. Dat C# nou een script taal of 'programmeer' taal is: geen idee ergens ertussen (net zoals Java). Eerlijk gezegd vind ik het onderscheid steeds meer vervagen... Bestaat daar ergens een 'harde' definitie over?. In feite is ASM de enige echte programeer taal (aangezien alle andere uiteindelijk toch tot dat nivieau worden gebracht) (en eigenlijk nog de machine code daaronder)...

  • whoami
  • Registratie: December 2000
  • Laatst online: 11:47
hobbit_be:
Ik heb geen zin om een discussie te gaan voeren over wat een scripttaal en wat een programmeertaal is, of wat de definitie van een scripttaal is (er zijn heir wel topics over), maar Java en C# zijn wel echt volwaardige programmeertalen. Ze zijn strong typed, compiled languages.

https://fgheysels.github.io/


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 21-08 17:14
The-Dragon schreef op 25 February 2003 @ 11:59:
Ok ik ben nu al een tijd aan het proberen dit in orde te krijgen
code:
1
2
3
4
5
6
7
8
9
10
#include <iostream>
using namespace std;

int main()
{
  const char * recbuffer;
  recbuffer = "NICK The_Dragon" + (char)(10);
  cout << recbuffer << endl;
  return 0;
}

maar als ik het uitvoer dan is de output 'ragon' en niet 'NICK The_Dragon (ASCII10)'. enig idee hoe ik dit moet/kan oplossen en hoe het veroorzaakt wordt
code:
1
2
3
4
5
6
7
#include <iostream>
int main()
{
  std::string recbuffer = std::string( "NICK The_Dragon" )+ char(10);
  cout << recbuffer << endl;
  return 0;
}

Het type van een "" is const char*. Als je bij zo'n pointer een getal optelt, zoals (int)(char)(10) /*automatisch teruggecast */, dan krijg je een pointer 10 posities verder. Bij "ragon" dus. Dit werkt net zoals in C. Het is wel handig voor getal->hex conversies:
*("012346789ABCDEF" + i) geeft het hex digit voor i.

std::string heeft een normale operator+( char ).

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • Dragon
  • Registratie: Oktober 2002
  • Laatst online: 23-06 10:04
Ok ben nu klaar na heel wat klooien... de implementatie... het is gestript...

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <string>

using namespace std;

int sendmessage(string strsend,int socknum);

int main()
{
  string sendbuffer="NICK Dragon";
  sendmessage(sendbuffer,0);
  return 0;
}

int sendmessage(string strsend,int socknum)
{
  char strbuffer[256];
  strncpy(strbuffer,strsend.c_str(),255);

  strcat (strbuffer,"\x0a");
  strcat (strbuffer,"\x0d");

  cout << strbuffer;
  return 0;
}

[ Voor 21% gewijzigd door Dragon op 25-02-2003 17:39 ]


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Waarom zet je sendmessage niet boven je main?
Waarom niet strcat(strbuffer, "\n\r")? En waarom "\n\r" in plaats van "\r\n"?
Als de invoer van sendmessage 254 tekens of langer is heb je trouwens een buffer overflow.

[ Voor 36% gewijzigd door Olaf van der Spek op 25-02-2003 17:16 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 22-08 01:56
Argh! Je krijgt nota bene een string argument mee in je functie!!! Gebruik dan gewoon die operator+! Zo dus:

C++:
1
2
3
4
5
int sendmessage(const string &strsend, int socknum)
{
    cout << (strsend + "\r\n");
    return 0;
}


Ik neem dan even aan dat je 'cout' tijdelijk is, anders kun je natuurlijk net zo goed direct cout << strsend << "\r\n" doen, zonder een overbodige concatenatie uit te voeren. Let er ook op dat je objecten doorgaans by const-reference doorgeeft, tenzij er een goede reden is om dit niet te doen. Dit voorkomt onnodige kopiën.

  • hobbit_be
  • Registratie: November 2002
  • Laatst online: 04-07-2025
Soultaker schreef op 25 February 2003 @ 17:27: Let er ook op dat je objecten doorgaans by const-reference doorgeeft, tenzij er een goede reden is om dit niet te doen. Dit voorkomt onnodige kopiën.
volgens Stroustrop is het nog beter een pointer mee te geven:

C++:
1
2
3
4
void aFunc(const string* aString)
{
   cout << *aString;
}


alleen voor het feit dat de user van de functie weet dat het pass-by-reference is. In dit geval van const is dat transparent maar niet als je je de const weglaat en dus het onduidelijk wordt dat de string aangepast kan worden.

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 22-08 01:56
hobbit_be schreef op 25 februari 2003 @ 17:37:
volgens Stroustrop is het nog beter een pointer mee te geven:

C++:
1
2
3
4
void aFunc(const string* aString)
{
   cout << *aString;
}


alleen voor het feit dat de user van de functie weet dat het pass-by-reference is. In dit geval van const is dat transparent maar niet als je je de const weglaat en dus het onduidelijk wordt dat de string aangepast kan worden.
Nadeel hiervan is natuurlijk dat als je later wel een kopie wilt maken (omdat je er bijvoorbeeld eerst iets aan toe wilt voegen) dat niet meer kan, zonder ook al je code aan te passen (*-tekentjes weghalen en . vervangen door ->). Ik doe het dus gewoon eigenwijs met een reference. :P

  • Dragon
  • Registratie: Oktober 2002
  • Laatst online: 23-06 10:04
Om een of andere vage reden vraagt de functie die ik gebruik vraagt om \n\r op het eind als een char. De rest kan ik wel mee in komen...

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
hobbit_be schreef op 25 February 2003 @ 17:37:
alleen voor het feit dat de user van de functie weet dat het pass-by-reference is. In dit geval van const is dat transparent maar niet als je je de const weglaat en dus het onduidelijk wordt dat de string aangepast kan worden.
Persoonlijk vind ik references toch beter. Je hoeft niet te controleren op een NULL-pointer, de caller hoeft geen & te gebruiken en als je later alsnog de parameter by-value wilt doorgeven merkt de caller daar niks van (op source level).

  • hobbit_be
  • Registratie: November 2002
  • Laatst online: 04-07-2025
Soultaker schreef op 25 February 2003 @ 17:40:
Nadeel hiervan is natuurlijk dat als je later wel een kopie wilt maken (omdat je er bijvoorbeeld eerst iets aan toe wilt voegen) dat niet meer kan, zonder ook al je code aan te passen (*-tekentjes weghalen en . vervangen door ->). Ik doe het dus gewoon eigenwijs met een reference. :P
idd :) ik gebruik ook vaak een:

C++:
1
2
#define IN_OUT
void aFunc(IN_OUT string& aString);


komt het lekker duidelijk in mijn 'Visual Assist' :) ik denk dat zoiets ook in C# zit en dat het daar ook nog echts iets doet :))

  • hobbit_be
  • Registratie: November 2002
  • Laatst online: 04-07-2025
OlafvdSpek schreef op 25 February 2003 @ 17:42:
[...]

Persoonlijk vind ik references toch beter. Je hoeft niet te controleren op een NULL-pointer, de caller hoeft geen & te gebruiken en als je later alsnog de parameter by-value wilt doorgeven merkt de caller daar niks van (op source level).
fair enough: maar 1) controleren op null pointer op dat niveau doe je met een ASSERT in je debug versie, 2) het moeten gebruiken van & is nou net de bedoeling omdat het ineens duidelijk wordt (er zijn redenen waarom de Windows API dit ook altijd doet), 3) idd maar je interface moet van de eerste keer juist zijn.

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 21-08 17:14
Sorry, maar juist voor const& is het nou net de bedoeling dat de gebruiker het niet merkt (snelheid is een implementatie detail).

Zo kun je de const char* "Hello, world" meegeven aan een std::string /*by value*/ of aan een std::string const& /* by reference */, maar niet aan een std::string const* /*rvalue heeft geen adres */

de Windows API is een C API. Die heeft simpelweg geen references, dat is noodgedwongen een pointer interface.

De ASSERT is bovendien niet toereikend. Een compiler kan zelf betere code genereren (en GCC doet dat al) als die een reference ziet, alleen omdat die nooit nul kan zijn. Na een ASSERT kan een pointer toch nul zijn (nl. release mode, ASSERT is dan verwijderd door preprocessor).

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • hobbit_be
  • Registratie: November 2002
  • Laatst online: 04-07-2025
nou dat van die C-API ben ik zowaar moeten gaan opzoeken :) Pascal->C++ dude dus _/-\o .. ik denk dat het meer een kwestie van gewoonte is voor de rest: ik gebruik std::string niet bijvoorbeeld (net iets trager dan mijn reference-counted, copy-on-write geval) en die is steeds een object (geen local, maar heap - long story maar ik doe altijd alles met objects geen structs...). en Assert is idd niet voor release maar dat is ook ineens de bedoeling... je gaat toch niet telkens bij het gebruik van elke pointer eerst kijken of ie NULL staat??? je code moet gewoon op een bepaald punt zeggen vanaf hier moet het goed zijn (hence de asserts in debug...) ... maar weliswaar weer wat bijgeleerd :)

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 22-08 01:56
hobbit_be schreef op 25 February 2003 @ 19:15:
ik gebruik std::string niet bijvoorbeeld (net iets trager dan mijn reference-counted, copy-on-write geval)
Niemand verbiedt je om de meest efficiente implementatie van de string klasse te gebruiken die je kan vinden, hoor. Zelf als je 'm zelf geschreven hebt.
en Assert is idd niet voor release maar dat is ook ineens de bedoeling... je gaat toch niet telkens bij het gebruik van elke pointer eerst kijken of ie NULL staat???
Voor mijn gevoel is dat wel een beetje de betekenis van een pointer. In een reference mag je NOOIT een 0-pointer stoppen (kan wel per ongelijk gebeuren, natuurlijk, maar dat is gegarandeerd fout). Het kan dus geen kwaad om voor optionele parameters (die 0 mogen zijn) een pointer te gebruiken en voor verplichte een reference.

Daarbij vind ik MSAlter's opmerking over automatische constructie van temporaries ook op z'n plaats: dat werkt gewoon (terecht) niet voor pointers. Dat zijn een variant op domme integers en missen vaak handige functionaliteit.

Tenslote wordt ik zelf altijd gek van het gebruik van * en -> overal, zeker als het type wat doorgegeven wordt van een iterator/container-achtig type is en je dus expliciet je pointer eerst moet dereferences voordat je 'm kunt gebruiken. Ik doe dat noodgedwongen vaak in C, maar ik vind het juist fijn dat dat in C++ niet meer hoeft.
je code moet gewoon op een bepaald punt zeggen vanaf hier moet het goed zijn (hence de asserts in debug...) ... maar weliswaar weer wat bijgeleerd :)
Daar ben ik het in principe mee eens. Ik neem zelf echter vaak wel de extra moeite om functies die "naar buiten toe" bekend zijn wat extra checking op invoer te laten doen, zeker als die relatief ingewikkelde dingen moeten doen. Om performance redenen kun je die checks dan nauwelijks achter wege laten en het kan veel ergernissen met debuggen besparen. Mijn ervaring met andere custom drivers/API's is vaak, dat je als gebruikende programmeur niet altijd een duidelijk beeld hebt van wat wel en niet de bedoeling is.

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 21-08 17:14
hobbit_be schreef op 25 February 2003 @ 19:15:
pointers vs references, null check
Assert is idd niet voor release maar dat is ook ineens de bedoeling... je gaat toch niet telkens bij het gebruik van elke pointer eerst kijken of ie NULL staat??? je code moet gewoon op een bepaald punt zeggen vanaf hier moet het goed zijn (hence de asserts in debug...)
Ja, jij weet wel dat die pointer vanaf een bepaald punt geen 0 is. Ik zei dat de compiler dat niet weet. De compiler neemt bij een pointer bijna altijd aan dat deze 0 kan zijn, daar helpt geen assert aan. De consequentie is dat bijvoorbeeld de volgende pointer code langzamer is dan de reference code:
code:
1
2
3
4
5
void foo( MyDerived*a , MyDerived& b )
{
  MyBase * ba = a;
  MyBase & bb = b;
}

De code voor pointers moet het geval a==0 meenemen, dan moet ba ook
0 worden. (Conditional Jump in assembly, en eventueel assignment). De reference cast is simpelweg een offset adjustment. Dit scheelt dus twee instructies.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • hobbit_be
  • Registratie: November 2002
  • Laatst online: 04-07-2025
wel dat is out of my league wat de compiler doet. van mijn assembly dagen verwacht ik gewoon:

code:
1
2
3
4
5
6
7
mov ba,  a;   //[mem, mem] ba,bb can be local heap dus esi of edi eg
mov bb,  b;   //a, b kunnen natuurlijk ook optimised pass as eax, ebx eg
//doe iets met beide = same
mov esi, ba //ba is een pointer maw een offset (32bit flat assumed)
mov [esi], 0 //oftwel *ba = 0; 
mov edi, bb //bb is een reference, maar das ook maar een offset
mov [edi], 1 //bb = 0;


maar ik neem aan dat je bedoelt dat de assert er staat? dan neem ik aan dat je ASSERT idd wordt weggevaagd (net zoasl (ASSERT(sizeof(char)==1), maar ik zie niet in waar je die a==0 condition vandaal haalt? please explain misschien ander topic... maar zelfs als em die (a==0) ergens zou doen het ging er me (Bjourne) er speciaal om dat als je een reference aan server-side (weet niet hoe het anders te zeggen) dat het dan niet direct duidelijk is aan client side of je variable nu verandered word of niet. daarom raad ie het gebruik van de & aan client side aan... maar wil graag weten hoe dat nu zit met die a==0 want vroeger deed ik nogal wat assembly aangezien Pascal and watcom c++ niet altijd de snelheid haalt die ik wou...

  • ^Mo^
  • Registratie: Januari 2001
  • Laatst online: 04-11-2025
hobbit_be schreef op 25 February 2003 @ 17:37:
[...]


volgens Stroustrop is het nog beter een pointer mee te geven:

C++:
1
2
3
4
void aFunc(const string* aString)
{
   cout << *aString;
}


alleen voor het feit dat de user van de functie weet dat het pass-by-reference is. In dit geval van const is dat transparent maar niet als je je de const weglaat en dus het onduidelijk wordt dat de string aangepast kan worden.
Stroustroup raadt de pointer notatie aan als de waarde veranderd moet worden omdat dit via een reference niet direct duidelijk is vanuit de function call. Als het een const reference is, dan is er in principe geen probleem.
A reference can be used to specify a function argument so that the function can change the value of an object passed to it.
[...]
To keep the program readable, it is often best to avoid functions that modify their arguments. Instead, you can return a value from the function explicitly or require a pointer argument.

"There are 10 kinds of people in the world, those who understand binary and those who don't" | Werkbak specs


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

hobbit_be schreef op 26 februari 2003 @ 01:37:
maar ik zie niet in waar je die a==0 condition vandaal haalt? please explain misschien ander topic...


Een Derived * hoeft niet naar hetzelfde adres te wijzen als Base *

voorbeeld:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct Base1 { int i; };
struct Base2 { int j; };

struct Derived : public Base1, public Base2 { };


int main ()
{
    Derived * d = reinterpret_cast<Derived *> (0x1000);   // d = 0x1000
    Base1 * b1 = &d;  // b1 = 0x1000
    Base2 * b2 = &d;  // b2 = 0x1004


    // dus op dezelfde wijze:
    d = NULL;
    b2 = &d;  // het is de bedoeling dat b2 ook NULL is, en niet 0x0004
    // dus de compiler moet code genereren die een NULL check doet
}


de waarden hierboven zijn wel fictief. Het zal in het algemeen wel kloppen (dus dat Base1 op Derived ligt, en Base2 op Derived+sizeof (int)), maar dat hoeft dus niet per se

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.


  • madwizard
  • Registratie: Juli 2002
  • Laatst online: 26-10-2024

madwizard

Missionary to the word of ska

MSalters schreef op 25 February 2003 @ 21:38:
[...]
Ja, jij weet wel dat die pointer vanaf een bepaald punt geen 0 is. Ik zei dat de compiler dat niet weet. De compiler neemt bij een pointer bijna altijd aan dat deze 0 kan zijn, daar helpt geen assert aan. De consequentie is dat bijvoorbeeld de volgende pointer code langzamer is dan de reference code:
code:
1
2
3
4
5
void foo( MyDerived*a , MyDerived& b )
{
  MyBase * ba = a;
  MyBase & bb = b;
}

De code voor pointers moet het geval a==0 meenemen, dan moet ba ook
0 worden. (Conditional Jump in assembly, en eventueel assignment). De reference cast is simpelweg een offset adjustment. Dit scheelt dus twee instructies.
Een beetje compiler maakt van deze functie 1 instructie en wel een ret want uiteindelijk doet de functie niks ;). Maar aangenomen dat je ook nog wat met die pointers en rerferences gaat doen, leveren beide regels soortgelijke instructies op. Een NULL pointer is niks bijzonders, een adres is gewoon een waarde die dus ook 0 kan zijn. Het enige wat de comiler hoeft te doen is a direct gebruiken of eventueel in een register stoppen, hangt af van de context en optimalisatiekeuzes.
Volgens mij zijn references en pointers qua implementatie exact het zelfde, het verschil zit hem in de notatie en het gebruik. Het gebruik voorkomt (zolang je geen trucs uithaald) dat je een 0 reference kan hebben zodat je op implementatieniveau daar geen rekening mee hoeft te houden.

www.madwizard.org


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

madwizard: zie mijn vorige post, het is wel degelijk van belang dat een pointer op NULL wordt gechecked als je m cast in het geval dat er pointer arithmetic nodig is voor de cast (dus als de base en de derived klassen zich niet op hetzelfde adres bevinden)

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.


  • madwizard
  • Registratie: Juli 2002
  • Laatst online: 26-10-2024

madwizard

Missionary to the word of ska

.oisyn schreef op 26 February 2003 @ 11:11:
madwizard: zie mijn vorige post, het is wel degelijk van belang dat een pointer op NULL wordt gechecked als je m cast in het geval dat er pointer arithmetic nodig is voor de cast (dus als de base en de derived klassen zich niet op hetzelfde adres bevinden)
Ah ja, die cast had ik over het hoofd gezien |:( .. dan is het idd wel belangrijk.

www.madwizard.org


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
hobbit_be schreef op 25 February 2003 @ 19:15:
nou dat van die C-API ben ik zowaar moeten gaan opzoeken :) Pascal->C++ dude dus _/-\o
Dat een calling convention wordt gebruikt die toevallig eerder bij Pascal werd gebruikt wil toch niet zeggen dat het een Pascal API is?
En als iets een Pascal API is, wat heeft dat dan met C++ te maken?

  • madwizard
  • Registratie: Juli 2002
  • Laatst online: 26-10-2024

madwizard

Missionary to the word of ska

Wel grappig dan trouwens dat (in ieder geval VC) wel op 0 references controleert voordat ie gaat casten. Op zich wel netjes natuurlijk maar onnodig als de programmeur tenminste kan programmeren ;)
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Base1 { int i; }; 
struct Base2 { int j; }; 
struct Derived : public Base1, public Base2 { }; 

void usePtr(const Base2 *ref) {}
void useRef(const Base2 &ref) {}

void foo(Derived *a , Derived &b)
{
    Base2 *ba = a;
    Base2 &bb = b;
    usePtr(ba);
    useRef(bb);
}

void main()
{
    Derived d1;
    foo(&d1, d1);
}

Die usePtr en useRef zijn nodig om te voorkomen dat de compiler de hele foo functie wegoptimaliseerd.
De foo functie resulteert in de volgende code:
GAS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
00401020 mov    eax, [esp+4]    ; eax = a
00401024 test   eax, eax        ; a==0?
00401026 je     0040102D        ; 0 pointer, laat 0
00401028 lea    ecx, [eax+4]    ; ecx = eax + 4 (Derived->Base2)
0040102B jmp    0040102F        ; ecx is ba
0040102D xor    ecx, ecx        ; bij 0 ptr wordt ecx (ba) ook 0
0040102F mov    eax, [esp+8]    ; eax = b
00401033 push   esi             ; bewaar esi
00401034 test   eax, eax        ; eax==0 (0 reference!!)
00401036 je     0040104C        ; 0 reference, laat 0
00401038 push   ecx             ; ref argument voor usePtr
00401039 lea    esi, [eax+4]    ; esi = eax + 4 (Derived->Base2)
0040103C call   00401000        ; usePtr(ecx)
00401041 push   esi             ; ref argument voor useRef
00401042 call   00401010        ; useRef(esi)
00401047 add    esp, 8          ; stack aanpassen voor beide calls
0040104A pop    esi             ; bewaarde esi terug
0040104B ret                    ; return

(Dit is release code dus geen extra controle in debug mode ofzo)
Hij controleert dus bij zowel de pointer als de reference op 0 voor de cast.

[ Voor 5% gewijzigd door madwizard op 26-02-2003 21:54 ]

www.madwizard.org


  • hobbit_be
  • Registratie: November 2002
  • Laatst online: 04-07-2025
hoedje af :) nooit geweten...

ik neem aan dat ie dat dus ook moet doen bij een

C++:
1
  Base1* b1 = d; 


maar is dit alleen betrekbaar op een multiple inheritance? (want dat wordt dan ook weer afgeraden :) Effective C++). Ik neem aan dat dit ook nodig is bij single inheritence consistent is...

Olaf:

ik wou gewoon zeggen dat ik van Pascal naar C++ ben overgestapt zonder pure C aan te raken. Dat er dus een reference bestond in C heb ik al heel mijn programmeer 'carriere' vanzelfsprekend genomen - tot gisteren.

Mad-wizard:

wou net effe die stap genomen om het te controleren - kick-ass. zou de intel-compiler (of GCC) dit ook doen?

[ Voor 39% gewijzigd door hobbit_be op 26-02-2003 12:07 ]


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 23-08 16:32
hobbit_be schreef op 25 February 2003 @ 17:37:
volgens Stroustrop is het nog beter een pointer mee te geven
Stroustrup zegt dat het beter( duidelijker ) is een pointer mee te geven als je het argument wilt aanpassen

In andere gevallen een const reference.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • madwizard
  • Registratie: Juli 2002
  • Laatst online: 26-10-2024

madwizard

Missionary to the word of ska

hobbit_be schreef op 26 February 2003 @ 12:02:
Mad-wizard:
wou net effe die stap genomen om het te controleren - kick-ass. zou de intel-compiler (of GCC) dit ook doen?
GCC doet het in ieder geval niet, hier is de gegenereerde code:
GAS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
00401254 sub    esp, 18h        ; stackframe (waarom weet ik ook niet?)
00401257 push   ebx             ; bewaar ebx
00401258 xor    edx, edx        ; edx = 0 (nutteloos?)
0040125A mov    eax, [esp+20h]  ; eax = a
0040125E test   eax,eax         ; a==0?
00401260 je     00401265        ; 0 pointer, laat 0
00401262 lea    edx, [eax+4]    ; edx = eax + 4 (Derived->Base2)
00401265 add    esp, -0Ch       ; esp -= 0xC
00401268 push   edx             ; ref argument voor usePtr
; hier komt de reference cast zonder null check:
00401269 mov    ebx, [esp+34h]  ; ebx = b
0040126D add    ebx,4           ; ebx += 4 (Derived->Base2)
00401270 call   00401288        ; usePtr(edx)
00401275 add    esp, -0Ch       ; esp -= 0xC
00401278 push   ebx             ; ref argument voor useRef
00401279 call   004012A8        ; useReg(ebx)
0040127E add    esp, 20h        ; stack aanpassen
00401281 pop    ebx             ; ebx terug
00401282 add    esp, 18h        ; weg stackframe
00401285 ret                    ; return

Ik moest wel de procs in aparte source files zetten om te voorkomen dat gcc nogal aggresief ging inlinen tot er uiteindelijk niks overbleef want het programma doet nog steeds niks. Visual C gaat standaard geen dingen inlinen dus daar kon het wel in een file. Wat gcc allemaal uithaald met het stackframe en esp (stackframe is niet eens nodig, er zijn geen locals) weet ik niet :?. Heb een zootje optimalisatie flags aangezet maar ben niet heel bekend met gcc dus misschien ben ik iets vergeten. Maakt voor de test niet uit maar maakt de code wel een beetje gaar. De intel compiler heb ik niet dus ook niet getest.

www.madwizard.org


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 21-08 17:14
GCC code is wel veelzeggend; de extra code voor de null-pointer is een initialisatie van edx, een test van eax en een conditional jump.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • madwizard
  • Registratie: Juli 2002
  • Laatst online: 26-10-2024

madwizard

Missionary to the word of ska

MSalters schreef op 26 februari 2003 @ 21:00:
GCC code is wel veelzeggend; de extra code voor de null-pointer is een initialisatie van edx, een test van eax en een conditional jump.
Ik vind die gcc code maar vreemd, esp hoeft nergens aangepast te worden behalve na de calls naar useRef en usePtr (2x +4 of 1x +8), verder niet. De gcc code haalt van alles uit met esp maar het nut ervan ontgaat me.
Ook is de extra code voor de null-pointer niet initialisatie van edx want die instructie is eveneens nutteloos. edx wordt verder nergens gebruikt, ja bij lea edx, [eax+4] maar dat is niet afhankelijk van de waarde van edx (tis gewoon eax+4 meer niet). Zoals ik al zei gebruik ik gcc bijna nooit dus kan ook aan verkeerde instellingen liggen maar het blijft vreemd.
Maar je hebt gelijk dat een null pointer (in gcc) dus meer code nodig heeft, al is het slechts alleen bij base classes die in adres verschillen ten opzichte van de derived class. In ieder geval een test en een conditional jump, of een cmove op PPro+ (xor edx, edx / test eax, eax / lea eax, [eax+4] / cmove eax, edx) die gooit de pipleline niet leeg bij een misprediction (misschien dat gcc daarom edx leeggooit maar de cmove vergeten was ;) ). In visual C maakt het dus allemaal niet uit, op zich is een extra null check wel veiliger maar uiteindelijk zal je toch ergens vastlopen met een null reference (in gcc zou je dus vastlopen met een 4-reference :))

www.madwizard.org


Verwijderd

madwizard schreef op 26 February 2003 @ 18:20:
Ik moest wel de procs in aparte source files zetten om te voorkomen dat gcc nogal aggresief ging inlinen tot er uiteindelijk niks overbleef want het programma doet nog steeds niks. Visual C gaat standaard geen dingen inlinen dus daar kon het wel in een file.
-fno-inline |:( gcc gaat overigens ook niet standaard inlinen, dat doet hij pas vanaf -O2, net als VC...
Wat gcc allemaal uithaald met het stackframe en esp (stackframe is niet eens nodig, er zijn geen locals) weet ik niet :?. Heb een zootje optimalisatie flags aangezet maar ben niet heel bekend met gcc dus misschien ben ik iets vergeten. Maakt voor de test niet uit maar maakt de code wel een beetje gaar. De intel compiler heb ik niet dus ook niet getest.
Je optimalisatieopties zijn vooral ook een beetje gaar. Gebruik eens geen "-mpreferred-stack-boundary" als je die stackoperaties niet wil, ze zorgen er echter wel voor dat stack-access sneller gaat. Waarom gebruik je uberhaupt "-fomit-frame-pointer" in je code (dan kun je je code nog amper debuggen), zolang je met ontwikkelen bezig bent zou ik je aanraden gewoon -O2 te gebruiken en meer niet, en als je zelfs helemaal geen inlining wilt, gebruik dan -O1 of -Os of zo...

  • madwizard
  • Registratie: Juli 2002
  • Laatst online: 26-10-2024

madwizard

Missionary to the word of ska

Ik zei al, ik gebruik gcc nauwelijks, heb het dus maar even anders opgelost.
|:( gcc gaat overigens ook niet standaard inlinen, dat doet hij pas vanaf -O2, net als VC...
Bij mijn weten gaat VC nooit zelf inlinen als een functie niet inline is, tenzij je /Ob2 gebruikt en die staat standaard uit. gcc begon alles inline te maken en dat wilde ik juist niet omdat de test dan niet goed ging. De foo functie moest juist helemaal losstaan.
Je optimalisatieopties zijn vooral ook een beetje gaar. Gebruik eens geen "-mpreferred-stack-boundary" als je die stackoperaties niet wil, ze zorgen er echter wel voor dat stack-access sneller gaat.
Die heb ik niet gebruikt. Standaard staat ie op 4 dacht ik, volgens mij zorgt die er dan alleen maar voor dat de stack aligned blijft op 32-bit boundaries, maar wat in dat stukje code gebeurd heeft daar niet veel mee te maken volgens mij. Alignment is inderdaad goed voor data access maar aangezien hier het enige stack gebruik de 2 pushes en het gebruik van de parameters is hoeft er niks extra's te gebeuren om de stack aligned te houden (op 4 bytes is ie altijd al aligned in windows). Die adds en subs in dit stukje code vertragen het alleen maar.
Waarom gebruik je uberhaupt "-fomit-frame-pointer" in je code (dan kun je je code nog amper debuggen), zolang je met ontwikkelen bezig bent zou ik je aanraden gewoon -O2 te gebruiken en meer niet, en als je zelfs helemaal geen inlining wilt, gebruik dan -O1 of -Os of zo...
Dit was ook geen project van me ofzo, het moet juist geen debug versie zijn. Het ging me er alleen om uit te vinden of de compiler checkt op null references. Alleen wilde ik zoveel mogelijk optimalisaties aan en debug dingen uit, visual C voegt ook een hoop extra checks (bijvoorbeeld voor de stack pointer) uit in de debug build.
In gcc heb ik dat maar geprobeerd met wat opties die ik zag staan, ik ken visual C veel beter dus daar weet ik precies wat ik wel en niet moet aanzetten. Ben wel benieuwd wat gcc ervan maakt met de 'goede' instellingen. Die xor edx, edx bijvoorbeeld is me nog steeds een raadsel en dat gedoe met esp ziet er toch ook niet echt uit als optimalisatie.

www.madwizard.org


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

hij aligned de stack blijkbaar op 16 bytes, aangezien ie er eerst 12 aftrekt, en daarna nog eens 4 bytes op pushed

die xor edx, edx is nodig om de pointer 0 te laten zijn. De conditionele jump springt ook voorbij de lea edx, [eax+4]. Dus als de pointer 0 is moet edx 0 zijn, anders wordt edx eax+4

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.


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Maar wat is het nut van de eerste en laatste operaties op esp dan?

Verwijderd

madwizard schreef op 27 February 2003 @ 17:19:
Bij mijn weten gaat VC nooit zelf inlinen als een functie niet inline is, tenzij je /Ob2 gebruikt en die staat standaard uit. gcc begon alles inline te maken en dat wilde ik juist niet omdat de test dan niet goed ging. De foo functie moest juist helemaal losstaan.
gcc gaat ook "nooit zelf inlinen als een functie niet inline is", tenzij je -O2 (optimalisatie level 2, AKA /O2 voor VC)
Die heb ik niet gebruikt. Standaard staat ie op 4 dacht ik, volgens mij zorgt die er dan alleen maar voor dat de stack aligned blijft op 32-bit boundaries, maar wat in dat stukje code gebeurd heeft daar niet veel mee te maken volgens mij. Alignment is inderdaad goed voor data access maar aangezien hier het enige stack gebruik de 2 pushes en het gebruik van de parameters is hoeft er niks extra's te gebeuren om de stack aligned te houden (op 4 bytes is ie altijd al aligned in windows). Die adds en subs in dit stukje code vertragen het alleen maar.
Misschien heb je wel dan een "-march=..." gebruikt, die zet ook deze optimalisatie.
Dit was ook geen project van me ofzo, het moet juist geen debug versie zijn. Het ging me er alleen om uit te vinden of de compiler checkt op null references. Alleen wilde ik zoveel mogelijk optimalisaties aan en debug dingen uit, visual C voegt ook een hoop extra checks (bijvoorbeeld voor de stack pointer) uit in de debug build.
In gcc heb ik dat maar geprobeerd met wat opties die ik zag staan, ik ken visual C veel beter dus daar weet ik precies wat ik wel en niet moet aanzetten. Ben wel benieuwd wat gcc ervan maakt met de 'goede' instellingen. Die xor edx, edx bijvoorbeeld is me nog steeds een raadsel en dat gedoe met esp ziet er toch ook niet echt uit als optimalisatie.
Als je met VStudio een debug build maakt, worden er ook geen dingen gedaan als "-fomit-frame-pointer" en -O2 e.d. Dan wordt het geheel ook zo clean mogelijk gecompileert, zonder teveel poespas. Wat ik niet helemaal snap is dat je met VC wel een debug build doet, maar met gcc niet :? Wat is de logica daar achter?

Maar ik vind ook dat gcc best had kunnen kiezen om "a" in edx te zetten, deze op nul te checken, en dan een conditionele move met [edx + 4] naar edx, dan had edx niet nog eens nul gemaakt hoeven worden. En de logica achter die esp += 0x18 snap ik ook niet, ik snap esp += 8. (al is al het alignen in veel gevallen helemaal niet nodig...)

Maar gcc doet wel meer rare dingen, zoals een for loop in een while met dezelfde conditie, daar wordt vaak ook twee keer hetzelfde getest voor een conditionele jump naar hetzelfde |:(

  • madwizard
  • Registratie: Juli 2002
  • Laatst online: 26-10-2024

madwizard

Missionary to the word of ska

.oisyn schreef op 27 February 2003 @ 17:42:
die xor edx, edx is nodig om de pointer 0 te laten zijn. De conditionele jump springt ook voorbij de lea edx, [eax+4]. Dus als de pointer 0 is moet edx 0 zijn, anders wordt edx eax+4
Arrghh stom |:( ja... Visual C deed precies hetzelfde natuurlijk (gaf zelfs zelf nog het voorbeeld van cmove met xor edx,edx). Vond het al vreemd dat een compiler nutteloze instructies niet zou weghalen :)
Verwijderd schreef op 27 February 2003 @ 18:12:
gcc gaat ook "nooit zelf inlinen als een functie niet inline is", tenzij je -O2 (optimalisatie level 2, AKA /O2 voor VC)
Okee, maar VC doet dat dus niet bij alleen /O2, daar moet je het expliciet instellen.
Als je met VStudio een debug build maakt, worden er ook geen dingen gedaan als "-fomit-frame-pointer" en -O2 e.d. Dan wordt het geheel ook zo clean mogelijk gecompileert, zonder teveel poespas. Wat ik niet helemaal snap is dat je met VC wel een debug build doet, maar met gcc niet :? Wat is de logica daar achter?
Waar zei ik dat dan :? Ik heb in visual studio ook een release gebakken hoor, anders zou die code er wel wat anders uitzien.
Maar ik vind ook dat gcc best had kunnen kiezen om "a" in edx te zetten, deze op nul te checken, en dan een conditionele move met [edx + 4] naar edx, dan had edx niet nog eens nul gemaakt hoeven worden.
Als je eerst a in edx zet en daarna een null check doet kunnen die 2 instructies niet pairen (read after write). Een mov eax, [a] en xor edx, edx pairen wel, een test + jCC ook. Waarschijnlijk is daarom deze manier gekozen.
En de logica achter die esp += 0x18 snap ik ook niet, ik snap esp += 8. (al is al het alignen in veel gevallen helemaal niet nodig...)
Zolang je niet met quadwords of groter gaat werken is 4 byte alignment genoeg, en dat is de stack altijd al (tenminste dat hoort zo).
.oisyn schreef op 27 februari 2003 @ 17:42:
hij aligned de stack blijkbaar op 16 bytes, aangezien ie er eerst 12 aftrekt, en daarna nog eens 4 bytes op pushed
Het lijkt wel op 16 byte alignment inderdaad maar als je het programma in de debugger gooit en over die sub + push heen tracet krijg je niet altijd 16 byte alignment. Op 8 bytes is het laagste dat ik gezien heb, dus 16 bytes is het niet. En als die alignment standaard op 4 staat is al dat aanpassen van esp niet nodig, de stack is al uitgelijnd op 4 byte boundaries.

www.madwizard.org

Pagina: 1