Tablica wskaźników na tablicę

0

Witam,

Otóż mam dziwny problem, potrzebuję wygenerować załóżmy 1000 słów i je gdzieś przechować. Brzmi banalnie, ale mam pewien problem. Jest funkcja zwracająca adres tablicy z wygenerowanym słowem:

char* generate_word()
{
    char tab[max_word+1];
    int n, i = 0;
        n=rand()%(max_word-min_word+1)+min_word;
        for(i; i<n; i++) tab[i] = rand()%26+65;
        tab[i] = '\0';
    return tab;
}

Więc tworze tablicę wskaźników - 1000 elementową:

#include "functions.h"
#include "variables.h"
#include <time.h>

using namespace std;

int main()
{
    srand(time(NULL));
    char* words[size]; 
          for(int i=0; i<size; i++) words[i]=generate_word();
          for(int i=0; i<size; i++) cout << words[i] << "\n";
          system("pause");
    return 0;
}

Niestety, wszystkie słowa są takie same, jednak adresy na jakie pokazują wskaźniki są różne...
Dodam, że dla:

for(int i=0; i<size; i++) cout << generate_word() << "\n";

Wszystko działa, wyświetlają się różne słowa, oraz dla:

words[0] = generate_word();         
cout << words[0] << "\n";
words[1] = generate_word(); 
cout << words[1] << "\n";

Również jest OK, jednak dla:

words[0] = generate_word();
words[1] = generate_word();          
cout << words[0] << "\n";
cout << words[1] << "\n";

Już działać nie chce, i wyświetlają się znów te same wyrazy.

Dodaję jeszcze wartości stałych:

const int size = 1000,
          min_word  = 4,
          max_word  = 15;

Dziwny problem, jeśli ktoś pomoże będę bardzo wdzięczny.

1

Primo, niee.... PRIMO !! W C++ do przechowywania słów używa się stringa. Jeśli nie masz konkretnych nakazów to polecam porzucić cywilizację i przenieść się do jaskinii lub conajmniej przywdziać zbroję i kupić kuca jeśli nie chcesz iść z duchem czasu :P
2. Co to za dziwne liczby 26 i 65? ASCII? Poszukaj dlaczego nie powinieneś tego robić.
3. Rozwiązanie problemu http://vijayinterviewquestions.blogspot.com/2007/07/write-c-code-to-return-string-from.html

Czyli np

#include <time.h>
 #include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

int max_word = 6;
int min_word = 3;
char* generate_word()
{
    char* tab;
    int n, i = 0;
        n=rand()%(max_word-min_word+1)+min_word;
        tab = (char*)malloc(sizeof(char)*n+1);
        for(i; i<n; i++) tab[i] = rand()%26+65;
        tab[i] = '\0';
    return tab;
}

int main()
{
    int size = 10;
    srand(time(NULL));
    char* words[size];
          for(int i=0; i<size; i++) words[i] = generate_word();
          for(int i=0; i<size; i++) cout << words[i] << "\n";
          system("pause");
          for(int i=0; i<size; i++)
          free(words[i]);
    return 0;
}
 

Fajnie by było jakbyś zapytał co jak i dlaczego a nie tylko zerżnął ten kod bo przy następnym programie znowu nie będziesz wiedział co robisz źle. Decyzję pozostawiam Tobie.

1
Malootki napisał(a):
#include <time.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
 

jak c++ to nie c, jak c to nie c++ zdecyduj się - skoro mówimy już o cywilizacyjnych wynalazkach.

#include <ctime>
#include <iostream>
#include <cstdlib>
#include <cstring>
1

Błędem jest, że w funkcji 'generate_word' zwracasz adres tablicy, która jest zmienną lokalną. Stąd nieprawidłowości i/lub takie same napisy. Po wyjściu z funkcji nie wolno odwoływać się do jej zmiennych lokalnych bo już nie istnieją!!!

#include <string>
#include <cstdlib>
#include <ctime>

using namespace std;

char generate_letter()
{
    return 'A' + rand() % ('Z' - 'A' + 1);
}

string generate_word(unsigned length)
{
    string ret;
    while (length-- > 0) ret += generate_letter();
    return ret;
}
 
int main()
{
    srand(time(NULL));
    string words[size];
    for(int i = 0; i < size; i++) words[i] = generate_word(...
3

Pozwolę sobie na parę słów komentarza.

char generate_letter()
{
    return 'A' + rand() % ('Z' - 'A' + 1);
}
 
string generate_word(unsigned length)
{
    string ret;
    while (length-- > 0) ret += generate_letter();
    return ret;
}

Powyższy kod jest dużo wolniejszy niż kod autora topicu (nawet zmieniając generate_letter na inline).
Mianowicie mamy tutaj obiekt ret, do którego, dla każdej literki, wywoływany jest operator+=(char) (chociaż pół biedy, że to nie operator+ ;>).
W każdym razie operator+= dla każdego chara zrobi this->push_back(ch), który to zacznie od sprawdzenia czy capacity bufora jest odpowiednie.
Jeśli nie jest, zostanie wywołane this->reserve(obecna długość + 1). Zakładając słabą implementacje metody reserve dostajemy alokacje pamięci, kopiowanie i uwolnienie pamięci w każdej iteracji funkcji, co jest bardzo bardzo wolne. Nawet zakładając dobrą implementację reserve (alokującą więcej pamięci niż potrzeba) i tak tych alokacji i kopiowań i uwalniań będzie sporo dla dużych stringów.
W związku z czym powyższy kod jest wolniejszy niż kod autora topicu.

Oczywiście można to fixnąć na kilka sposobów.

  1. użyć ret.reserve(length) na początku funkcji.
  2. wygenerować słowo w tablicy charów (mimo, że to C++ ;>) po czym zrobić return string(wygenerowana_tablica)
  3. można też użyć np stringstream które służy do składania stringów, natomiast w tym wypadku będzie to i tak wolniejsze niż metoda 2 (zresztą, będzie też wolniejsze niż metoda 1)

Primo, niee.... PRIMO !! W C++ do przechowywania słów używa się stringa. Jeśli nie masz konkretnych nakazów to polecam porzucić cywilizację i przenieść się do jaskinii lub conajmniej przywdziać zbroję i kupić kuca jeśli nie chcesz iść z duchem czasu :P

Z tego co ostatnio sprawdzałem, tablice charów są dozwolonym konstruktem w najnowszym standardzie C++. Więc, zgodnie z duchem czasu, najnowszymi standardami, etc, nadal można używać tablic charów :)
Nie widzę więc sensu krytykowania autora topicu (bo to raczej nie była krytyka jego kodu ;>) za używanie tablic charów jeśli jedynym argumentem ma być "bo to jest niezgodne z duchem czasu" (cokolwiek to znaczy).

To powiedziawszy, zgadzam się jednak, że w miarę możliwości użycie std::*string jest rozsądniejsze niż operacje na tablicach charów, ale nie z powodu "niezgody z duchem czasu", a z powodów związanych z bezpieczeństwem - bardzo trudno jest napisać poprawny złożony kod operujący na tablicach charów.

Poza tym... Chyba zgodnie z duchem czasu Malootki powinien wskazać raczej std::wstring lub std::u16string lub std::u32string jako odpowiednią klasę do przechowywania stringów w naszym międzynarodowym świecie :)
No i użyć do alokacji new, a nie malloc...

tab = (char*)malloc(sizeof(char)*n+1);

Ale uff, na szczęście że std::malloc załapał się jeszcze do najnowszego (tj. zgodnego z duchem czasu ;p) standardu C++, więc to nadal jest C++, a my możemy spać spokojnie ;)

1

Niech autor najpierw napisze działający kod, później może się martwić o optymalizację jeśli w ogóle zajdzie taka potrzeba.

1 użytkowników online, w tym zalogowanych: 0, gości: 1