Tömb méretének meghatározása

 ( pelz | 2008. szeptember 9., kedd - 19:31 )

Sziasztok!

Kérdés: Meg lehet-e határozni egy string tömb méretét, ha egy függvényben átadjuk, mint paraméter?

int fv(std::string t[]) {
  int count;
  // hány elemből áll a tömb??????
  return count;
}

int main() {
  std::string tomb[] = {"egy", "ketto", "harom", "negy", "ot"};
  int meret = fv(tomb);
  // stb...
  return 0;
}

Én egyelőre nem jöttem rá, hogyan lehetséges. A sizeof(t) minden esetben csak 4-et ad vissza. Valószínűleg, mert egy pionter tulajdonképpen. Szerintetek, meg lehet határozni a tömb méretét?

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

törölve. Rosszul értelmeztem a kérdést :)

-----------------------------
Debian "lenny", 2.6.24-amd64

Nem jött be. Ezeket a hibaüzeneteket kaptam:

src/x.cpp:6: error: request for member ‘size’ in ‘t’, which is of non-class type ‘std::string*’
src/x.cpp:7: error: request for member ‘length’ in ‘t’, which is of non-class type ‘std::string*’

Tudom, mert én azt hittem a string méretét akarod megtudni ;)

-----------------------------
Debian "lenny", 2.6.24-amd64

Nem lehet meghatározni. Ez csak egy jelölés, hogy ez egy tömbként kezelt mutató. Valójában a fenti a következővel ekvivalens:

int fv(std::string *t);

Használj vektort:

int fv(std::vektor t) {

...
t.size()
t[i]
}

Ja. Vector esetén nem nagy kunszt.

Ez így van, de std::string tömb nem előre ismert mérettel háát kissé furcsa elképzelés, ha a tömböt át lehet adni, akkor a hosszát is. Bár ezek után kérdéses, hogy érdemes-e C++ kódot írni, ha tele van C-s kódrészletekkel ott is, ahol van C++-nak megfelelő megoldás.

Mondjuk kerdes, h a vector mekkora memory overhead a string[] -hez kepest. Mert ugye a string[] az valahol fix meretu, csak a hivott fuggveny nem fog rajonni a meretere, kiveve ha nem valami #define -ban van meghatarozva (ami ugye preprocesszalaskor konstans szam lesz). A vector ellenben elvben vegtelen meretu.
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Ez nyilván STL implementációs kérdés, de az biztos, hogy nem nagy.
A "GNU ISO C++" implementációban pl a vektor összesen 3 mutatóval van implementálva. Ha úgy tetszik a string[] egy mutató, ha a méretét is átadod, akkor az egy mutató, és egy int (ami kb 2 mutató).
Azaz a memory overhead 1 mutató. Szerintm elfogadható a kapott szolgáltatások fényében...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

De ott is van egy lefoglalt adatterület, ami nagyobb lehet, mint amit a vector-ban lévő elemek igényelnének, nem?

KisKresz

Persze hogy lehet nagyobb, különben a beszúrás ideje nem lenne konstans nagyságrendű. Előre lefoglal valamekkora területet, és amíg tud, addig abba szúr bele, aztán ha már nem megy, akkor lefoglal egy nagyobbat. Ha csak akkora helyet foglalna le, amennyi az épp aktuális elemszámnak kell, akkor viszont minden egyes beszúrás lineáris lenne, vagyis n elem esetén n ideig tartana, ami gyakorlatilag használhatatlanná tenné.

A beszúrás (insert) lineáris.
Vektornál csak a push_back konstans(*). (Gondolom erre gondoltál.)

(*) Valójában persze nem mindig konstans. Ha a konstruktorban megadod a vektor méretét, akkor akkora területet fog lefoglalni. Ha túlléped a keretet, akkor lefoglal egy 2x akkora területet, és az elejére másolja a már bent lévő elemeket.
Emiatt átlagosan minden elemet 1x másol le.
Egyébként a vector::capacity visszaadja a lefoglalt területet, a vector::reserve pedig lefoglal. Így igény szerint testreszabható.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Igen, valóban a push_back()-re gondoltam. Az a többet használt beszúrás...

Az insert() lineáris, hiszen átlagosan az elemek felét másolnia kell minden egyes alkalommal, hogy az elemek elérése is konstans időben történhessen (operator[] és at()).

Bár azt megnézném, hogy az egyes megvalósítások ezen feltételezéseknek mennyire tesznek eleget.

Ezek felső határát a szabvány rögzíti...

http://www.tantalon.com/pete/cppopt/appendix.htm#AppendixA

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

tömb mérete: sizeof(tomb)/sizeof(tomb[0])
De csak a main-ből, nem a függvényből.

A függvényen belül tehát reménytelen?

Igen. Max átadod paraméterként, ha a fv-ben van rá szükség.

Ha parameterkent kaptad, remenytelen, ha globalis/lokalis valtozo, akkor mukodik.

_________________________________________________________________________
"ONE OF THESE DAYS I'M GOING TO CUT YOU INTO LITTLE PIECES!!!$E$%#$#%^*^"

OK!

Köszönöm szépen a hozzászólásokat.

std::string tomb[] = {"egy", "ketto", "harom", "negy", "ot", NULL};

?

Valami ilyesmin töröm a fejem éppen.

Majd az x0-ban. Abban vannak ilyen inicializátorok. (null nélkül) :)

Az csak annyit segít, hogy írhatod ezt:
std::vector<std::string> tomb = {"egy", "ketto", "harom", "negy", "ot"};

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Nem gonodltam többre én sem.

Qt-ben találták ki ezt:

QVector<QString> vect;
vect << "egy" << "ketto" << "harom";

Ennek igazából semmi akadálya std::vector-ral sem, leszámítva, hogy nem lehet az std névtérben a túlterhelt <<.

template <typename T>
inline std::vector<T>& operator<<(std::vector<T>& v,const T& t)
{
  v.push_back(t);
  return v;
}


inline std::vector<string>& operator<<(std::vector<string>& v,const char* t)
{
  v.push_back(string(t));
  return v;
}

int main() 
{
  vector<string> test;
  test << "egy" << "ketto";

  cout << test.size() << endl;

  return 0;
}

Persze a C++0x tisztább, szárazabb, biztonságosabb érzés lesz...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Érdekes ötlet - tetszik. :)

Szerintetek ez nagy hülyeség?

#include <iostream>

bool doHaveAnyControlChar(std::string& s) {
    bool has = false;
    for (char c = 0; c < 32; c++) {
        if (s.find(c) != std::string::npos) {
            has = true;
            break;
        }
    }
    return has;
}

int getStringArrayLength(std::string array[]) {
    int length = 0;
    while (!doHaveAnyControlChar(array[length])) {
        length++;
    }
    return length;
}

int main() {
    std::string anArray[] = {"egy", "ketto", "harom", "negy", "ot", "hat"};
    std::cout << "\n A tömb mérete: " << getStringArrayLength(anArray) << "\n";
    return 0;
}

Ez túlcímez a tömbön, nem?

KisKresz

Igen, ez elég nagy hülyeség. Az std::string megvalósításától függ, hogy mi van benne, arról meg nem tételezhetsz fel semmit. Ennek a kódrészletnek akármi (fals negatív, fals pozitív és akár programfagyi is) lehet az eredménye.

Szerk: A post megírása után felébredtem, persze először mást láttam bele, bár a lényegen nem változtat ;-)
A legnagyobb gond az, hogy a tömb utáni memóriaterületet próbálod stringként felhasználni, pedig lehet ott bármi. A másik gond az, hogy a string tartalmában keresel 0-31-ig karaktereket, amit jó eséllyel soha nem fogsz találni.

Nos, a program érdekessége, hogy mégiscsak működik. Tehát visszaadja a tényleges tömbméretet.

Ha feltételezem, hogy a string nem tartalmaz vezérlő karaktereket, akkor talán használható is.

Vagy talán "zamboriz" javaslata lenne a legkorrektebb:

Idézet:
std::string tomb[] = {"egy", "ketto", "harom", "negy", "ot", NULL};

Ezzel a megoldással elég jól lehetne detektálni a tömb végét.

Nem, nem működik. Erre:

int main() {
    //std::string anArray[] = {"egy", "ketto", "harom", "negy","","","","\t","", "ot", "hat"};
    std::string anArray[] = {"egy"};                                                                                                        
    std::string megegy = "haha";
    std::cout << "\n A tömb mérete: " << getStringArrayLength(anArray) << "\n";
    return 0;
}

nekem éppen most 3-at ad vissza.
Szerintem is a vector lenne a természetes megoldás.

KisKresz

Ráadásul a vector/paraméterként átadott méret konstans idő alatt oldja meg a feladatot, míg az ilyen hekkelős, platform/minden-függő félmegoldások pedig a legjobb esetben is lineárisak lesznek. Ha a vectort referenciaként, esetleg konstans referenciaként adod át, akkor még a másoló operátor miatti overhead sem lesz. Nem vesztesz semmit, viszont biztonságosabb, szabványosabb kódot kapsz eredményül. A kérdés sokkal inkább az, hogy miért ragaszkodsz ehhez, ha létezik rá jó és gyors megoldás.

Felejtsd el ezeket a baromságokat.

Most épp túlindexelsz a tömbön, és azt a területet akarod string objektumként értelmezni. Viszont a string objektum nem a karaktereket tartalmazza, hanem pl. egy mutatót a karakterekre, illetve egy számot a mutató által mutatott karakterek számával. (Persze más megvalósítás is lehetséges, sőt)
Azaz az array tömb utáni szemetet értelmezed mutatóként, illetve számként.
Mivel a program most indult, ezért teljesen véletlenül akkora mázlid van, hogy csupa nulla van ott, amit üres stringként értlemez.

Már a túlindexelés is segfault-ot eredményezhet, a szemét string-ként értelmezése szinte biztosan.

Két dolgot tehetsz:
a) használj vektort
b) ne használj string-et se

Mindenképp az a)-t ajánlom.

Nem látok semmi okot a b)-re de az így nézne ki:

int getStringArrayLength(char** strs) {
    int length = 0;

    while (strs && strs[length]))
        ++length;

    return length;
}

int main() {
    char* anArray[] = {"egy", "ketto", "harom", "negy", "ot", "hat",0};
    std::cout << "\n A tömb mérete: " << getStringArrayLength(anArray) << "\n";
    return 0;
}

Más variáció nincs.

Ez nem jó:

std::string tomb[] = {"egy", "ketto", "harom", "negy", "ot", NULL};

Itt már említettem, hogy C++-ban nincs NULL.

Ha mégis van definiálva, akkor valószínűleg 0-nak. (De ez nem szabványos.) Ezt a 0-t kapja majd meg a string konstruktora, és mivel olyan konstruktora nincs, ami egész számot vár, ezért cast-olódik char*-ra.
Ami azért gond, mert pl MinGW alatt ez rögtön elszáll.
Ha véletlenül nem (mert abban az STL implementációban ellenörzik, hogy 0-t kap-e), akkor is legjobb esetben üres string-et kapsz. Azt meg nem lehet megkülönböztetni a ""-tól.

Használj vektort.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Köszönöm a további hozzászólásokat is!

Személy szerint én is a vector tároló használatát részesítem előnyben, csak elméletileg szerettem volna tisztázni a tömbök lehetőségeit. Jó, hogy megalkották az STL-t! Ezzel könnyebb a programozás.

Szia Pelz! ;)
Csak nehogy túlbonyolítsd a dolgot, ha még a Qt vizeken evezel, lehet hogy nem más mint a QStringList tökéletesen kiszolgálja az igényeidet. Már ha jól értem a dolgot :D

Hi dii!

Kösz! Nem konkrét programozási probléma megoldása miatt indítottam a témát, csak elméletileg érdekelt a kérdés.

(Info: A legutóbbi, általad is látott, alkalmazás forráskódját felraktam a qt-apps.org -ra.)

Esetleg a string == operatora miatt lehet meg ilyent csinalni, de ez mar nagyon eroltetett:

std::string ary[] = { "1", "2", "3", "4", "5", "\xff" };
// Blank iteration code
for(std::string *s = &ary; *s == "\xff"; s++);

es akkor a "\xff" a vegejel.

Masik verzio, ha a feladat megengedi, hogy ures string a vegejel (""), es akkor csak a stringek hosszat figyelni, hogy mikor epp 0.

De a vector nyer minden fronton.
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Szerintem std::string-et hülyeség C-és array-be rakni.

Helyesen:

void fv( const vector< string > &strs ) {
unsigned size = strs.size();
....
}

Esetleg vector helyett list.