[c/c++] newbie - Pointers

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

  • terabyte
  • Registratie: September 2001
  • Laatst online: 28-06 12:53

terabyte

kan denken als een computer

Topicstarter
Inleiding
Dit is een (zeer) lange, uitgebreide post

Ik ben nu bezig met het leren van C++, omdat ik ISAPI Filters moet schrijven.

Ik heb al programmeerervaring (VB), maar het is de bedoeling dat C++ mijn standaardprogrammeertaal wordt, want op de universiteit zal ik toch nooit meer VB gebruiken.

Om mijn code goed te testen, heb ik een command-line programma geschreven. (Als het eenmaal goed werkt maak ik er een ISAPI DLL van - dat is niet zo moeilijk).

Wat is de bedoeling?
De Filter draait op de server, een bezoeker typt een URL in, bijv:

http://www.testserver.com/virtdir/dit/is/een/url.asp?dit=een&query=string

De filter ontvangt:

/virtdir/dit/is/een/url.asp?dit=een&query=string

Daarbij is het de bedoeling dat de gehele querystring (mits aanwezig) wordt verwijderd (heeft de filter nl. niet nodig). (Dit is in de code 'stap 1').

Als de querystring is verwijderd, dan blijft het volgende over:

/virtdir/dit/is/een/url.asp

Stap 2 in m'n code is het bepalen van de 'VDName'. Dat is de naam van de virtual directory.
In bovenstaande voorbeeld is "virtdir" de naam van de virtual directory.

Als als URL
/index.htm
wordt opgegeven, dan is dat geen probleem, want er wordt in een later stadium gecontroleerd of de gevonden waarde voor VDName wel een directory is.

Als als URL bijvoorbeeld
/?querystring
wordt opgegeven, dan is er geen waarde voor VDName... in dat geval krijgt de variabele VDName de waarde "VD_EMPTY".

De Code
De code is eigenlijk langer, maar ik heb niet relevante en minder belangrijke stukken eruit gelaten.
code:
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <algorithm>
#include <iostream.h>
#include <string.h>
#include <stdio.h>

using namespace std;

#define deletepointer(p) if(p) { delete p; p = NULL; }


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

    // Get URL from commandline (for testing only)

    char URLBuffer[1024];
     strcpy(URLBuffer,argv[1]);
    cout<<"\n\n==================
===================\nURLBuffer: "<<URLBuffer<<"\n";


    // Get the virtual directory name from the requested URL

    char * VDName;
    VDName = new char;

    char * pch1;
    pch1 = new char;

    char * pch2;
    pch2 = new char;

    char * pch3;
    pch3 = new char;

    char * pch4;
    pch4 = new char;

    if(VDName != NULL && pch1 != NULL && pch2 != NULL
 && pch3 != NULL && pch4 != NULL && strlen(URLBuffer) > 1)
    {

        // Step 1: Locate and remove querystring

        pch1 = (char*) memchr(URLBuffer,'?',strlen(URLBuffer));
        if (pch1 != NULL)
        {
            strncpy(pch2,URLBuffer,pch1-URLBuffer);
        }
        else
        {
            strcpy(pch2,URLBuffer);
        }

        // Step 2: get virtual directory name

        reverse(pch2,pch2+strlen(pch2));
        strncpy(pch3,pch2,strlen(pch2)-1);
        reverse(pch3,pch3+strlen(pch3));

        strcat(pch3,"/");

        strncpy(pch4,pch3,1);
        if(strcmp(pch4,"/") == 0)
        {
            strcpy(VDName,"VD_EMPTY");
        }
        else
        {
            pch4 = (char*) memchr(pch3,'/',strlen(pch3));
            if (pch4 != NULL)
            {
                strncpy(VDName,pch3,pch4-pch3);
            }
        }

    }
    else
    {
        strcpy(VDName,"VD_EMPTY");
    }

    cout<<"Virtual Directory Name: "<<VDName<<"\n";
    cout<<"-------------------------------------\n";

    // cleanup

    deletepointer(VDName);
    
    deletepointer(pch4);

    deletepointer(pch3);

    deletepointer(pch2);

    deletepointer(pch1);

    return 0;
}

Input: (command line arguments): een url (bijv: "/ditiseentest/home/test/")
Output: Virtual Directory Name: {VDName} (bijv: Virtual Directory Name: ditiseentest)

Code testen
Er zijn heel veel verschillende (geldige & ongeldige) URLs mogelijk, de ISAPI Filter DLL moet ze allemaal aankunnen zonder te crashen.

Daarom heb ik een batchbestand gemaakt, waarmee ik het programma snel met een aantal verschillende URLs kan testen.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@echo off
cls
test.exe "/virtdir/home/test"
test.exe "/virtdir/home/test/"
test.exe "/virtdir/"
test.exe "/virtdir"
test.exe "/virtdir/?dit=eenquerystring"
test.exe "/virtdir?dit=eenquerystring"
test.exe "/?dit=eenquerystring"
test.exe "/virtdir/?dit=eenquerystring"
test.exe "/virtdir?dit=eenquerystring"
test.exe "/?dit=eenquerystring"
test.exe "/"
test.exe "/virtdir/?dit=eenquerystring"
test.exe "/virtdir?dit=eenquerystring"

Het probleem

Als alles goed zou werken, zou dit de correcte output van m'n batchbestand moeten zijn:
code:
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
=====================================
URLBuffer:  /virtdir/home/test
VDName:    virtdir


=====================================
URLBuffer:  /virtdir/home/test/
VDName:    virtdir


=====================================
URLBuffer:  /virtdir/
VDName:    virtdir


=====================================
URLBuffer:  /virtdir
VDName:    virtdir


=====================================
URLBuffer:  /virtdir/?dit=eenquerystring
VDName:    virtdir


=====================================
URLBuffer:  /virtdir?dit=eenquerystring
VDName:    virtdir


=====================================
URLBuffer:  /?dit=eenquerystring
VDName:    VD_EMPTY


=====================================
URLBuffer:  /virtdir/?dit=eenquerystring
VDName:    virtdir


=====================================
URLBuffer:  /virtdir?dit=eenquerystring
VDName:    virtdir


=====================================
URLBuffer:  /?dit=eenquerystring
VDName:    VD_EMPTY


=====================================
URLBuffer:  /
VDName:    VD_EMPTY


=====================================
URLBuffer:  /virtdir/?dit=eenquerystring
VDName:    virtdir


=====================================
URLBuffer:  /virtdir?dit=eenquerystring
VDName:    virtdir

Maar ik krijg dit:
code:
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
=====================================
URLBuffer:  /virtdir/home/test
VDName:    =&#9786;tirtdir


=====================================
URLBuffer:  /virtdir/home/test/
VDName:    =t=


=====================================
URLBuffer:  /virtdir/
VDName:    &#9787;virtdir¢Ÿ


=====================================
URLBuffer:  /virtdir
VDName:    virtdir


=====================================
URLBuffer:  /virtdir/?dit=eenquerystring
VDName:    &#9787;virtdir¢Ÿ


=====================================
URLBuffer:  /virtdir?dit=eenquerystring
VDName:    &#9787;virtdir&#9787;


=====================================
URLBuffer:  /?dit=eenquerystring
VDName:    =&#9786;=


=====================================
URLBuffer:  /virtdir/?dit=eenquerystring
VDName:    &#9787;virtdir¢Ÿ


=====================================
URLBuffer:  /virtdir?dit=eenquerystring
VDName:    &#9787;virtdir&#9787;


=====================================
URLBuffer:  /?dit=eenquerystring
VDName:    =&#9786;=


=====================================
URLBuffer:  /
VDName:    VD_EMPTY


=====================================
URLBuffer:  /virtdir/?dit=eenquerystring
VDName:    &#9787;virtdir¢Ÿ


=====================================
URLBuffer:  /virtdir?dit=eenquerystring
VDName:    &#9787;virtdir&#9787;

Het valt op dat bij sommige URLs, VDName vreemde tekens erbij heeft gekregen. (=☻, ☺, ¢Ÿ, etc)
Hier wordt het niet goed weergegeven ivm HTML etc. maar de tekens die ik bedoel zijn dingen in het begin van de ASCII set, zoals een hartje, een smiley, etc)
Ik vermoed dat dat te maken heeft met een verkeerd gebruik van pointers (ik ben nog steeds newbie).
Ik heb gelezen dat dat soms 'garbage' kan opleveren of dat het gevaarlijk is, etc.

Ik zit al weken vast, dus misschien dat hier iemand me kan helpen.
Alvast bedankt

Extra Info
Compiler: g++ voor Windows
(Microsoft VC++ werkt hier helemaal niet)

  • Sjonny
  • Registratie: Maart 2001
  • Laatst online: 17:53

Sjonny

Fratser

code:
1
2
char * VDName;
VDName = new char;

zou je ook niet meegeven hoe lang je string moet worden? nu alloc je maar 1 char, kan je net je \0 terminater instoppen :Y)
code:
1
2
3
char *VDName = new char[256];
/* do your stuff */
delete [] VDName;

jij had ipv die delete deletepointer(meuk) ? geen idee wat dat was, maar zo hoort het.
gaat vast wat beter werken zo :)

The problem is in the part of your brain that handles intelligence.


  • terabyte
  • Registratie: September 2001
  • Laatst online: 28-06 12:53

terabyte

kan denken als een computer

Topicstarter
Op woensdag 19 juni 2002 15:10 schreef Sjonny het volgende:
code:
1
2
char * VDName;
VDName = new char;

zou je ook niet meegeven hoe lang je string moet worden? nu alloc je maar 1 char, kan je net je \0 terminater instoppen :Y)
Hmm... en dan nu een absolute newbie vraag:
wat als je niet weet hoe lang VDName eigenlijk gaat worden?
code:
1
2
3
char *VDName = new char[256];
/* do your stuff */
delete [] VDName;

jij had ipv die delete deletepointer(meuk) ? geen idee wat dat was, maar zo hoort het.
gaat vast wat beter werken zo :)
OK, 256 will do.
(en nu maar hopen dat niemand en VDname intikt langer dan 256 chars... of levert dat geen problemen op?)

deletepointer stond bij #define.
het betekent:

delete p;
p = NULL;

Ik heb ooit ergens gelezen dat nadat je delete hebt gebruikt, je pointer een NULL waarde moet geven... vandaar.

Verwijderd

Als je in C++ werkt, waarom gebruik je dan C strings (char*) ipv. C++ strings (std::string)? Zo'n C++ string hoef je niet te alloceren en te deleten, daar zorgt hij zelf wel voor (en het kan niet fout gaan, zoals hier); en heeft tientallen prachtige methods...

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 28-08 12:00

Janoz

Moderator Devschuur®

!litemod

Op woensdag 19 juni 2002 15:17 schreef terabyte het volgende:


Hmm... en dan nu een absolute newbie vraag:
wat als je niet weet hoe lang VDName eigenlijk gaat worden?
Tja, dat is 1 van de meest voorkomende problemen bij het programmeren.. Hoeveel geheugen reserveer ik. Het beste is om dit soort getallen op te slaan in een constante. Op deze manier kun je door het aanpassen op 1 plek in je source en het hercompileren de buffers groter maken. Je zou ook met dynamische buffers kunnen werken natuurlijk.

In dit geval kan ik je gerust stellen.. Een URL mag volgens de RFC niet langer dan 255 tekens zijn (controleer wel ff op de lengte voordat je een nieuwe 'Code red vunerability' maakt)

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Verwijderd

Op woensdag 19 juni 2002 15:17 schreef terabyte het volgende:
Ik heb ooit ergens gelezen dat nadat je delete hebt gebruikt, je pointer een NULL waarde moet geven... vandaar.
1) Dit is onwaar, je hoeft een pointer niet null te zetten na een delete (en je hoeft een pointer niet op null te testen voor je hem delete, je kunt de zelfde pointer 10x deleten).

2) NULL is een C-ism. Als je in C++ een pointer null wilt zetten doe je dat met een 0, en niet met een macro.

<edit>
En gebruik in godsnaam een std::string en ga geen char[] met een arbitraire lengte zitten gebruiken. Iedereen die in C++ met char* strings werkt, moet zich serieus afvragen waar zijn masochistische aanleg vandaan komt.
</edit>

  • Orphix
  • Registratie: Februari 2000
  • Niet online
Zoals mietje al zei, als je toch in C++ programmeert probeer dan zo snel mogelijk gebruik te maken van std::string. Deze STL class maakt gebruik van malicious pointers vaak overbodig.

Onderstaande werkt bij mij voorzover ik heb getest
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <string>
#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{   
    string test = "/?dit=eenquerystring";

    // zoek de eerste letter die het einde van een
    // virtuele directory weergeeft: / of ?
    // begin bij de 2e positie (minstens 1 letter voor de virtuele directory)
    int position = test.find_first_of("/?", 2);

    // check of er iets is gevonden (npos geeft 'niet gevonden' aan)
    if(position == std::string::npos)
        cout << "VD_EMPTY" << endl;
    else
        cout << test.substr(1, position-1) << endl;         
    cin.get();
}

In feite is hier het algoritme teruggebracht tot 5 regels :Y)

edit:

Ik heb
string test = "....";
hier gebruikt om te testen. Maar je kan natuurlijk ook dit doen:
string test(argv[1]);
om directories vanaf de commando te parsen.

  • ^Mo^
  • Registratie: Januari 2001
  • Laatst online: 04-11-2025
Op woensdag 19 juni 2002 15:26 schreef mietje het volgende:

[..]

1) Dit is onwaar, je hoeft een pointer niet null te zetten na een delete (en je hoeft een pointer niet op null te testen voor je hem delete, je kunt de zelfde pointer 10x deleten).
code:
1
2
3
int *p = new int( 4);
delete p;
delete p;

Dit soort dingen gaan toch echt errors opleveren. Wie weet wat er in tussentijd met dat stukje geheugen is gebeurd?
Je hoeft een pointer niet op NULL te zetten inderdaad, maar mocht jij in een situatie komen dat een pointer al is gedelete (door een bepaalde verloop in het programma) dan wil je wel weten of de pointer nog geldig is of niet...

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


  • yade
  • Registratie: Mei 2002
  • Laatst online: 25-11-2025
Op woensdag 19 juni 2002 15:26 schreef mietje het volgende:
1) Dit is onwaar, je hoeft een pointer niet null te zetten na een delete (en je hoeft een pointer niet op null te testen voor je hem delete, je kunt de zelfde pointer 10x deleten).
Je kunt een pointer die op 0 staat 10x deleten.

Een pointer die naar geheugen wijst mag je maar 1 keer deleten. Daarna kan je problemen verwachten aangezien het geheugen dat vrijgekomen is door anderen gealloceerd kan worden.

  • terabyte
  • Registratie: September 2001
  • Laatst online: 28-06 12:53

terabyte

kan denken als een computer

Topicstarter
Okay, bedankt voor de replies.

Ik ga het proberen met normale strings ipv C char arrays.

ik krijg dan hoogstwaarschijnlijk problemen als ik er een ISAPI van ga maken, maar dat zie ik dan wel weer.
Nu kan ik iig weer vooruit :) :)

edit:

Maar het is wel belangrijk voor me om die pointers goed onder de knie te krijgen

  • Orphix
  • Registratie: Februari 2000
  • Niet online
Op woensdag 19 juni 2002 15:39 schreef terabyte het volgende:
ik krijg dan hoogstwaarschijnlijk problemen als ik er een ISAPI van ga maken, maar dat zie ik dan wel weer.
Veel API's/libraries verwachten nog een char* als string. Daarom heeft de string classe een method: c_str() die een const char* teruggeeft. Op deze manier kan je altijd je std::string blijven gebruiken en 'backwards'-compatible zijn.

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 18:57
Op woensdag 19 juni 2002 15:39 schreef _Mo_ het volgende:

[..]
code:
1
2
3
int *p = new int( 4);
delete p;
delete p;

Dit soort dingen gaan toch echt errors opleveren. Wie weet wat er in tussentijd met dat stukje geheugen is gebeurd?
Je hoeft een pointer niet op NULL te zetten inderdaad, maar mocht jij in een situatie komen dat een pointer al is gedelete (door een bepaalde verloop in het programma) dan wil je wel weten of de pointer nog geldig is of niet...
In serieuze C++ code komt delete niet voor buiten destructors. Het gevolg is dat de pointer na de } aan het eind van dtor niet meer bestaat. En als'ie toch niet meer bestaat, dan heeft het geen zin op'm daarvor op 0 te zetten.

Daarom zal een goede C++ programmeur nooit delete p; p=0; doen.

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


  • Pooh
  • Registratie: April 2001
  • Niet online

Pooh

Lees eens een boek

Op woensdag 19 juni 2002 17:08 schreef MSalters het volgende:

[..]

In serieuze C++ code komt delete niet voor buiten destructors. Het gevolg is dat de pointer na de } aan het eind van dtor niet meer bestaat. En als'ie toch niet meer bestaat, dan heeft het geen zin op'm daarvor op 0 te zetten.

Daarom zal een goede C++ programmeur nooit delete p; p=0; doen.
Wat een mateloos arrogante en naief idealistische uitspraak. ;)

In volmaakte C++ code komt delete misschien niet voor buiten destructors. (Hoewel ik zelfs hier nog wel een discussie over wil voeren). Maar de wereld is niet volmaakt, en in een groot (door meerdere mensen tegelijk gemaakt) C++ project zal het toch echt wel eens voorkomen dat een pointer ergens halverwege gedelete wordt. Om je dus aan te wennen altijd if(p) { delete p; p=NULL; } te schrijven is prijzenswaardig.

Verwijderd

Op woensdag 19 juni 2002 17:32 schreef Poohbear het volgende:
Om je dus aan te wennen altijd if(p) { delete p; p=NULL; } te schrijven is prijzenswaardig.
Wanneer dringt het nu door? Die if(p) ... hoeft nooit, zelfs niet als je free() gebruikt. Het is standard-conforming-behaviour dat zowel delete als free() geen effect hebben op een nullpointer, dus waarom doe je die test als delete/free hem zelf doet?

Als je dit soort constructies regelmatig nodig hebt, dan is dat een indicatie voor een designfout. Dat stukje code is slechts symptoombestrijding...

  • terabyte
  • Registratie: September 2001
  • Laatst online: 28-06 12:53

terabyte

kan denken als een computer

Topicstarter
Op woensdag 19 juni 2002 15:51 schreef Orphix het volgende:

[..]

Veel API's/libraries verwachten nog een char* als string. Daarom heeft de string classe een method: c_str() die een const char* teruggeeft. Op deze manier kan je altijd je std::string blijven gebruiken en 'backwards'-compatible zijn.
Ik heb c_str() een paar keer moeten gebruiken. Op de meeste plaatsen werkt het perfect, behalve bij dit:
code:
1
pHeaderInfo->AddHeader(pCtxt->m_pFC, "VDName:", VDName.c_str());

Ik krijg daar als fout:

'BOOL (_HTTP_FILTER_CONTEXT *,LPSTR,LPSTR)' : cannot convert parameter 3 from 'const char *' to 'LPSTR'

Ik moet de string VDName eigenlijk omzetten in LPSTR

Na een beetje zoeken kwam ik uit bij 'StringToHGlobalAnsi' maar zoals het er nu uitziet werkt dat ook niet...

  • ^Mo^
  • Registratie: Januari 2001
  • Laatst online: 04-11-2025
gebruik dan const_cast<char *>( VDName.c_str());

De functie verwacht een LPSTR (oftewel char * ) en jij geeft een const char * aan, en dat kan niet... met const_cast haal je dat eraf...

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


  • terabyte
  • Registratie: September 2001
  • Laatst online: 28-06 12:53

terabyte

kan denken als een computer

Topicstarter
OK iedereen,

Bedankt; ik heb m'n ISAPI DLL aan de praat *D
extra dank: Orphix, _Mo_

Nu nog flink checken op memory leaks, performance en security flaws...

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03-09 13:30

.oisyn

Moderator Devschuur®

Demotivational Speaker

Op woensdag 19 juni 2002 20:16 schreef _Mo_ het volgende:
gebruik dan const_cast<char *>( VDName.c_str());

De functie verwacht een LPSTR (oftewel char * ) en jij geeft een const char * aan, en dat kan niet... met const_cast haal je dat eraf...
het const keyword bestaat natuurlijk niet voor niets :)
Als een functie een LPSTR (char * dus) verwacht, dan wil hij hoogstwaarschijnlijk ook schrijven naar de betreffende buffer (anders was het wel een LPCSTR)

En dus moet je ook een beschrijfbare buffer meegeven ipv eentje waar je niet naar hoort te schrijven

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.


  • ^Mo^
  • Registratie: Januari 2001
  • Laatst online: 04-11-2025
Op woensdag 19 juni 2002 23:42 schreef .oisyn het volgende:

[..]

het const keyword bestaat natuurlijk niet voor niets :)
Als een functie een LPSTR (char * dus) verwacht, dan wil hij hoogstwaarschijnlijk ook schrijven naar de betreffende buffer (anders was het wel een LPCSTR)

En dus moet je ook een beschrijfbare buffer meegeven ipv eentje waar je niet naar hoort te schrijven
Maar moet het dan geen reference zijn om de buffer te veranderen...?

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


  • Orphix
  • Registratie: Februari 2000
  • Niet online
Op woensdag 19 juni 2002 23:46 schreef _Mo_ het volgende:
Maar moet het dan geen reference zijn om de buffer te veranderen...?
Lees eens dit topic door (ook voor terabyte):
[topic=518783]
const_cast is echt een no go.

  • ^Mo^
  • Registratie: Januari 2001
  • Laatst online: 04-11-2025
Op woensdag 19 juni 2002 23:51 schreef Orphix het volgende:

[..]

Lees eens dit topic door (ook voor terabyte):
[topic=518783]
const_cast is echt een no go.
Ik heb het topic gelezen, 'tis inderdaad iets waar ik nog helemaal niet aan had gedacht, dat de string ergens anders bijvoorbeeld zou kunnen worden opgeslagen :'( of wat MSalters zegt...

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


  • Gerco
  • Registratie: Mei 2000
  • Laatst online: 02-09 19:44

Gerco

Professional Newbie

Dus maak je een kopie van je string, de handigste manier lijkt me wel een tweede std::string te maken en die naar een LPSTR omzetten.

Dan hoef je zelf niet in de weer met strcpy() enzo en hoef je nog steeds niet zelf buffers te alloceren met het risico op een overflow e.d.

- "Als ik zou willen dat je het begreep, legde ik het wel beter uit!" | All number systems are base 10!


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 18:57
Op woensdag 19 juni 2002 17:32 schreef Poohbear het volgende:

[..]
...
Maar de wereld is niet volmaakt, en in een groot (door meerdere mensen tegelijk gemaakt) C++ project zal het toch echt wel eens voorkomen dat een pointer ergens halverwege gedelete wordt. Om je dus aan te wennen altijd if(p) { delete p; p=NULL; } te schrijven is prijzenswaardig.
Nou zeg je twee tegenstrijdige dingen: 1) Dat C++ code door andere (incompetente) C++ programmeurs wordt gemaakt, die hun delete's buiten dtors doen, en 2) dat diezelfde programmeurs blijkbaar wel hun pointers op NULL zetten.

Evengoed, als de andere programmeurs dat doen staat dat los van waar jij je delete's doet. Jij als goede C++ programmeur zal ze toch gewoon in een dtor doen. En aangezien die pointer daardoor 3 regels verder uit scope gaat, kan die andere "slechte" programmeur hem toch niet deleten. Dus jij hoeft na jouw correct geplaatste delete toch geen p=0 te doen.

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


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 18:57
Op woensdag 19 juni 2002 19:38 schreef terabyte het volgende:

[..]

Ik heb c_str() een paar keer moeten gebruiken. Op de meeste plaatsen werkt het perfect, behalve bij dit:
code:
1
pHeaderInfo->AddHeader(pCtxt->m_pFC, "VDName:", VDName.c_str());

Ik krijg daar als fout:

'BOOL (_HTTP_FILTER_CONTEXT *,LPSTR,LPSTR)' : cannot convert parameter 3 from 'const char *' to 'LPSTR'

Ik moet de string VDName eigenlijk omzetten in LPSTR
( LPSTR is gewoon de MS methode om char* onleesbaar te krijgen )
Het probleem is niet alleen parameter 3; ook parameter 2 is een char*. En aangezien je niet naar een constante zoals "VDName:" kunt schrijven heb je een tweede probleem daar.

De makkelijkste en correcte methode om aan een char* te kmen is een vector<char> te nemen; &vec[0] is dan een char* waar je vec.size() bytes naar toe kunt schrijven.

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


  • terabyte
  • Registratie: September 2001
  • Laatst online: 28-06 12:53

terabyte

kan denken als een computer

Topicstarter
Ik heb const_cast eruit gesloopt (met const_cast werkte het trouwens OK) en vervangen door strdup (ik heb ook strcpy geprobeerd).

Onderstaand code werkt, maar niet voor lang.

Als ik de DLL laad, en ik houd F5 ingedrukt (om veel request te simuleren), dan kapt ie er na ongeveer 300 a 400 request mee... de server reageert niet meer en ik moet w3svc opnieuw opstarten.

Dat is dus niet de bedoeling, met const_cast had ik geen last van dit probleem.

Hieronder een deel van de source van de ISAPI:
code:
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
DWORD CCSInitFilter::OnPreprocHeaders(CHttpFilterContext* pCtxt,
         PHTTP_FILTER_PREPROC_HEADERS pHeaderInfo)
{

 char * URLBuffer = NULL;
 DWORD size = 0;
 BOOL res;

 // Checken hoe groot de buffer moet zijn 
 //  dmv een te kleine buffer
 res = pHeaderInfo->GetHeader (pCtxt->m_pFC, "url", URLBuffer, &size);
 if (!res && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
 {
  // Nieuwe buffersize instellen
  URLBuffer = (char*)pCtxt->m_pFC->AllocMem (pCtxt->m_pFC, size, 0);
  if (URLBuffer != NULL)
  {
   // Opnieuw gevraagde URL uit de header halen
   if (pHeaderInfo->GetHeader (pCtxt->m_pFC, "url", URLBuffer, &size))
   {

    string URLParse(URLBuffer);

    // URLParse moet altijd eindigen op een '/'
    URLParse += "/";

    // zoek de eerste letter die het einde van een
    //  virtuele directory aangeeft: '/' of '?'
    int position = URLParse.find_first_of("/?", 1);

    // als VDName niet leeg is, doorgaan
    if(position > 1)
    {
     // waarde aan VDName toekennen
     string VDName;
     VDName = URLParse.substr(1, position-1);

     // fysieke pad naar het ContentInit.asp bestand genereren
     string VDPhysicalPath;
     // wwwroot pad wordt uit Register gehaald (m_VDPath)
     VDPhysicalPath = m_VDPath;
     VDPhysicalPath += "\\content\\";
     VDPhysicalPath += VDName;
     VDPhysicalPath += "\\ContentInit.asp";

     // kijken op ContentInit.asp bestaat
     //  door proberen het te openen
     FILE * pFile;
     pFile = fopen(VDPhysicalPath.c_str(),"rt");

     if (pFile != NULL)
     {
    // ContentInit.asp sluiten
    fclose(pFile);

    // url voor de redirect genereren
    string redirecturl;
    redirecturl = "/";
    redirecturl += VDName;
    redirecturl += "/ContentInit.asp";

    // pas bestaande headers aan
    pHeaderInfo->SetHeader(pCtxt->m_pFC,"method","POST");
    pHeaderInfo->SetHeader(pCtxt->m_pFC,"url",strdup(redirecturl.c_str()));
    // voeg nieuwe headers toe
    pHeaderInfo->AddHeader(pCtxt->m_pFC,"OriginalURL:",URLBuffer);
    pHeaderInfo->AddHeader(pCtxt->m_pFC,"VDName:",strdup(VDName.c_str()));

     }
    }
   }
  }
 }

 // Doorgaan
 return SF_STATUS_REQ_NEXT_NOTIFICATION;
}

Verwijderd

Je hebt een memory-leak geintroduceerd.

Strings die je dupliceert met strdup() zul je ergens ook weer moeten vrijgeven met free().

Je zou, als je die const_cast niet gebruiken wilt, dmv. een basic_string::reference ook een charT* kunnen adresseren. Dus iets als &VDName[0]. Dit is trouwens wel net zo'n quick-and-dirty hack als die const_cast.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03-09 13:30

.oisyn

Moderator Devschuur®

Demotivational Speaker

Op donderdag 20 juni 2002 17:04 schreef mietje het volgende:
Je zou, als je die const_cast niet gebruiken wilt, dmv. een basic_string::reference ook een charT* kunnen adresseren. Dus iets als &VDName[0]. Dit is trouwens wel net zo'n quick-and-dirty hack als die const_cast.
is std::string gegarandeerd zero terminated dan?
maar goed, eigenlijk vind ik deze oplossing nog viezer dan de const_cast oplossing ;)

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.


  • Orphix
  • Registratie: Februari 2000
  • Niet online
Op donderdag 20 juni 2002 11:57 schreef MSalters het volgende:

[..]

( LPSTR is gewoon de MS methode om char* onleesbaar te krijgen )
Het probleem is niet alleen parameter 3; ook parameter 2 is een char*. En aangezien je niet naar een constante zoals "VDName:" kunt schrijven heb je een tweede probleem daar.

De makkelijkste en correcte methode om aan een char* te kmen is een vector<char> te nemen; &vec[0] is dan een char* waar je vec.size() bytes naar toe kunt schrijven.
Garandeerd een vector dan wel dat het zijn informatie altijd op dezelfde plek in het geheugen opslaat?

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 18:57
Op donderdag 20 juni 2002 17:26 schreef Orphix het volgende:

[..]

Garandeerd een vector dan wel dat het zijn informatie altijd op dezelfde plek in het geheugen opslaat?
Ja, het geheugen van een vector is altijd continu, en zolang je geen geheugenreallocaties doet (reserve()/resize()/push_back() ) blijft &[0] ongewijzigd. En als je een .reserve(N) doet dan blijft &[0] daarna ongewijzigd zolang .size()<=N .

Meestal is een .resize(MAX_OUTPUT_SIZE) genoeg.


std::string heeft geen van beide garanties, en er zijn genoeg implementaties die daar gebruik van maken. Zo is c_str() een effectieve methode om &[0] van een string te veranderen.

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


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 18:57
Op donderdag 20 juni 2002 17:21 schreef .oisyn het volgende:

[..]

is std::string gegarandeerd zero terminated dan?
Nee, is dacht ik zelfs ongebruikelijk. Waarvoor dacht je dat er zowel .data() als .c_str() is?

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


  • terabyte
  • Registratie: September 2001
  • Laatst online: 28-06 12:53

terabyte

kan denken als een computer

Topicstarter
Op donderdag 20 juni 2002 17:04 schreef mietje het volgende:
Je hebt een memory-leak geintroduceerd.

Strings die je dupliceert met strdup() zul je ergens ook weer moeten vrijgeven met free().
Als andere oplossing had ik de hier gesuggereerde strcpy:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.....


// Tijdelijke buffer voor conversie const char* naar char*
char * tempbuffer = new char[256];

// pas bestaande headers aan
pHeaderInfo->SetHeader(pCtxt->m_pFC, "method", "POST");
pHeaderInfo->SetHeader(pCtxt->m_pFC, "url", strcpy(tempbuffer,redirecturl.c_str()));
// voeg nieuwe headers toe
pHeaderInfo->AddHeader(pCtxt->m_pFC, "OriginalURL:", URLBuffer);
pHeaderInfo->AddHeader(pCtxt->m_pFC, "VDName:", strcpy(tempbuffer,VDName.c_str()));

// Cleanup
delete[] tempbuffer;


.....

Maar als ik het goed begrijp, is bovenstaande stukje code ook gevaarlijk: als de lengte van 'VDName' of 'redirecturl' groter is dan 256 tekens, dan kunnen er vreemde dingen gebeuren (?).
(en overdreven veel geheugen alloceren is ook niet alles)

strcpy is zo gedefinieerd:

char * strcpy ( char * dest, const char * src );

waarbij de return value exact hetzelfde is als dest.

Is het niet mogelijk om strcpy zo te herschrijven dat je zoiets krijgt:

char * strcpy ( const char * src );

zodat ik helemaal geen 'tempbuffer' als dest nodig heb?
Of is het allemaal niet zo simpel als dat het lijkt?

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 18:31
Op donderdag 20 juni 2002 18:42 schreef terabyte het volgende:
Is het niet mogelijk om strcpy zo te herschrijven dat je zoiets krijgt:

char * strcpy ( const char * src );

zodat ik helemaal geen 'tempbuffer' als dest nodig heb?
Ja, die bestaat. Die heet strdup (met het exacte prototype zoals jij 'm aangeeft).

Aangezien in dit geval het geheugen dynamisch gealloceerd wordt, dien je er aan te denken het ook weer vrij te geven (met behulp van free()).

edit:
Blijkbaar had je dat al eens geprobeerd. Wat is dan precies het probleem?

Een functie die een nieuw stuk geheugen teruggeeft zonder dat dat geheugen opgeruimt moet worden, bestaat natuurlijk niet. Er zijn wel wat functies die een bestaande buffer (die dus niet door jou opgeruimt hoeft te worden) teruggeven, maar het gebruik daarvan wordt meestal afgeraden.

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 17:11
Op donderdag 20 juni 2002 18:42 schreef terabyte het volgende:

Maar als ik het goed begrijp, is bovenstaande stukje code ook gevaarlijk: als de lengte van 'VDName' of 'redirecturl' groter is dan 256 tekens, dan kunnen er vreemde dingen gebeuren (?).
Gebruik dan strncpy(...) waarbij je ook een maximale lengte mag opgeven.

Overigens kan men de benodigde grootte van het array achterhalen met bijvoorbeeld strlen(...). Als je deze lengte gebruikt en afkapt op wat jij de maximale lengte vind, ben je er ook.

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.


  • terabyte
  • Registratie: September 2001
  • Laatst online: 28-06 12:53

terabyte

kan denken als een computer

Topicstarter
Over het probleem dat de server blijft hangen na +/- 400 requests dmv F5:

Ik heb een vermoeden hoe het komt:

het ligt volgens mij niet aan mijn code, maar aan IIS..
Ik heb IIS 5.1 onder WinXP Professional, deze staat maar 10 verbindingen tegelijkertijd toe. :+

Als ik F5 ingedrukt houd, dan worden er zoveel requests gedaan, dat er op den duur 10 verbindingen gemaakt zijn.

Als ik de browser afsluit en dan opnieuw begin met vernieuwen, dan gaat dat weer goed totdat er weer 10 verbindingen zijn gemaakt, etc.

Ik heb nu dit als code en het werkt perfect (mits bovenstaande theorie waar is).
code:
1
2
3
4
char* d1 = new char[ redirecturl.size() + 1 ];
strcpy( d1, redirecturl.c_str() );
pHeaderInfo->SetHeader(pCtxt->m_pFC, "url", d1);
delete[] d1;

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03-09 13:30

.oisyn

Moderator Devschuur®

Demotivational Speaker

Op donderdag 20 juni 2002 18:08 schreef MSalters het volgende:

[..]

Nee, is dacht ik zelfs ongebruikelijk. Waarvoor dacht je dat er zowel .data() als .c_str() is?
daarom juist, ik vond het ook al zo vreemd dat mietje dan met die oplossing kwam :)

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.


Verwijderd

Op donderdag 20 juni 2002 23:27 schreef terabyte het volgende:
code:
1
2
3
4
char* d1 = new char[ redirecturl.size() + 1 ];
strcpy( d1, redirecturl.c_str() );
pHeaderInfo->SetHeader(pCtxt->m_pFC, "url", d1);
delete[] d1;
Of zo:
code:
1
pHeaderInfo->SetHeader(pCtxt->m_pFC, "url", vector<char>(redirecturl.begin(), redirecturl.end()).begin());

Vind ik persoonlijk wat mooier.

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 18:31
Op donderdag 20 juni 2002 23:53 schreef Sneechy het volgende:
Of zo:
code:
1
pHeaderInfo->SetHeader(pCtxt->m_pFC, "url", vector<char>(redirecturl.begin(), redirecturl.end()).begin());

Vind ik persoonlijk wat mooier.
Dat geeft aardig wat performance overhead en ik vind 't ook niet echt duidelijk te lezen. Het voordeel (?) is wel dat 't 100% C++ is.

Wat ik me echter afvroeg, is of een vector<T> naar een (niet constante) T* te casten is?

Een ander voordeel is natuurlijk dat de geheugenmanagement hier automatisch geregeld wordt. Misschien had dat ook met een auto_ptr gekund?

Ik zou 't trouwens gewoon met strdup gedaan hebben:
code:
1
2
3
char *dup=strdup(redirecturl.c_str());
pHeaderInfo->SetHeader(pCtxt->m_pFC, "url", dup);
free(dup);

Verwijderd

Op vrijdag 21 juni 2002 00:09 schreef Soultaker het volgende:

[..]

Dat geeft aardig wat performance overhead
Kun je dit misschien wat verder toelichten ?
Misschien had dat ook met een auto_ptr gekund?
auto_ptr werkt niet met arrays (zie ook: GotW #42: Using auto_ptr)..

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 18:31
Op vrijdag 21 juni 2002 00:17 schreef Sneechy het volgende:
Kun je dit misschien wat verder toelichten ?
Ik ken de implementatie van Vector niet, maar aangezien 'ie iterators meekrijgt, kan 'ie de elementen alleen een voor een uitlezen.

Ik geloof verder ook dat 'ie van te voren niet kan weten om hoeveel karakters het gaat, (of kan een iterator veilig gedupliceerd worden?). Als dit het geval is (maar dat weet ik niet zeker) moet 'ie ook nog diverse keren geheugen alloceren.

Het zal waarschijnlijk niet echt veel uitmaken, maar als je dan toch een C functie aanroept en een C string moet bouwen, kun je daar net zo goed een C functie bij gebruiken.

Verwijderd

Op vrijdag 21 juni 2002 01:24 schreef Soultaker het volgende:
Als dit het geval is (maar dat weet ik niet zeker) moet 'ie ook nog diverse keren geheugen alloceren.
Over die (re-)allocaties, hier een paar stukjes November '96 draft:

23.2.4.1:
The constructor template <class InputIterator> vector(InputIterator first, InputIterator last) makes knip no reallocations if iterators first and last are of knip random access categories.
21.3, paragraaf 2:
the iterators supported by basic_string are random access iterators
Er is dus geen sprake van reallocaties.
Het zal waarschijnlijk niet echt veel uitmaken, maar als je dan toch een C functie aanroept en een C string moet bouwen, kun je daar net zo goed een C functie bij gebruiken.
Niet mee eens.

std::vector is uitermate geschikt voor het spelen met char buffertjes en dus gebruik ik 'm daarvoor. Ik zie niet in waarom het gebruik van een bepaalde C interface het gebruik van C++ library utils uit zou moeten sluiten..

  • terabyte
  • Registratie: September 2001
  • Laatst online: 28-06 12:53

terabyte

kan denken als een computer

Topicstarter
Op vrijdag 21 juni 2002 00:09 schreef Soultaker het volgende:

Ik zou 't trouwens gewoon met strdup gedaan hebben:
code:
1
2
3
char *dup=strdup(redirecturl.c_str());
pHeaderInfo->SetHeader(pCtxt->m_pFC, "url", dup);
free(dup);
Is dat sneller dan strcpy?

Hmm. Misschien moet ik maar ff een test algoritme maken.


(Oplossing met vectoren werkte trouwens niet.)

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 18:57
Op donderdag 20 juni 2002 23:53 schreef Sneechy het volgende:

[..]

Of zo:
code:
1
pHeaderInfo->SetHeader(pCtxt->m_pFC, "url", vector<char>(r.begin(), r.end()).begin());
.begin() returned een iterator, en dat is niet altijd (bv VC7, STLport, Metroworks8) een pointer. &.front() werkt wel.

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


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 18:57
Op vrijdag 21 juni 2002 01:24 schreef Soultaker het volgende:

[..]

Ik ken de implementatie van Vector niet, maar aangezien 'ie iterators meekrijgt, kan 'ie de elementen alleen een voor een uitlezen.

Ik geloof verder ook dat 'ie van te voren niet kan weten om hoeveel karakters het gaat, (of kan een iterator veilig gedupliceerd worden?). Als dit het geval is (maar dat weet ik niet zeker) moet 'ie ook nog diverse keren geheugen alloceren.
Er is zoiets als iterator_traits, die je de iterator categorie kan vertellen, en std::distance die voor RandomIterators in O(1) het aantal elementen tussen i1 en i2 vertelt.

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


Verwijderd

Op donderdag 20 juni 2002 23:44 schreef .oisyn het volgende:
daarom juist, ik vond het ook al zo vreemd dat mietje dan met die oplossing kwam :)
Ik kwam met die oplossing omdat een const_cast werkt. Ik gokte er dus op dat MS lui was geweest en een LPCSTR bedoelde, maar ik vergat idd. de zero-termination. |:(
Pagina: 1