string egyetlen karakteréből egészet

Fórumok

Üdv!

Egy szöveges, kézzel vi-jal szerkeszthető fájlban (adat.text) számjegyek vannak egymás alatti sorokban, pölö így:

12345624
27638744
17622357
28454989

a sorokat egyesével be tudom olvasni ciklusban egy stringbe (s), de csak a lenti módon sikerült elérnem, hogy pölö a 4. karaktert egész számként használhassam.

ifstream befajl;
string s;
char c;
int i;
befajl.open ("adat.text");
getline(befajl,s);
c=(char) s[3];
i=atoi(&c);
i=i*3;
cout << i << endl;
befajl.close();

Van valamilyen egyszerűbb/elegánsabb módja, hogy elérjem a célomat C++ alatt?
Próbálkoztam még ezzel is:

char * cp;
c=befajl.get();
cp=&c;
i=atoi(cp);

Sajnos ebben az esetben a hasznos stringek utáni fehérkaraktereket is beolvassa a fájlból a program. (Persze ciklusban megadhatnám, hogy egy-egy sorban hány darab karaktert akarok beolvasni, de hogyan ugrok a következő sorra, kihagyva az adott sor nem érdekes részét?)

Köszi!

Hozzászólások

1. Tessen hasznalni a [code] BBCode taget. Baromi olvashatatlan igy a kod.
2. Lehet karakterenkent is olvasni a file-t (ekkor persze inkabb tisztan C-ben erdemes lekodolni), es akkor kiporgeted (fseek) a szamodra erdektelen reszt, beolvasod a szamjegyet, es atoi. De ha inkabb a feladatot mondanad el, ugy tomondatokba, mindenki jobban jarna. Mert amennyire elnezem, teljesen felesleges ennyire c++ oldani meg a dolgot. Raadasul csak felig C++ mert a cin-t ki se hasznalod.


getline(befajl,s);
i = s[3] - '0';

Annyit még lehetne kérni, hogy elmondd, miért ez a jó megoldás?
A '0' ebben a formában egy karakter konstans, nem? Ennek az ASCII értéke mintha 38 lenne, de erre már nem emléxem. Önmagában az s[3] nem az s string 4. karakterét jelenti ezexerint? Miért kell kivonni belőle a nulla karaktert? Ez így ebben a formában nem a karakterlánc terminátor ugye? Ha jól sejtem a string objektum nem nulla bájtos végű karakterláncot jelent, hanem az objektumban el van tárolva a string hossza és onnan lehet tudni terminátor nélkül is, hogy mekkora a karakterlánc.
Köszönöm!
--
unix -- több, mint kód. filozófia.
Life is feudal

és ezt így nyersen bele lehet tolni egy integerbe? Ez kicsit hasonlít arra, amikor anno a commodore plus 4-esen assemblyben attól függően mást eredményezett a 0ah memóriába írása, ha a képernyőmemóriáról, vagy a szín memóriáról volt szó... Maga a byte ugyanaz, csak máshol mást jelent. Itt attól függően mást jelent az adott helyen található byte, hogy milyen típusú változóba kerül?
--
unix -- több, mint kód. filozófia.
Life is feudal

Egy nulla byteos karaktertömb egy eleme önmagában csak egyetlen karakter, nulla nélkül, nem? Akkor miért volt szükség erre:
"s[3] - '0'"?
Ha egy byteon tárolódik egyetlen karakter, akkor s[3] mérete egy byteos, nem? A fenti sor nem azt jelenti, hogy egy nullás írásjelet von ki egyetlen karakterből?

"Tehát s[3]-'0' a 0 KARAKTERTŐL való távolságot nézi - spec. hogy ez melyik SZÁM."

Történetesen ezt a fenti sort nem értem. Illetve nem vagyok biztos abban, hogy jól értem a kivonás formájú írásjellel megvalósított operátor szerepét. Nyilván nem aritmetikai kivonásról van szó, nem? Csak egy nagyon hasonló dologról? Ugyanígy ha s[3] történetesen egy B írásjel lenne, akkor s[3] - 'A' eredménye egy lenne? Mondhatjuk, hogy a '-' jel olyan "távolságmegmondó" operátor a std::stringek esetén (vagyis nem std::string, csak az std::string egyetlen karaktere)?

És még ez sem világos:
Ha s egy std::string típusú változó valamilyen értékkel, akkor s[3] típusa micsoda? std::string[3]? Hogy hívjuk ezt a típust?

Most végül is s string egy nulla végű karakterlánc, vagy egy olyan objektum ami tárolja egy adattagban a hosszát?
--
unix -- több, mint kód. filozófia.
Life is feudal

Mint már írtam, a string [] operátora egy char értéket ad vissza, tehát s[3] egy sima char. A megértéshez azt kell tudni, hogy a karaktereket is számmal tárolja a gép, általában ASCII kódtábla szerint. Tehát ha van egy char változód, amiben leírva a '4' szám van, az valójában 52-t tartalmaz. A '0' számjegy a kódtáblában a 48-as értékű, ha jól emlékszem. A kivonás a szokásos módon működik, egy egyszerű művelet ezen két egész között. Tehát ha az s[3], ami az s 3. karaktere egy '4'-es, akkor a művelet így néz ki: '4'-'0', egészekre lefordítva: 52-48. Ennek az értéke természetesen 4, amit már be lehet pakolni az int-be.

Most végül is s string egy nulla végű karakterlánc, vagy egy olyan objektum ami tárolja egy adattagban a hosszát?

Az s változót te hoztad létre, méghozzá string típussal. A string egy objektum.
A null végű karakterlánc így nézne ki:

char s[14];

Javaslom valamilyen könyv nézegetését a témában, vagy vannak jó online források is, pl www.cppreference.com

Köszönöm a választ! Valahol a filerendszeren belül meg lehet találni a std::string osztály leírását (Deklaráció?)? Gondolom, ha belenézek a cpp kódba, akkor látni fogom, hogy mi alapján tudja magáról egy std::string, hogy mekkora a mérete.
Nézegetek könyveket.
--
unix -- több, mint kód. filozófia.
Life is feudal

Nálam most épp:
"MinGW\include\c++\3.4.2\bits\basic_string.h"

Linux alatt értelemszerűen. Ezzel egyébként nem leszel beljebb. :)

A lényeg, hogy az iso szabvány nem rendelkezik a belső ábrázolásról. Legtöbbször a méretet folyamatosan nyilvántartja egy változóban, és a karaktereket egy folytonos területen tárolja a memóriában.

De persze elképzelhető lenne egy olyan megvalósítás, hogy a string tartalmát valahány karakteres szakaszokban tárolja egy listában, ezzel felgyorsítva az összefűzést, beszúrást, stb.

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

Egy kis elgondolkodtato…
Spolsky altal leirt a problema a sztring hasznalatakor itt:

http://hungarian.joelonsoftware.com/Articles/BacktoBasics.html

Vissza az alapokhoz


Szerző: Joel Spolsky
Fordította: Krisztián Gyuris
2001. December 11.


Általában sok időt töltünk az olyan magasszintű dolgok megvitatásával, mint a .NET és a Java összehasonlítása, XML stratégia, a Lock-In, versenystratégia, szoftver tervezés, architektúra és így tovább. Ezek a dolgok csak egy rétege a tortának, ha úgy tetszik. A legfelsőbb réteg a stratégia. Eggyel alatta az architektúrák vannak pl .NET és ez alatt pedig olyan fejlesztői eszközök, mint a Java vagy platformok, mint a Windows.

Menj lejjebb egy szintet. Dll-k? Objektumok? Eljárások? Nem! Lejjeb! Addig a pontig ahol már a programsorokról van szó.

Még mindig nem elég. Ma a CPU-król szeretnék beszélni. Egy kis darab szilikonmorzsa, amiben bájtok szaladgálnak. Tegyél úgy egy pillanatra mintha kezdő programzó lennél. Felejts el mindent, amit idáig tudsz a programozásról, a menedzsmentről és menj vissza a Neumann féle elmélethez. Felejtsd el a J2EE-t egy pillanatra. Gondolkodj bájtokban.

Miért erre szükség? Azt hiszem néhány hiba, amit az emberek elkövetnek a stratégia szintjén abból fakad, hogy egyes alapelveket nem teljesen vagy egyáltalán nem értenek. Egy gyönyörű palotát építettél, de az épület gyenge alapokon nyugszik. Így aztán egy jó erős beton alap helyett egy gyenge alapra építkezel. A palota jól néz ki, de a csap leesik a a fürdőszobában és gőzöd sincs, hogy miért.

Most vegyünk egy nagy levegőt. Tarts velem, egy kis gyakorlatra.

Emlékszel hogyan működnek a C sztringek: egy csomó bájtot tartalmaznak, amit egy null karakter követ (ennek az értéke nulla). Ennek két nyilvánvaló következménye van:

1.Nem tudjuk megmondani a sztring hosszát anélkül, hogy, végig ne menjünk rajta a null karakterig.

2.Nem lehetnek nullák a sztringben. Tehét bináris állományok, mint például JPEG tárolására nem alkalmas egy C sztring.

Miért működnek a C sztringek így? Azárt mert a PDP-7 mikroprocesszor, amelyen megalkották a UNIX-t és a C programozási nyelvet rendelkezett ASCIIZ sztring típussal ASCIIZ “ASCIIZ Z-vel (zéróval) a végén”.

Ez az egyetlen mód arra, hogy sztringeket tároljunk? Nem, valójában ez az egyik legrosszab megoldás. Egyszerűbb programoknál, programozási felületeknél, operációs rendszereknél, osztálykönyvtáraknál, óvakodnod kell az ASCIIZ sztringektől, mint a pestistől. Miért?

Kezdjük azzal, hogy megirjuk a strcat függvényt, amivel egy függvényt összefűzhetünk egy másikkal.


void strcat( char* dest, char* src )
{
     while (*dest) dest++;
     while (*dest++ = *src++);
}

Ha jobban megnézed a kódot könnyen rájöhetsz hogyan is működik. Elöször, végigmegyünk az első sztringen a nulla karakterig. Amikor megtaláltuk, végigmegyünk a második sztringen, egyenként átmásolva a karaktereket.

Ez a fajta sztring kezelés és összefűzés elég jó Kernighan-nek és Ritchie-nek, de megvannak a hátrányai. Itt van mindjárt egy probléma. Tegyük fel, hogy van egy rakás név, amit egy sztringgé szeretnénk összefűzni:


char bigString[1000];     /* Nem tudom, hogy milyen hosszú lesz... */
bigString[0] = '\\0';
strcat(bigString,"John, ");
strcat(bigString,"Paul, ");
strcat(bigString,"George, ");
strcat(bigString,"Joel ");

Ez így működik ugye? Igen. És egy tiszta és szép megoldás.

Milyen a teljesítménye? Olyan gyors amilyen csak lehet? Jól skálázható? Ha egymillió sztringünk lenne összefűzésre, akkor is ezt a megoldást használnánk?

Nem. Ez a kód a “Shlemiel féle festő algoritmust” használja. Ki az a Schemiel? Ime a vicc:

Schemielt felveszik útkarbantartónak, Neki kell az elvalasztóvonalat festeni az út közepére. Az első napon magához vesz egy vödör festéket és a nap végére 300 méternyi vonalat fest fel az útra. "Ez kiváló!" mondja a főnöke, "kiváló munkaerő vagy!" és ad neki egy dollárt.

A következő napon, csak 150 méter vonalat fest. "Nos, ez nem olyan sok mint tegnap, de még mindig elégedett vagyok. 150 méter igen szép teljesítmény." és add neki egy dollárt.

A következő nap Schemiel csak 30 méterrel végez. "Csak 30 méter!" ordít a főnök. "Ez elfogadhatatlan! Ez első napon tízszer ennyit festetél! Mi folyik itt?"

"Nem tehetek róla, uram" válaszolja Schemiel. "Minden nap messzebre és messzebre kerülök a festékes vödörtől!"

(Egy kis agytorna, mik a valódi számok a viccben ?) Ez a gyenge vicc azt hivatott illusztrálni mi megy végbe, amikor az strcat függvényt használod. Mivel az strcat függvénynek végig kell menni a cél sztringen minden esetben, a zéro karakter miatt újra és újra, ezért nagyon lassú lesz a fent mutatott ejárás és nem skálázható jól. Nagyon sok mindennap használt kódnak ez a baja. Nagyon sok file rendszert implementáltak úgy, hogy a teljesitmeny drámaian csökken ha több ezer file van egy könyvtárban. Próbálj megnyitni egy telepakolt Windows szemetest, hogy lásd ezt a hatást a gyakorlatban is – perceket vesz igénybe amig feljön az ablak, ami nincsen lineáris kapcsolatban a fileok számával. Valahol elrejtve ott dolgozik egy “Schemiel féle festő algoritmus”. Bármikor amikor úgy tűnik, hogy lineáris kapcsolat kell, hogy legyen és ehelyett négyzetes adódik, keress rejtett Schemieleket. Gyakran a programozói könyvtárakban van elrejtve. Egy sor strcat-ról vagy egy strcat ciklusbamagban nem ordít négyzetes teljesítményről, pedig erről van szó.

Hogyan lehet ezt kiküszöbölni? Néhány eszes C programozó megalkotta a saját strcat függvényét, ime:


char* mystrcat( char* dest, char* src )
{
     while (*dest) dest++;
     while (*dest++ = *src++);
     return --dest;
}

Mit is értünk el itt? Nagyon kis többlet munkával visszaadjuk az új hosszú sztring végét. Igy az ezt használó kód hozzá tud illeszteni egy új sztringet, anélkül hogy újra végig kellene menni az egész sztringen:


char bigString[1000];     /* Nem tudom, hogy milyen hosszú lesz... */
char *p = bigString;
bigString[0] = '\\0';
p = mystrcat(p,"John, ");
p = mystrcat(p,"Paul, ");
p = mystrcat(p,"George, ");
p = mystrcat(p,"Joel ");

Ez igy, természetesen, lineáris teljesítményt eredményez, négyzetes helyett, így hát nem lassul le hogyha nagyon sok sztringet kell összefűzni.

A Pascal nyelv tervezői tudtak erről a problémáról és „megoldották” a problémát úgy, hogy az első bájton tárolták el a sztring hosszát. Ezt a fajta sztringet Pascal sztringnek nevezzük. Tartalmazhatnak nullákat és nem nullára végződnek. Mivel a bájt csak 0 és 255 között képes számokat ábrázolni ezért a Pascal sztring nem lehet 255-nél hosszabb, mivel a Pascal sztring nem tartalmaz nulla karaktert ezért pontosan ugyanakkora helyet foglal el mint egy 255 karakter hosszú ASCIZ sztring. A nagy erőssége a Pascal sztringnek az hogy nem kell végigszaladni rajta, hogy meg tudjuk mondani a hosszát. A hossz információ költsége egy Pasal sztring esetében egyetlen utasítás szemben egy ciklussal. Ez sokkal gyorsabb.

A régi Machintos operációs rendszer Pascal sztringeket használt. Nagyon sok C programozó más platformokon Pascal típusú sztringeket használt a sebbeségük miatt. Az Excel Pascal sztringeket használ belső műveletekhez ez az amiért néhány helyen a sztringek hossza 255 karakter lehet maximum, és ez az egyik ok amiért az Excel nagyon gyors.

Hosszú időn keresztül, ha Pascal sztringet akartál C-ben használni, valami ilyesmit kellett csinálni:


char* str = "\\006Hello!";

Igen, a bájtokat saját magadnak kellett megszámolni és bedrótoznod a sztring első bájtjába. Egy lusta programozó ezt így oldaná meg, egy lassú programot kapva végeredményként:


char* str = "*Hello!";
str[0] = strlen(str) - 1;

Észrevehetjük, hogy ez a sztring tulajdonképpen egy nulla terminált sztring (a fordítónak köszönhetően) és Pascal sztring is. Ezeket a sztringeket elcseszett sztringeknek kerszteltem el magamban, mivel könnyebb kimondani, mint azt hogy nulla végű pascal sztring, de mivel ez egy korhatár nélküli website így kénytelenek leszünk a hosszabb nevet használni.

Kihagytam egy fontos dolgot az előbb. Emlékszel még erre?

char bigString[1000]; /* Nem tudom, hogy milyen hosszú lesz... */

Mivel ma a bitekről és bájtokról beszélünk ezt nem lett volna szabad kihagynom. Helyesen kellett volna eljárnom: kitalálni hogy mennyi bájtra van szükségem és pontosan annyit foglalni le a memóriából.

Ugye?

Mivel másképpen, egy ügyes hacker a kódból kitalálhatja, hogy 1000 karaktert foglaltam - remélve, hogy elég lesz - és találna valami ügyes módot arra, hogy valahogy egy 1100 bájt hosszú sztringet irjon az 1000 bájt hosszú sztringem helyett, ezzel felül írva a stacket és megváltoztatva a visszatérés címét. Amikor aztán ez a függvény befejezi a futást, lehetőség nyílhat a hacker által irt kód futtatására. Ezt a jelenséget hívják buffer overflow-nak. Régebben ez volt az elsőszámú módszere a hackereknek és féreg programoknak, hogy hozzáférjenek más rendszerekhez, mielőtt az Outlook segítségével széles rétegek számára elérhetővé vált az illegális kód futtatása más gépeken.
Rendben, tehát minden programozó lusta. Ki kellett volna találniuk, hogy mennyi memóriát kellett volna lefoglalni.
De valójában, a C nem könnyíti meg a dolgunkat. Térjünk vissza a Beatles-s példánkra:


char bigString[1000];     /* Nem tudom, hogy milyen hosszú lesz... */
char *p = bigString;
bigString[0] = '\\0';
p = mystrcat(p,"John, ");
p = mystrcat(p,"Paul, ");
p = mystrcat(p,"George, ");
p = mystrcat(p,"Joel ");

Mennyi memóriát kellene lefoglalnunk? Próbáljuk meg most “A Helyes Utat”.


char* bigString;
int i = 0;
i = strlen("John, ")
     + strlen("Paul, ")
     + strlen("George, ")
     + strlen("Joel ");
bigString = (char*) malloc (i + 1);
/* plussz egy byte a null karakternek! */
...

Lassan kezdek elfáradni. Valószínűleg már te is rég magamra hagytál. Nem is hibáztatlak ezért, de tarts velem, mert innentől kezd érdekes lenni.

Mindig végig kell futnunk a sztringen egyszer azért, hogy megmondjuk a hosszát, aztán újra végigfutunk, hogy összefűzzük. Ha Pascal sztringet használunk, akkor legalább a strlen művelet az gyors. Esetleg írhatnánk egy olyan strcat függvényt, amely lefoglalja és hozzádja a szükséges memóriát a cél sztringhez.

Ez felvet egy másik érdekes problémát is. Tudod hogyan működik a malloc függvény? A malloc egy láncolt listában tárolja a szabad memória blokkokat. Amikor meghívod a malloc-t, akkor az végigsétál ezen a láncolt listán, addig amíg a kérésnek megfelelő nagyságú memória blokkot (vagy annál nagyobbat) nem talál. Ezután két részre osztja a blokkot – az egyik a kérésnek megfelelő méretű, a másik pedig a maradék, az első lesz a lefoglalt memória terület és a maradékot (ha van) visszateszi a láncolt listába. Amikor a free függvényt hívod meg, akkor hozzáadja a felszabadítani kívánt blokkot a listához. Esetenként a szabad memória területek listája elaprózódik és amikor neked egy nagyobb darabra lenne szükséged előfordulhat, hogy nincsen akkora memória egyben. Ekkor a malloc függvény timeout-t add és elkezdi a szabad memória láncot rendezgetni és a szabad darabokat nagyobb darabokká összeállítani. Ez körübelül 3 és fél napot vesz igénybe. Ebből az egészből az következik, hogy a malloc nem igazán gyors (mindig végigszalad a szabad memória listán), és néha, előrejelezhetetlenül és sokkolóan lelassul, amíg újrarendezi a listát. (Ez véletlenül éppen ugyanazt a teljesítmény profilt mutatja, mint az úgynevezett szemétgyűjtő (garbage collection) rendszerek. Nahát, nahát. Tehát azok, akik az ilyen (garbage collection) rendszerek lassúságát kritizálják nincsen teljesen igazuk, hiszen ez hasonlít a malloc működéséhez, jóllehet ez utóbbi nem annyira erőforrásigényes.)

Okos programozók úgy kerülik el a malloc ezen zavaró viselkedését, hogy mindig 2 hatványával felírható nagyságú memóriát foglalnak le. Tudod, 4 byte, 8 byte, 16 byte, 18446744073709551616 byte, és így tovább. Rgyébb okokból, ami nyilvánvaló kell, hogy legyen annak, aki játszott már LEGO-val, ez lecsökkenti a furcsa töredezettségek számát a szabad láncban. Habár ez a módszer pazarlónak látszik, könnyen belátható, hogy így sohasem pazarlunk el többet mint a lefoglalt memória 50 százaléka. Tehát a programod nem használ többet, mint a szükséges memória kétszerese, ami még elfogadható.

Ha megírtad az intelligens strcat függvényt, ami mindig újra allokálja a cél buffert. Mindig pontosan a szükséges nagyságú memóriát kell lefoglalni? Tanárom és mentorom Stan Eisenstat azt tanácsolja, hogy amikor meghívod a realloc függvényt, mindig az előzőleg lefoglalt memória kétszeresét kell lefoglalnod. Ez azt jelenti, hogy a realloc-t nem kell lg n –nél többször meghívnod, aminek így igen jó lesz a teljesítmény karakterisztikája és nem pazarolsz 50%-nál több memóriát.

Akárhogy is. Az élet egyre és egyre bonyolultabb lesz itt bájtföldön. Hát nem vagy szerencsés, hogy nem kell C-ben kódolnod többé? Ott vannak a Perl és Java, VB és XSLT – nagyszerű nyelvek – soha többet nem kell ilyesféle dolgokon gondolkodnod, mivel ezek a nyelvek megoldják ezt helyetted. De néha elsyabadul a pokol és - csőtörés miatt - a szennyvíz elárasztja a nappalid és olyan dolgokon kell törnünk a fejünket, hogy a String vagy a StringBuilder osztályt használd, vagy hasonló dolgok, mivel a fordítók nem elég okosak ahhoz, hogy megértsék, hogy mit is próbálunk elérni és segíteni abban, hogy ne tegyünk Schemiel féle festő algoritmusokat a kódunkba.
....
....

Az ilyen és hasonló kérdések kívánják meg azt hogy bitekben és bájtokban gondolkozzunk és ezek azok a dolgok amik kihatnak a stratégia és architektúrális szintekre is. Ezért gondolom azt, hogy az elsőéves informatikus halgatóknak C-t kell tanulniuk és az alapoktól (CPU) indulva felfelé építkezni. Személy szerint igen botrányosnak találom, hogy nagyon sok informatikai oktatás a Java-t első nyelvként tanítja, mivel “könnyű” és nem kell mindféle zavaró és unalmas malloc-string dolgokkal foglakozni, hanem rögtön az Objektum Orientált programozást mutatja be, amivel minden eddiginél modulárisabb programok írását teszi lehetővé. Ez az oktatás csődje katsztrófa ami nemsokára bekövetkezik. Frissen végzett generációk jönnek, akik jobbra és balra is “Schemiel Festő algoritmus”-t implementálják, azért mert alapvetően nincsen fogalmuk arról, hogy milyen is például egy sztring megvalósítása a legalacsonyabb szinten. Ha valakit jól meg akarsz tanítani valamire a legegyszerűbb dolgoknál kell kezdeni. Mint a Karate Kölyökben. Wax On, Wax Off. Wax On, Wax Off. Csináld ezt három hétig, kiütni egy másik kölyköt ezután már gyerekjáték.

Köszönöm a hasznos olvasnivalót!
Azt egy életre megtanultam, hogy const * char a paramétere az atoi-nak. És azt is, hogy a konstans karakter változó, vagy konstans karaktertömb változó létrehozásakor a nullás terminátor automatikusan a végéhez íródik, ezért nem ellenőriz az atoi és ezért bízik abban, hogy addig kell mennie, amíg nullás terminátort nem talál.
Azt az egyet viszont nem tudom, hogy miért pont Scheimel és miért nem Smith?
--
unix -- több, mint kód. filozófia.
Life is feudal

const * char

const char* :)

És azt is, hogy a konstans karakter változó

Konstans valtozo eleg keves van. :)

nullás terminátor automatikusan a végéhez íródik

Csak ha konstansrol van szo, ha valtozo, akkor neked kell gondoskodnod a vegen a \0-rol.
Pl.
- atoi("23"); - itt valoban a compiler hozzacsapja.
- char tmp[512]; read(fd, tmp, 10); atoi(tmp); - ez mar fals erteket fog adni, ha nem teszed oda a tmp[10]=0;-t. (Felteve, hogy a szam amit beolvasol 10 karakter hosszu. A pelda itt santit kicsit.)

---
pontscho / fresh!mindworkz

Itt azt hiszem, hogy meg kell világítsak pár dolgot. Kezdjük azzal, hogy minden ami a gépen van az számként tárolódik, a különbség annyi, hogy mi megmondjuk, pl. char c-t karakterként kezeljük. Ezért kiíratásnál az érték helyett a karakter íródik ki. Nem próbáltam de szerintem csak ASCII karakterkódolás estén működik. És a legnagyobb hátránya, hogy akármilyen karakter van az s[3] helyén mindenképpen számot fogsz kapni, tehát az ilyen hibát soha nem fogod észre venni.

Szerintem egy sokkal elegánsabb megoldás:


#include <string>
#include <iostream>

using namespace std;

int main(void) {
	char c;
	string str("1234");
	int a;
	
	
	c = str.c_str()[1]; //használdd mindig a standard konverziót, ne típuskényszerítést
	//átalakítások előtt mindenképpen ellenőrizni kell, hogy mit alakítunk
        if(isdigit(c)){
		a = atoi(&c); 
		cout << a << endl;
		return 0;
	}

	cout << "c nem egész szám!" << endl;
	return 1;

	
}


--
A lehetetlen csak a lusta ember kifogása!

Ez így egyszerűen rossz. :)

Kezdjük az elején:
"Nem próbáltam de szerintem csak ASCII karakterkódolás estén működik."
Mindenhol működik, ahol a szám karakterek egymás után növekvő sorban helyezkednek el.
Ilyen az ASCII, illetve minden ASCII leszármazott 8-bites kódolások (latin-1, stb.), illetve az egyéb 8 bites kódolások (UTF-8, EBCDIC).
Ennél egzotikusabbal nem valószínű hogy találkozhatsz.


c = str.c_str()[1]; //használdd mindig a standard konverziót, ne típuskényszerítést

A string::operator[] char-t ad vissza, tehát az égvilágon semmi típuskényszerítés nincs, itt teljesen jó a:


c = str[1];

A te verziód csúnyább, és lassabb is...

Ez szintén rossz:


a = atoi(&c);

Miért? Mert az atoi c-stílusú stringet vár, te viszont egy char címét adod vissza, ami mögött maximum csak véletlenül van lezáró 0...

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

char c;
i=atoi(&c);

Ebben az a fasza, hogy gyonyoruen kepes fals erteket adni. Mivel te a c cimet adod at, amit az atoi stringnek vesz. Mindossze csak azert mukodott, mert a stack-en veletlenul nulla volt a c utan.

Ebbol lesz aztan kesobb a BoF, etc. Buktattam meg embert anno vizsgan ilyen huzasokert. (Amugy mint mindenki, en is kovettem mar el ilyen hibat.:)

---
pontscho / fresh!mindworkz

józan parasztul gondolkodni csak pontos információk alapján lehet. Egy függvény belső működése nem tippelhető meg józan paraszti ésszel. A függvény olyan, amilyenre kialakítják. Embertől függ. Az iskola pont az a hely, ahol elmesélhetnek olyan apróságokat, amin nagyot lehet hasalni kódolás közben. Ezek nagy része könyvben nincs benne. Maarad a fórum és a kérdezősködés.
--
unix -- több, mint kód. filozófia.
Life is feudal

józan parasztul gondolkodni csak pontos információk alapján lehet. Egy függvény belső működése nem tippelhető meg józan paraszti ésszel.

Nem egeszen. Mivel el lehet kepzelni, hogy ha te csinalnad hogyan mukodne. Max. nem ugy van a valosagban. Masreszt a fuggveny definiciobol mar eleg jo aranyban lehet ki lehet talalni, hogy mi a feladata es mit muvel a hatterben.

Az iskola pont az a hely, ahol elmesélhetnek olyan apróságokat, amin nagyot lehet hasalni kódolás közben. Ezek nagy része könyvben nincs benne.

Ha szerencsed van. Az viszont garantalhato, hogy az esetek nagy reszeben ez nem igy mukodik. Masreszt ami az eloadason az egyik fuleden bemegy, az a masikon kifog. Ha nem kepzed magad (es nem ok teged! fontos kulonbseg), akkor az egesz iskola lofaszt se er. Plane az atlag 10 evvel elavult tudas a vicces, amit sok felsooktatasi intezmenyben nyomnak.

---
pontscho / fresh!mindworkz

Az jutott még eszembe ezzel kapcsolatban, hogy miért számít, hogy nulla értékű-e a következő byte? a char * cp nem azt jelenti, hogy olyan mutató, ami karakter tárolására alkalmas méretű memóriaterületre mutat? Azaz a mérete adott, nem? nem fog addig haladni a memóriában, amíg nullás byteot nem talál.
--
unix -- több, mint kód. filozófia.
Life is feudal

De az atoi valoszinu, hogy fog tovabb is olvasni. Addig olvasa karaktereket folyamatosan amig elkepzelheto, hogy a kov karakter egy int szam resze.

Es a legtobb char* kapo fuggveny addog olvassa a karaktereket megatott char* kezdo pontol amig \0 -t nem talal.

char* valoban egy karakterre mutat, C -stringeknel csak a kezdo karakter cimet adjuk at, es addig tart a string amig \0 -val le nem zarodik.
"" -kozotti szoveg vegere automatikusan bekerul '\0' . ' ' kozottihez nem.


#include <stdio.h>
/*
 * mindharmat mezei char* nak tekinti
 */
char *fuggveny (char negyvenketto[42], char zjel[],char * valami)
{
        printf(negyvenketto);
        printf("%s",zjel);
        puts(valami);
        return negyvenketto;
}
int main()
{
        char valami[20]="424\n"; //hat most pazaroltunk 20 byteon taroljuk 5 byte adatot
        char ize[]="thing\n"; //most nincs pazarlas
        char *haha="[]\n"; //most gyartotunk egy stringet meg egy mutatot ra.
        fuggveny(haha,ize,valami);

        haha=NULL; //ezt ennel megtehetem, de a tobbinel nem.
        return 0;
}

"De az atoi valoszinu, hogy fog tovabb is olvasni. Addig olvasa karaktereket folyamatosan amig elkepzelheto, hogy a kov karakter egy int szam resze."

Ez attól függ mit kap paraméterként.
Az atoi definíciója: int atoi( const char *str ); Ami azt jelenti, hogy amit vár az egy karakterre mutató mutatót, ami egy (kezdő)karakter címét tartalmazza, ezért tudjuk átadni neki egy karakter címét is. Így ezt már megtehetjük kétféle módon:


char c; // char típusú változó
char* mc; // egy mutató ami egy karakterre mutat, ugye ebben az esetben mindig egy lezáró 0-al végződik, lehet
          //karaktertömb is

//az atoi meghívásának módjai

atoi(&c);
atoi(mc);

Ami fontos a kettő közti különbség, nem mindegy, hogy mutatóról van szó, vagy egy memória terület címéről, még ha nagyon hasonló is a kettő. Ha valaki viszont normális eszközökkel el tudja érni a char c következő "karakterét" az számoljon be róla, mert nagyon kíváncsi lennék, a hogyanra.

megj.: És mindezt C++-ra vállalom felelősségteljesen.
--
A lehetetlen csak a lusta ember kifogása!

OMG, ez az egész bullshit.
Vannak itt tudásbeli hiányosságok...

Ha valaki viszont normális eszközökkel el tudja érni a char c következő "karakterét" az számoljon be róla, mert nagyon kíváncsi lennék, a hogyanra.

(&c)[1]

vagy

*(&c+1)

tetszés szerint.

megj.: És mindezt C++-ra vállalom felelősségteljesen.

Egyebet nem akarok mondani. Kérem kapcsolja ki.

g++ v4.1.2 Verzióval fordítva, ha a c így lett deklarálva: char c; Akkor mindegyik esetben error-t ad fordításkor: "error: invalid conversion from ‘char’ to ‘const char*’"

Lehet, hogy nem fogalmaztam túlságosan pontosan, de ha nincs igazam, azt elismerem, amit meg is fogok tenni ha mutatsz olyan esetet amikor a char c='3'; atoi(&c) nem hármat ad eredményül.
--
A lehetetlen csak a lusta ember kifogása!

Az ilyen aprosagok szoktak a daily WTF oldalan elsokent megjelenni.

1. Az egesz sztorit ott kurtad el, hogy a char*-nal egy string kezdo cimet varja az atoi, nem egy karakter cimet. Es igen, az atoi addig fogja azt a szerencsetlen string-et beolvasni, amig egy \0-t nem talal, es nem egy egesz byte-ot dolgoz fel.

2. char c következő "karakterét" az számoljon be róla, mert nagyon kíváncsi lennék, a hogyanra.

Szepen: mc = &c; mc[1]...
Mukodokepesen: *(&c + 1)

A miertekre a valaszt megtalalod a 'C for dummies' kotetben.

---
pontscho / fresh!mindworkz

Ez nem igaz! Ugyanis char c egy karakter tárolására alkalmas, &c egy karakter tárolására alkalmas memóriaterület címe, és nem nagyobb. Tehát semmi nem nézi még át utána a 2 GB memóriát, míg nem talál egy lezáró karaktert. (char*-ra igaz lenne, de itt nem az szerepelt).
--
A lehetetlen csak a lusta ember kifogása!

Nem az. Nem veletlen tiltja minden normalisabb coding policy az strcpy(), sprintf(), stb hasznalatat, mivel kurvara nem foglalkoznak az array bounding-gal. Ugy tovabb irja, mint a huzat a tombot es jon az alomszep BOF. Effektive ha a string a stack-en van, akkor ott.

---
pontscho / fresh!mindworkz

Te szerencsetlen, mielott osztod az eszet nezz mar utana annak amirol magyarazol ha mar fingod sincs rola.

char *
strcpy(char * __restrict to, const char * __restrict from)
{
        char *save = to;

        for (; (*to = *from); ++from, ++to);
        return(save);
}

strcpy() libc-bol. Latsz itt valahol meret limitet? Igenis vegig fogja nyalni azt a (max. cim - string base pointer)-nyi teruletet szo nelkul. Ha 2GB atnyalasa utan talalja meg a \0-t, akkor ott fogja.

---
pontscho / fresh!mindworkz

Teljesen igazad van. Akármit kapok ebben a témában, azt megérdemlem. Ezt nagyon elb*am, akarom mondani nagyon benéztem. Nem akarom védeni magam, de két óriási tanulsággal szolgált ez az eset a számomra:
- soha ne állíts olyat, amit 3x át nem rágtál, és utána nem néztél, főleg ne rutinból
- ennyi tapasztalat után ekkora lámaságot elkövetni, ezért vezekelnem kell, ezért elő is vettem azt a bizonyos 2000 oldalas C++ könyvet

Félreértés ne essék, nem védeni akarom magamat, de ilyen hibát nekem semmilyen körülmények között nem szabadott volna elkövetni, és ez miatt most nagyon szarul érzem magam, ráadásul visszagondolva még én is érzem mennyire nem gondolkodtam.
--
A lehetetlen csak a lusta ember kifogása!

A dolog inkabb ket okbol vicces:
- az x86_64 arch.-ok mar javareszt tudnak SIMD-ul, es cache buzeralasul. Ezekkel viccesebben meg lehet irni jobbra.
- rep movs a CPU-ban pontosan string masolasra szolgal.

Nem tudom milyen kodot lattal, es hogy a cmovcc-nek milyen cache/etc beli vonatkozasa van, egy ideje nem nagyon foglalkoztam a "modern" cpu-k asm szintu programozasaval. De azt sem tartom kizartnak, hogy a tisztelt "coder"-nek eszebe sem jutott cmovcc lete. Anno glibc-ben/apple corefoundation-ban/etc-ben is talaltam jo par WTF-et.

---
pontscho / fresh!mindworkz

rep prefixet altalban nem ajanljak, pedig az en gyik proceszoromon jobbank tunt (fix hoszakra) rep movsq mint 4 mov egy ciklusban, bar mereskor ugyanazt masoltam ugyan oda, tehet cache misseim nem igen voltak, de masodik legjobb volt csak a 4 mov (probaltam parat). optimalicacios javaslatok, talan ez en esetemben is 4 -es movost ajanlottak.

Ez van leforgatva:


#include <sysdep.h>
#include "asm-syntax.h"
#include "bp-sym.h"
#include "bp-asm.h"

#ifndef USE_AS_STPCPY
# define STRCPY strcpy
#endif

	.text
ENTRY (BP_SYM (STRCPY))
	movq %rsi, %rcx		/* Source register. */
	andl $7, %ecx		/* mask alignment bits */
	movq %rdi, %rdx		/* Duplicate destination pointer.  */

	jz 5f			/* aligned => start loop */

	neg %ecx		/* We need to align to 8 bytes.  */
	addl $8,%ecx
	/* Search the first bytes directly.  */
0:
	movb	(%rsi), %al	/* Fetch a byte */
	testb	%al, %al	/* Is it NUL? */
	movb	%al, (%rdx)	/* Store it */
	jz	4f		/* If it was NUL, done! */
	incq	%rsi
	incq	%rdx
	decl	%ecx
	jnz	0b

5:
	movq $0xfefefefefefefeff,%r8

	/* Now the sources is aligned.  Unfortunatly we cannot force
	   to have both source and destination aligned, so ignore the
	   alignment of the destination.  */
	.p2align 4
1:
	/* 1st unroll.  */
	movq	(%rsi), %rax	/* Read double word (8 bytes).  */
	addq	$8, %rsi	/* Adjust pointer for next word.  */
	movq	%rax, %r9	/* Save a copy for NUL finding.  */
	addq	%r8, %r9	/* add the magic value to the word.  We get
				   carry bits reported for each byte which
				   is *not* 0 */
	jnc	3f		/* highest byte is NUL => return pointer */
	xorq	%rax, %r9	/* (word+magic)^word */
	orq	%r8, %r9	/* set all non-carry bits */
	incq	%r9		/* add 1: if one carry bit was *not* set
				   the addition will not result in 0.  */

	jnz	3f		/* found NUL => return pointer */

	movq	%rax, (%rdx)	/* Write value to destination.  */
	addq	$8, %rdx	/* Adjust pointer.  */

	/* 2nd unroll.  */
	movq	(%rsi), %rax	/* Read double word (8 bytes).  */
	addq	$8, %rsi	/* Adjust pointer for next word.  */
	movq	%rax, %r9	/* Save a copy for NUL finding.  */
	addq	%r8, %r9	/* add the magic value to the word.  We get
				   carry bits reported for each byte which
				   is *not* 0 */
	jnc	3f		/* highest byte is NUL => return pointer */
	xorq	%rax, %r9	/* (word+magic)^word */
	orq	%r8, %r9	/* set all non-carry bits */
	incq	%r9		/* add 1: if one carry bit was *not* set
				   the addition will not result in 0.  */

	jnz	3f		/* found NUL => return pointer */

	movq	%rax, (%rdx)	/* Write value to destination.  */
	addq	$8, %rdx	/* Adjust pointer.  */

	/* 3rd unroll.  */
	movq	(%rsi), %rax	/* Read double word (8 bytes).  */
	addq	$8, %rsi	/* Adjust pointer for next word.  */
	movq	%rax, %r9	/* Save a copy for NUL finding.  */
	addq	%r8, %r9	/* add the magic value to the word.  We get
				   carry bits reported for each byte which
				   is *not* 0 */
	jnc	3f		/* highest byte is NUL => return pointer */
	xorq	%rax, %r9	/* (word+magic)^word */
	orq	%r8, %r9	/* set all non-carry bits */
	incq	%r9		/* add 1: if one carry bit was *not* set
				   the addition will not result in 0.  */

	jnz	3f		/* found NUL => return pointer */

	movq	%rax, (%rdx)	/* Write value to destination.  */
	addq	$8, %rdx	/* Adjust pointer.  */

	/* 4th unroll.  */
	movq	(%rsi), %rax	/* Read double word (8 bytes).  */
	addq	$8, %rsi	/* Adjust pointer for next word.  */
	movq	%rax, %r9	/* Save a copy for NUL finding.  */
	addq	%r8, %r9	/* add the magic value to the word.  We get
				   carry bits reported for each byte which
				   is *not* 0 */
	jnc	3f		/* highest byte is NUL => return pointer */
	xorq	%rax, %r9	/* (word+magic)^word */
	orq	%r8, %r9	/* set all non-carry bits */
	incq	%r9		/* add 1: if one carry bit was *not* set
				   the addition will not result in 0.  */

	jnz	3f		/* found NUL => return pointer */

	movq	%rax, (%rdx)	/* Write value to destination.  */
	addq	$8, %rdx	/* Adjust pointer.  */
	jmp	1b		/* Next iteration.  */

	/* Do the last few bytes. %rax contains the value to write.
	   The loop is unrolled twice.  */
	.p2align 4
3:
	/* Note that stpcpy needs to return with the value of the NUL
	   byte.  */
	movb	%al, (%rdx)	/* 1st byte.  */
	testb	%al, %al	/* Is it NUL.  */
	jz	4f		/* yes, finish.  */
	incq	%rdx		/* Increment destination.  */
	movb	%ah, (%rdx)	/* 2nd byte.  */
	testb	%ah, %ah	/* Is it NUL?.  */
	jz	4f		/* yes, finish.  */
	incq	%rdx		/* Increment destination.  */
	shrq	$16, %rax	/* Shift...  */
	jmp	3b		/* and look at next two bytes in %rax.  */

4:
#ifdef USE_AS_STPCPY
	movq	%rdx, %rax	/* Destination is return value.  */
#else
	movq	%rdi, %rax	/* Source is return value.  */
#endif
	retq

Jeeezus. Ez ilyen: "anyuuuu... bonyolultabban nem lehet?". :) Legviccesebb resze az, hogy beolvas egyszerre 64 bitet, majd vegig buzeralja a regisztert, hogy az egyik resze nem nulla-e.

Ha az egyszeruseg a cel, akkor a rep movs verhetetlen. Ha a sebesseg, akkor a pipeline optimalizacio miatt jobb a 4 mov. Mivel altalaban - mar a Cyrix 6x86 ota - az x86 vonal kepes egyreszt "kiugratni" a pipeline-bol a vegrehajtast nem akadalyozo utasitasokat masik pipe-ra, illetve az egymas utan kovetkezo, precedencia problemat nem okozo utasitasokat egymas mellett vegrehajtani.

---
pontscho / fresh!mindworkz

Ez tényleg perverz, de nem hülyeség amit csinál... Beolvas 64 bitet, és azt buzerálja, viszont ezt mind regiszterekkel, ami majdnem ingyen van, hiszen a következő 64 bitre úgyis várni kell. Nekem tetszik. :)

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

Ja, ha kisebb egysegeket hasznalna az is problema lehet, hogy nem tudna olyan jol parhuzamositani a CPU mivel ugyan azon a 8 byten belul cimezne.
Mintha olyat is olvastam volna, hogy az inc az gonosz es add 1 jobb sok esetben.

Erdekes lenne versenyeztetni kodokat a problemara, (hossz szerint, cache legyilkolassal es nelkule, grafikonokat krealni.. )

Prefetch -dologal gond lehet, ha string utanni terulet nem a mienk.Es ugye elore nem tudjuk meddig tart.

Tudom, utána kéne olvasnom, de most így hirtelen nem találtam többet ennél:
std::string s;
cin>>s;
s.size();
s.length();
mondja meg az s hosszát. De azt nem tudtam meg, hogy ezek a függvények belülről hogyan néznek ki és honnan/hogyan állapítják meg a string hosszát?
Elnézést, hogy ezzel hozakodok elő, de tényleg érdekel és nyilván unalmas és túl
egyszerű kérdés. Nézzétek el nekem, hogy ezzel zaklatom a társaságot!
Szeretném ezt megtudni.
Köszönöm!
--
unix -- több, mint kód. filozófia.
Life is feudal