Toon posts:

[C++] Char** dynamisch vullen en retourneren met functie

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik heb een functie nodig die een char** retourneert, maar ik ben niet heel bekend met C++ en heb problemen met het vullen van een dynamische tweedimensionale array. Dit is een vereenvoudigde weergave van wat ik heb geprobeerd.

Ik maak gebruik van een functie die er zo uitziet:

char * getword(int);

En dit is de betreffende functie

code:
1
2
3
4
5
6
7
8
9
char** voorbeeld() {
  char** test;
  test = new char[x][x];  // FOUT DUS
  for (i := 0; i < x; i++) {
    strcpy(*test, getword(i));
    test++;
  }  
  return test;
}


Let even niet op de waarden en de werking van het programma, dit heb ik even ter plekke verzonnen om het te vereenvoudigen. Waar ik nu x heb ingevuld zijn dus variabele waarden.
Ik wil NEW gebruiken voor het alloceren van geheugen. Het probleem is dus dat ik niet weet hoe ik met new een meerdimensionale array alloceer.

Verder heb ik voor de strcpy geen "*test = new char[5]" gedaan omdat strcpy zelf al geheugen alloceert, correct me if i'm wrong.

De goede syntax is welkom!

  • ajvdvegt
  • Registratie: Maart 2000
  • Laatst online: 15-08 12:40
Verwijderd schreef op 06 October 2003 @ 15:12:
Verder heb ik voor de strcpy geen "*test = new char[5]" gedaan omdat strcpy zelf al geheugen alloceert, correct me if i'm wrong.
Nope, je moet zelf nog geheugen alloceren. Uit 'man strcpy':
char *strcpy(char *dest, const char *src);

<snip>

... the destination string dest must be large enough to receive the copy.

I don't kill flies, but I like to mess with their minds. I hold them above globes. They freak out and yell "Whooa, I'm *way* too high." -- Bruce Baum


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 01:56
Blijkbaar heb je nog weinig ervaring met pointers want er is van alles mis met je programma.

Een belangrijk punt is dat arrays in C/C++ geen volwaardige datatypen zijn. Arrays worden gerepresenteert als pointers, waardoor je ze (onder andere) niet by value kunt passen, maar ook betekent het dat ze in principe altijd 1-dimensionaal zijn. Je kunt lokale, globale en member variabelen wel met meerdere dimensies declareren, maar dat is eigenlijk meer uitzondering dan de regel; met argumenten en return values kan het zeker niet (want dat zijn altijd gewoon domme pointers). New retourneert ook een pointer en snapt dus niets van dimensies. Je kunt dus op de heap uitsluitend 1-dimensionale arrays alloceren.

Een alternatief is het alloceren van een 1-dimensionale array van pointers en aan elk van die pointers ken je vervolgens een nieuwe 1-dimensionale array toekennen. Je hebt dan in het geheugen iets wat een 2-dimensionale array benadert, maar het is natuurlijk niet hetzelfde (elke subarray kan een andere lengte hebben en zo kun je dus bijvoorbeeld ook driehoekige matrices alloceren); er zijn onder andere wat performance issues.

Een tweede belangrijk punt is dat het voor memory management in principe veel eenvoudiger is om de caller de benodigde geheugenallocatie te laten doen en mee te laten geven aan de callee. Op die manier is het ook gewoon mogelijk om de resultaten op te slaan in een variabele op de stack, in een globale variabele, of het geheugen met een alternatieve allocator te alloceren. Uiteraard moet de grootte van het geheugen dan wel van te voren bekend zijn bij de caller.

Verder doet strcpy niets met allocatie, zoals ook in de documentatie te lezen is. In C++ is de mooiste manier van initialisatie van arrays gewoon met een for-lusje, als is memcpy (een beetje een C-techniek) ook wel bruikbaar. Strcpy wordt alleen gebruikt als je strings (dus pointers naar een stukje zero-terminated geheugen) heen en weer wilt kopiëren. Denk in alle gevallen goed na over de grootte van de blokken geheugen waar je mee te maken hebt.

Verwijderd

Topicstarter
owk bedankt voor de antwoorden...
Heb idd geen uitgebreide kennis van C++, ken wel een aardige basis maar het is al weer een tijd terug.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 21-08 12:02

.oisyn

Moderator Devschuur®

Demotivational Speaker

Voor de topicstarter: onderstaande post maakt het in principe alleen maar lastiger en kun je denk ik dus beter overslaan ;)
Soultaker schreef op 06 oktober 2003 @ 15:43:
Je kunt lokale, globale en member variabelen wel met meerdere dimensies declareren, maar dat is eigenlijk meer uitzondering dan de regel; met argumenten en return values kan het zeker niet (want dat zijn altijd gewoon domme pointers).
Dat is natuurlijk ook niet helemaal waar, alleen waar je wel mee zit is dat alleen de hoogste dimensie variabel mag zijn. Of anders gezegd: je kunt ook pointers hebben naar arrays met een vaste lengte. Datgene waar de pointer naar wijst is een array van vaste grootte, en ptr+1, oftewel ptr[1], wijst naar zo'n zelfde array wat daarna komt

C++:
1
2
3
4
5
int a[10];
int (*ptr)[10] = &a;  // ptr is een pointer naar a

// en dit kan dan ook gelijk:
ptr = new int[10][10];

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

Topicstarter
Ik heb 't een en ander geprobeerd, maar het wil toch niet lukken...

Kan iemand niet even een simpel voorbeeldje geven hoe je (ik durf de term meerdimensionale array niet meer te zeggen) een char** vult en retourneert?

[ Voor 8% gewijzigd door Verwijderd op 06-10-2003 16:08 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 01:56
Nee, ga zelf maar proberen. Loop je vast? Geen probleem, leg maar uit wat je precies probeert en wat er mis gaat, dan kunnen we daar wel weer uitleg bij geven. Simpelweg goede code neerplanten (als dat al zou kunnen, want ik snap nog steeds niet precies wat de bedoeling is) daar leer je niets van; bovendien zijn er verschillende goede oplossingen mogelijk.

Als je per se voorbeeldcode wilt hebben, kun je wel even zoeken op GoT; er is wel regelmatig en recentelijk over gediscussieerd hier.

<flauw>
C++:
1
2
3
4
5
char **foo()
{
    char *bar = "baz!";
    return &bar;
}

</flauw>
(Toch niet zo heel flauw, want het illustreert wel dat je met "hoe vul ik een char**" je probleem niet duidelijk genoeg omschreven hebt.)

edit:
@.oisyn: die code heb ik in beraad... dat werkt inderdaad wel, maar de casts zijn niet echt mooi en volgens mij is het ook onmogelijk om zo'n array door te geven als argument (of return value), zonder dat de caller (respectievelijk caller) als nog moet gaan zitten casten. Ik moet zeggen dat ik new[] zelf nooit heb gebruikt om een meerdimensionale array te alloceren; was me er niet van bewust dat dat gewoon kon. :)

[ Voor 56% gewijzigd door Soultaker op 06-10-2003 16:17 ]


Verwijderd

Topicstarter
Nou ik heb dus al een tijd op GoT gezocht. Daar heb ik wel wat voorbeelden gezien met alloc maar niet met new. Ik snap ook niet wat er nou onduidelijk is aan de beschrijving. Ik wil gewoon een pointer variabele vullen met "strings". Een char** dus lijkt me. Alleen het geheugen alloceren wil niet echt lukken.

Ik heb dit geprobeerd door eerst het aantal strings te alloceren:
test = new char[3] (moeten 2 strings in komen)

en vervolgens per string ruimte te alloceren door:
*test = new char[7] (m'n strings zijn 6 karakters)

Maar dit werkt dus niet bepaald en weet niet hoe ik het wel kan doen. Misschien omschrijf ik het niet zo, maar heb zelf ook liever geen kant en klare oplossing.

  • BoAC
  • Registratie: Februari 2003
  • Laatst online: 21-08 13:30

BoAC

Memento mori

Als je nu een DWORD array aanmaakt dus:
C++:
1
char **p = new char *[10];

Nieuwe substrings aanmaken
C++:
1
p[1]=new char[10];

Als je die char's wil benaderen:
C++:
1
sprintf(p[1], "test");

[ Voor 48% gewijzigd door BoAC op 06-10-2003 16:51 . Reden: ff verbeterd ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 21-08 12:02

.oisyn

Moderator Devschuur®

Demotivational Speaker

Soultaker schreef op 06 oktober 2003 @ 16:13:
@.oisyn: die code heb ik in beraad... dat werkt inderdaad wel, maar de casts zijn niet echt mooi en volgens mij is het ook onmogelijk om zo'n array door te geven als argument (of return value), zonder dat de caller (respectievelijk caller) als nog moet gaan zitten casten. Ik moet zeggen dat ik new[] zelf nooit heb gebruikt om een meerdimensionale array te alloceren; was me er niet van bewust dat dat gewoon kon. :)
ok, arrays moeten natuurlijk altijd by-reference, maar je kunt ze natuurlijk wel gewoon meegeven als parameters en teruggeven als returnwaarde.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
// parameter
void func (int (*array)[100], int height)
{
    for (int y = 0; y < height; y++)
        for (int x = 0; x < 100; x++)
            doeIets (array[y][x]);
}

// returnwaarde
int (*new2DArray (int height)) [100]
{
    return new int[height][100];
}


en met template truucjes wordt het nog veel leuker:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void func (int * array, int size)
{
    std::cout << "size = " << size << std::endl;
}

// een wrapper voor die func van boven:
template <int SIZE>
void func (int (&array)[SIZE])
{
    func (array, SIZE);
}

int main ()
{
    int a1[10];
    int a2[3];

    func (a1);
    func (a2);
}


Wat je met die casts bedoelt is me niet echt duidelijk

[ Voor 3% gewijzigd door .oisyn op 06-10-2003 16:40 ]

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

Topicstarter
Ja dat zocht ik Boac!
bedankt voor jullie antwoorden

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 21-08 12:02

.oisyn

Moderator Devschuur®

Demotivational Speaker

BoAC schreef op 06 October 2003 @ 16:35:
C++:
1
sprintf(p[1], "test");
sprintf is evil, zeker voor dit soort dingen. Gebruik gewoon strcpy (), maar aangezien je toch met C++ bezig bent kun je beter std::string gebruiken, en std::ostringstream als je een string wilt maken van verschillende componenten (stringstream is van stream wat sprintf van printf is zeg maar ;))

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.


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 17:14
Waarom doe je eigenlijk zo moeilijk?

Met C++ types in plaats van C types wordt C++ veel makkelijker:
code:
1
2
3
4
5
6
7
8
9
10
typedef std::vector<std::string> string_list;

string_list voorbeeld() {

  string_list ret_val(16);

  for(int i=0;i<16;++i)
      retval[ i ]  = getword(i);
  return retval;
}

[ Voor 3% gewijzigd door MSalters op 06-10-2003 17:41 . Reden: [i] is niet zo handig in [code] ]

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


  • BoAC
  • Registratie: Februari 2003
  • Laatst online: 21-08 13:30

BoAC

Memento mori

.oisyn schreef op 06 October 2003 @ 17:39:
[...]


sprintf is evil, zeker voor dit soort dingen. Gebruik gewoon strcpy (), maar aangezien je toch met C++ bezig bent kun je beter std::string gebruiken, en std::ostringstream als je een string wilt maken van verschillende componenten (stringstream is van stream wat sprintf van printf is zeg maar ;))
Ik noemde maar een voorbeeld ;) (Ben niet zo thuis in std vandaar ;) )

[ Voor 5% gewijzigd door BoAC op 06-10-2003 22:12 ]

Pagina: 1