Sziasztok!
Új vagyok mind a C-ben, mind a HUP fórumán, úgyhogy kérlek nézzétek el, ha baromságokat kérdezek.
A kérdésem a következő:
A C-ben ha sztringet próbálok deklarálni,
az alábbi módon:
char *string;
akkor tulajdonképpen mit csinálok?
Mi garantálja azt, hogy ez a pointer olyan területre mutat, ahol nincs semmiféle más adat, amibe beleírhatnék? Ez a kérdés tulajdonképpen azért merült fel bennem, mert sok példakódban, szakkönyvben használják ezt így, mindenféle malloc() nélkül.
A másik kérdésem, hogy az így deklarált sztringeknél érdekes dolgot fedeztem fel:
char* string1="valami";
char* string2="valami";
char* string3="valami";
Nevezetesen azt, hogy a gcc fogja magát, és nemes egyszerűséggel ugyanarra a címre mutató pointerrekké teszi azokat. Ennek következményeként tkp. csak 1 darab sztringhez jutok, ami nem az amit akartam.
Válaszotokat előre is köszönöm.
- 4983 megtekintés
Hozzászólások
elso:
char* azt jelenti, hogy egy karakterre mutato mutato. int* meg egy mutato, ami intre mutat. ennyi. malloc nelkul nem szabad hasznalni, mert random ertekre mutat. ja, es free()zd, ha mar nem kell, itt nincs garbage collection.
masodik:
gcc kioptimalizalja, valoszinuleg. valahogy tuti le lehet tiltani, vagy bugos a gcc :) hanyas verzio?
- A hozzászóláshoz be kell jelentkezni
Hálásam köszönöm a gyors választ, a kérdés igazából azért merült fel bennem, mert a
char string[32]="valami";
kódot a gcc visszadobta, valamint, amint írtam, sok helyen láttam malloc() nélkül használt sztringpointert.
a másodikra a válszom az, hogy gcc 4.1 (djgpp) alatt történt a fent említett eset.
Mellesleg, a hupon valami szokas ekezet nelkul irogatni, rengeteg threadban lattam mar ezt.
- A hozzászóláshoz be kell jelentkezni
static char string[] = "valami" -t nem fogja visszadobni.
--
Live Free or Die
UNIX
- A hozzászóláshoz be kell jelentkezni
Ekezetek nelkuli iras hmmm... Szerintem joval gyorsabb, mint ekezetekkel. Tovabba egy kis gyakorlassal rendkivul konnyen dekodolhato, es nem zavaro. Szerintem azok irnak ekezetek nelkul, akik
a, nagyon regota, meg a DOS-os korszaktol hasznalnak szamitogepet es angol billentyuzeten szocializalodtak
b, fejlesztok, rendszergazdak akiknek gyakorlatilag soha sincs szukseguk magyar kiosztasra (bar lattam mar magyar kiosztassal kodolo arcot is)
- A hozzászóláshoz be kell jelentkezni
En pl. dvorak layoutot hasznalok. :)
--
Live Free or Die
UNIX
- A hozzászóláshoz be kell jelentkezni
De ha esetleg a hibaüzit magyarul kellene visszaadni, és abban van ékezet, akkor arra ott a hunglish :- ).
- A hozzászóláshoz be kell jelentkezni
A type error exceptiont okozott. Kerjuk debugaljon. Thx.
:-)
"i pensieri stretti & il viso sciolto." -- Sir Henry Wootton
- A hozzászóláshoz be kell jelentkezni
LaTeX: kell a [],{}, &, \, $, _, ^ karakter minden mennyiségben + ékezetek. Ilyen feltételek mellett angol billentyűzet-kiosztást használni elég érdekes lenne. Akkor már programozáshoz is nyilván magyar van nálam :D
- A hozzászóláshoz be kell jelentkezni
A masodiknal adjal meg kulonbozo kezdoertekeket, kulonben nem erzekeled a kulonbseget. :-)
---------------------
Minnél korszakalkotóbb ötlettel állsz elő, annál több hülyén kell átverekedned magadat.
- A hozzászóláshoz be kell jelentkezni
nem tudom pontosan, hogy ertetted,hogy nem erzekelem a kulonbseget, mindenesetre a
char *string1="0";
char *string2="0";
strcpy(string1,"blabla");
printf("%s",string2);
kod blabla-t nyomtat a kepernyore.
- A hozzászóláshoz be kell jelentkezni
Ez azért van mert mind a két string egy pointer, ami a "0" címre mutat (ascii 0 értékére).
Ha erre a címre írsz valamit, akkor másik is ezt látja, mivel oda mutat. Ez a művelet roppant veszélyes!!!! mert valahova írtál, de fogalmad sincs mi volt ott és mit írtál felül.
Helyesen ez lenne
char st1[256];
char *string1;
char *string2;
string1=st1;
strcpy(string1,"blabla");
printf("%s",string2); // zagyva kiirás
string2=string1;
printf("%s",string2); // blabla kiirása
- A hozzászóláshoz be kell jelentkezni
Hálásan köszönöm informatív válaszodat, pont erre voltam kíváncsi, köszönöm az idődet, és most megyek , és megjavítok egypár sornyi katasztrofálisan veszélyes kódot. :)
- A hozzászóláshoz be kell jelentkezni
Valami ilyesmire gondoltam:
char *string1="1";
char *string2="2";
Mivel az elozo megoldasnal ahol az osszesnel "valami" volt az kezdoertek, akarmelyiket valasztod egy printf-nel, ugyanaz az eredmeny fog visszajonni (csak egy masik valtozobol). :-)
---------------------
Minnél korszakalkotóbb ötlettel állsz elő, annál több hülyén kell átverekedned magadat.
- A hozzászóláshoz be kell jelentkezni
A C-ben igazából "nincsen" string változó. Ezt egy karakteres tömb helyetesíti. Enne a tömbnek a kezdetére mutató pointerrel kell dolgozni. Egy string végét a 0 jelöli. Így amennyiben nem foglalsz le helyet úgy egy tömb tartalmazhatja fix!! hosszon a stringedet.
Ha szeretnél szöveget konstansként definiálni akkor használd a
char string1[]="Szoveg....";
char string2[]="Szoveg....";
stb...
Ha csak akarsz egy "string" változót csinálni, akkor az előbb leírtak szerint egy char tömbt kell csinálnod.
char string[256]; // ebbe 255 karakternyi szöveg fér, mert a végére 0 is kell
ebbe úgy tusz szöveget írni, hogy belemásolod.
1. lehetőség:
strcpy(string,"Szovegem.....");
2. formázottam:
sprintf(string,"Ide jön a szöveg, meg az egyég báltozók kiiratási módja, pl %d értéke",egy_szam);
A pointerek egy kicsit másak.
Két tulajdonságuk van, a hoszuk, és az értékük. A hosszával csak a fordító foglalkozik, meg persze Te, ígazából sehol nem jelenik meg, de ha a pointerhez hozzáadsz, akkor mindig a hozzáadott érték szer a hosszával nő az értéke. Az értéke meg egy cím ahova mutat.
A string változót (ami egy char tömb) ne keverd egy pointerrel!!!
- A hozzászóláshoz be kell jelentkezni
Inkabb strncpy! Nem lehet elegszer hangsulyozni :)
- A hozzászóláshoz be kell jelentkezni
Egy kicsit hozzáfűzök, nem biztos, hogy hasznos lesz most, de szerintem jó tudni.
Amikor egy stringet pl. így deklarálsz, hogy:
char *valami = "Túró";
akkor azt mondod, hogy a "Túró"-t tárolja le a binárisban, a valami kezdeti értékét pedig tegye a tárolt terület elejére.
Abban az esetben, ha a következőt használod:
char valami[256];
akkor utasítod a fordítót, hogy a binárisban jelezze, hogy félreteszel statikusan 256 byte-ot. Majd ha strcpy-vel, memcpy-vel, stb. Belemásolsz, akkor erre az eleve lefoglalt statikus területre írsz.
Ha pedig ezt használod:
char *valami = (char *)malloc(sizeof(char)*256);
akkor a fordítót utasítod, hogy a bináris tartalmazza a malloc hívást. A malloc pedig utasítja a rendszert, hogy foglaljon le neked 256 char-t.
"i pensieri stretti & il viso sciolto." -- Sir Henry Wootton
- A hozzászóláshoz be kell jelentkezni
ez a "binaris tartalmazza a malloc hivast" meg a "binarisban jelezze, hogy..." ez nagyon gaz :-)
- A hozzászóláshoz be kell jelentkezni
Jó, akkor legyen rántájm és kompájltájm. :-)
"i pensieri stretti & il viso sciolto." -- Sir Henry Wootton
- A hozzászóláshoz be kell jelentkezni
Abban az esetben, ha a következőt használod:
char valami[256];
akkor utasítod a fordítót, hogy a binárisban jelezze, hogy félreteszel statikusan 256 byte-ot. Majd ha strcpy-vel, memcpy-vel, stb. Belemásolsz, akkor erre az eleve lefoglalt statikus területre írsz.
a 'statikus' egészen mást jelent, itt azt akartad írni, hogy
char valami[256];
-> a stack-ben lefoglalsz 256 byteot
char* valami=(char*) malloc(sizeof(char)*256);
-> a heap-ben foglalsz le 256 byteot
static char valami[256]
-> a statikus területre foglalod le a változót.
- A hozzászóláshoz be kell jelentkezni
Statikusat ugy ertettem, hogy nem futasidoben, hanem forditasi idoben kulonul el a terulet.
"i pensieri stretti & il viso sciolto." -- Sir Henry Wootton
- A hozzászóláshoz be kell jelentkezni
de azért vigyázz, mit beszélsz! ;)
- A hozzászóláshoz be kell jelentkezni
Jóvanna, tartom magam a mottómhoz. Alig győzök magyarázkodni. :-)
"i pensieri stretti & il viso sciolto." -- Sir Henry Wootton
- A hozzászóláshoz be kell jelentkezni
Brian W. Kernighan and Dennis M. Ritchie, A C programozási nyelv, Az ANSI szerint szabványosított változat, Műszaki Könyvkiadó
Morzel
- A hozzászóláshoz be kell jelentkezni
már mindent leírtak előttem, csak morzsákat teszek hozzá. A
char* ptr;
az egy pointer változót deklarál. Ez egy integer, ami megmondja, hogy a memóriában hányadik bájton kezdődik az adat.
A fenti változó egy karakter címét hordozza.
Mivel a pointert nem inicializáltad, ezért értéke bármi is lehet, vagyis a memóriában random helyre mutat. Ez nagyon rossz gyakorlat, általában előbb-utóbb katasztrófához vezet.
A C-ben a string az karaktertömb, más néven karakterek /byte-ok/ memóriafolytonos egymás utánja, amit nulla byte zár le.
Ezért az első karakter címét elég megadni, hiszen azután ott következik a többi.
Ha valahol ezt írod:
"szoveg"
akkor egy string konstanst hoztál létre. Ezt a gcc beleteszi a binárisba, egy csak olvasható adatterületre. Ha ide írni próbálsz, segfaultot kapsz. A
char* ptr = "szoveg";
létrehoz egy pointer változót /beleteszi egy integer változóba a konstans string első byte-jának címét/.
A fordítók kellemes tulajdonsága, hogy a többször előforduló ugyanlyan string konstansokat csak egyszer tárolják. Hiszen minek is tárolnák többször, ha egyszer úgysem módosítható?
Ezért a
char* ptr = "szoveg";
char* ptr2 = "szoveg";
eredménye az lesz, hogy a
's' 'z' 'o' 'v' 'e' 'g' '\0'
bájt sorozat csak egyszer lesz benn a binárisban, és mindkét pointer arra lesz ráállítva.
Figyelmesebb szemlélőnek feltűnhet, hogy a pointer változónk deklarációja tulajdonképpen hibás, hiszen a
const char* ptr = "szoveg";
lett volna a helyes. Ez fejezi ki, hogy a pointer által mutatott adatterület nem módosítható!
Kezdő programozók vért izzadnak, mire megértik, hogy miért fontos a const korrektség. Lamer programozók ismérve, hogy ezt soha nem értik meg, és nem fogadják el. Ajánlom, hogy te a szabályos úton menj.
Más:
a karakter konstansokat ne keverd össze a pointerekkel! Lásd
const char* ptr = "szoveg";
const char myszoveg1[] = "szoveg1";
const char myszoveg2[] = {'s','z','o','v','e','g','2','\0'};
A ptr itt egy önálló életet élő pointer változó. Értéke megváltoztatható, ekkor egy másik karakter vagy string címe kerül bele.
A myszoveg1, myszoveg2 string konstansok címei. Így csupán szimbolikus nevet adunk egy string konstansnak.
Más: ha műveleteket akarun kvégezni stringekkel, akkor átmeneti string kezelő adatterületekre lesz szükségünk. Ezek a nem const karakter tömbök:
char tmps[100];
Ebbe lehet firkálni, belemásolni, appendelni stb a stringeket.
De: a C-s stringkezelő függvények tulajdonképpen teljesen elavultak, mert biztonsági résekhez vezethetnek. Keveredik bennük a fix méretű temporáris stringterület és a definiálatlan hosszúságú nullával lezért string koncepció.
Ha megteheted, nagyobb string kezeléssel foglalkozó rendszert ne C-ben írj! Használd inkább a magasabb szintű c++ string kezelő osztályokat.
- A hozzászóláshoz be kell jelentkezni
pont, hogy nem valtoztathato meg, hiszen const. marmint a legutolso peldaban a const char *ptr (nemtudom mi ez a mania hogy a tipus utan irjak a csillagot...), masreszt a const ANNYIRA nem fontos, max olvashatosagon javit IMHO => kompajler ugyis eszreveszi. meg nem lehet mindig tudni, hogy mi lesz const, mi nem...
- A hozzászóláshoz be kell jelentkezni
const char * string = "almafa";
Itt nyilván ilyet nem lehet: string[0] = 'c';
No de később a kódban:
const char string2[] ="körtefa";
string = string2;
teljesen helyes, hiszen nem a pointer konstans, hanem a pointer által hivatkozott terület
Helyes használat:
const char* const string = "almafa";
- A hozzászóláshoz be kell jelentkezni
Helyes használat:
const char* const string = "almafa";
A contextustól függ, hogy helyes-e a második const használata.
Akkor helyes, ha a string nevű változónk olyan, hogy mindig a "almafa"-ra kell mutatnia, és meg akarjuk tiltani, hogy valaki egy másik konstans stringre állítsa rá. Ekkor viszont felesleges pointer változót használni, erre valók a string konstansok.
- A hozzászóláshoz be kell jelentkezni
string konstans? Mire gondolsz? ui:
char * s1 =...;
char s2[] =...;
a kettő ekvivalens.
- A hozzászóláshoz be kell jelentkezni
NEM EKVIVALENS.
s1 egy pointer változó, 4 byte-ot foglal.
s2 egy karakterfüzér első byte-jának címe, 0 byte-ot foglal, a felhasználás helyén behelyettestődik a 4 byte-os értéke.
- A hozzászóláshoz be kell jelentkezni
ugytunik h ujdonsag szamodra h a tombok es pointerek kozott nincs semmi kulonbseg :)
lehet k&rt el kene olvasni?
- A hozzászóláshoz be kell jelentkezni
kedves vmiklos, inkább nézz utána, mielőtt személyeskedsz, ugyanis a tömb és a pointer bezony eltérő módon viselkedik.
- A hozzászóláshoz be kell jelentkezni
pedig igaza van: elso esetben van a tomb valahol a memben + 4 bajtos pointer (amit aztan mashova is "iranyithatsz")
masodik esetben csak a tombod van, nincs "pointer"
--
A vegtelen ciklus is vegeter egyszer, csak kelloen eros hardver kell hozza!
- A hozzászóláshoz be kell jelentkezni
Felhasználás helyén? Azt a precompiler végzi, ha valahol van egy #define utasítás. De itt nincs.
64 bites rendszereken a pointerek is 8 bájtosak. Sőt, MS-DOS 16 bites rendszer, ott meg 2. Na most akkor hogy is van?
- A hozzászóláshoz be kell jelentkezni
lényegtelen, hogy hány bájtos, a 4-et csak a tömörebb szövegért írtam "az adott platformon érvényes pointerméret byte-ban" helyett.
Az inicializált tömb bizony majdnem teljesen úgy viselkedik, mint egy define, a változó viszont nem úgy viselkedik! Tárfoglalása van /+1 pointernyi/, változtatható értéke van, értelmes a címét lekérdezni, használatakor +1 memóriaelérésre van szükség!
példa:
const char szoveg[] = "szoveg";
const char* ptr = szoveg;
int func()
{
printf("%s\n", szoveg); /* 1 */
printf("%s\n", ptr); /* 2 */
}
Az 1-es esetben a lefordított kód valami ilyesmi:
ld eax,0x12345678 ;szoveg[] konstans cimenek betoltese
push eax
call _printf
A 2-es esetben viszont ez:
lea eax,ptr ;ptr cimenek betoltese
mov eax,[eax] ;ptr-ben levo cim betoltese
push eax
call _printf
teccik latni a +1 indirekciot?
- A hozzászóláshoz be kell jelentkezni
Abban igazad van, hogy pointernek lehet értéket adni, char tömbnek meg nem.
//pointerek.c
int main() {
char szoveg[] = "szoveg";
char szoveg2[] = "szove2";
char*const ptr = "szove3";
puts(szoveg);
puts(szoveg2);
puts(ptr);
puts("====");
2[szoveg]='a';
szoveg2[3] = 'b';
*(ptr+2) = 'a';
return 0;
}
make CFLAGS="-Wall -Werror -pedantic -std=c99" pointerek
és lefordul! Sőt, ansi mellett is. És segfault-ol. No ez már röhejes. Triviális hiba, könnyen kiszúrhatná a fordító.
Ha nem adnék kezdőértéket, nem lenne különbség a változók között.
- A hozzászóláshoz be kell jelentkezni
nem tudom, hogy pontosan mire gondoltál, de az ilyet:
char* const ptr="szove3";
*(ptr+2) = 'a';
nem biztos hogy a fordítónak kéne kiszűrnie, ld.:
char* p="alma";
char a[]="korte";
unsigned diff=(unsigned)a - (unsigned)p;
*(p+diff)='b';
- A hozzászóláshoz be kell jelentkezni
a C sajnos nem eléggé strict. ez c++ -ban nem fordul.
- A hozzászóláshoz be kell jelentkezni
const char * string = "almafa";
teljesen helyes, hiszen nem a pointer konstans, hanem a pointer által hivatkozott terület
*gyakorlatilag* nem a pointer által hivatkozott terület a konstans, hanem egy const T* -szerű pointeren keresztül nem lehet megváltoztatni a mutatott területen lévő típus értékét. A const egy fordítási időben jelenlévő attribútum (gimme ellenpélda, amikor nem az).
- A hozzászóláshoz be kell jelentkezni
a legtöbb mai architektúrán nem igaz, amit írsz. 386-os procitól vannak a csak olvasható memória szegmensek. Ha ezekbe próbálsz írni, akkor coredump a jutalmad.
A literális string konstansokat és a const változókat a c fordítók általában ilyen adatszegmensekbe pakolják, ezek tehát valóban, fizikailag sem írhatók közönséges pointer castolással.
példa:
const char* ptr = "almafa";
int func()
{
*(char*)ptr = 'b'; /* core dump! */
}
Hogy ez ne csak futási időben derüljön ki, hanem fordítási idejű támogatásod is legyen, azért létezik a const.
- A hozzászóláshoz be kell jelentkezni
Tudnál mondani olyan gcc kapcsolót, amivel olyan kódot fordít ebből (az én gépemen) hogy segfaultoljon?:
const int a=5;
int* m=(int*) &a;
*m=3;
- A hozzászóláshoz be kell jelentkezni
ötleteim:
- a változó deklaráció legyen függvényen kívül /alkalmazás globális változó legyen, ne lokális változó/!
- c fordítót használj /ne c++ -t/
- próbáld meg string konstanssal
- O1
- nézd meg debuggerrel, hogy ne optimalizálja ki a változót
- A hozzászóláshoz be kell jelentkezni
eredmények:
- globális const változóval segfault, tehát így jó
- gcc-vel fordítottam eddig is:)
- string konstanssal is segfaultol
itt van a kutya elásva:
0x0804836b : movl $0x5,0xfffffff8(%ebp)
0x08048372 : movl $0x3,0xfffffff8(%ebp)
Ezek szerint readonly szegmenst csak egyet foglal le, ahova viszont csak globálisan const-nak definiált változókat, meg string konstansokat rak.
- A hozzászóláshoz be kell jelentkezni
viszont akkor nem értem, hogy a topicnyitó kódjában az strcpy miért nem dobott segfaultot... mert nekem azt dob.
"char *string1="0";
char *string2="0";
strcpy(string1,"blabla");
printf("%s",string2);
kod blabla-t nyomtat a kepernyore."
- A hozzászóláshoz be kell jelentkezni
const int a = 12345;
main()
{
int *p = (int*)&a;
*p = 123;
return 0;
}
ez meghal... függetlenül hogy C vagy C++.
gcc 3.3.6, slackware linux
- A hozzászóláshoz be kell jelentkezni
Ja, fentebb már rá is jöttem, miért meg hogyan. :)
- A hozzászóláshoz be kell jelentkezni
Segfaultot nem tudok produkálni, de más érdekes viselkedést igen.
Próbáld ki ezt:
const int a=5;
const int b=5;
printf ("Elotte a:%d b:%d\n",a,b);
int* m=(int*) &a;
*m=3;
printf ("Utana a:%d b:%d\n",a,b);
Próbáld ki optimalizálás nélkül, és -O1-el.
- A hozzászóláshoz be kell jelentkezni
Ez jó, és igaza is van egyébként: aki ész nélkül kasztol, az megjárja :)
- A hozzászóláshoz be kell jelentkezni