C++-ban kellene kódot írnom (hobby), de még a C sem az erősségem.
Próbálom megérteni, hogy elméleti alapon miért rossz a C++ következő kód:
const char* fix_string = "FIX STRING";
char* szoveg = fix_string;
Nem értem, hibás ez? Amit mondani akarok vele, hogy van egy szöveges konstansom, ami sohasem változhat, és van egy másik szöveges változóm, ami felvehet tetszőleges értéket, például a fix értéket is.
Hol rontom el a gondolatmenetemet?
- 746 megtekintés
Hozzászólások
Nem a fix_string pointer konstans, hanem amire mutat. Tehát fix_string[0]='a' nem jó, viszont
szoveg[0]='a' igen. Még azt tegyük hozzá, hogy a 'char *' csak egy pointer, tehát az értékadásod nem stringet másol, hanem csak egy pointert állít át.
- A hozzászóláshoz be kell jelentkezni
Lehet, így írnám inkább:
const char fix_string[] = "FIX STRING";
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Szerintem is csak ilyen egyszerű, nem kell a *. Igazából a szögletes zárójelek elhagyásával is működnie kéne.
“Windows 95/98: 32 bit extension and a graphical shell for a 16 bit patch to an 8 bit operating system originally coded for a 4 bit microprocessor, written by a 2 bit company that can't stand 1 bit of competition.”
- A hozzászóláshoz be kell jelentkezni
Igazából a szögletes zárójelek elhagyásával is működnie kéne.
Lehet, hogy a compiler rájön, hogy nem egyetlen karakterről van szó - bár nem tudom -, de mindenképp értelemzavaró, ha karakter típusnak van deklarálva valami, ami egy karakterre mutató cím. Szóval én akkor is kiírnám a szögletes zárójelet, ha véletlenül nélküle is lefordul.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Ezzel az a baj ogy a fix_string altal mutatott adatot ("FIX STRING") a linker olyan teruletre helyez(het)i ami csak olvashato. Ezt hivjak .rodata szegmensnek. Ha csinalsz egy `char* szoveg` pointert, ami utana bele mutat ebbe a teruletbe, akkor a fordito azt hiheti hogy ez a terulet modosithato. Azaz a szoveg[0]='a' egy szep combos segfault-hoz vezethet (vagy mikrokontrolleren egy bus error-hoz, extrem esetben meg a periferia-busz is mas, pl egy harvard architekturan ez const char* altal mutatott string mehet a progrmem-be, mig a sima char * a RAM-ba mutat).
Olyannnyira igaz ez hogy eleve a string mint tipus meg sima mezei C-ben is const char * tipusu lesz, meg ha ki sem irod:
apal@laptop:~$ cat x.c
#include <stdio.h>
char * fix_string = "FIX STRING";
int main(void)
{
char * szoveg = fix_string;
szoveg=fix_string;
/* szoveg[0]='a'; */
printf("%s\n",szoveg);
return(0);
}
apal@laptop:~$ gcc -o x x.c
apal@laptop:~$ ./x
FIX STRING
apal@laptop:~$
verzusz:
apal@laptop:~$ cat x.c
#include <stdio.h>
char * fix_string = "FIX STRING";
int main(void)
{
char * szoveg = fix_string;
szoveg=fix_string;
szoveg[0]='a';
printf("%s\n",szoveg);
return(0);
}
apal@laptop:~$ gcc -o x x.c
apal@laptop:~$ ./x
Segmentation fault
apal@laptop:~$
Es akkor itt jonnek szep sorban a warning/error szintek:
- naivan, alapertelmezesben C nyelv eseten a fenti pelda nem ad forditasi warningot/hibat
- ha a const-ot kiirod a fix_string ele, akkor mar a C forditasi warningot ad (azaz szol hogy valami potencialis gaz lehet, es gaz is lesz mert segfault)
- a C++ eseten ez meg mar nemhogy warning hanem error, az megerosebben tipusos
- A hozzászóláshoz be kell jelentkezni
Teljesen jó, amit írsz, csak erről eszembe jut, mindig roppant bizonytalan vagyok abban, ilyenkor a string vagy a pointer lesz read only, illetve hogyan kell leírni azt, ha a másikat szeretném.
char *p
const char *p
char const *p
const char const *p
Ezeknek egyáltalán van így értelmük?
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
A const a C++-ből származik, de ennek ellenére nem hülyeség. Gondok azért vannak vele, lásd pl. itt: http://yosefk.com/c++fqa/const.html
By the way, not using const in C++ is quite likely not a very good idea. Of all questionable C++ features, const probably does the most visible damage when avoided.
- A hozzászóláshoz be kell jelentkezni
Persze, mukodnek ezek is. Sot, ilyet is csinalhatsz:
const char const * const * fix_string_array;
- A hozzászóláshoz be kell jelentkezni
De ez már valami ilyesmi: { "alma", "körte", "szilva" }
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
- A hozzászóláshoz be kell jelentkezni
Van belőle Debian csomag is.
- A hozzászóláshoz be kell jelentkezni
Én nem értek a C++-hoz, de C-ben nem olyan bonyolult:
const char *p - konstans karakterre mutató változó pointer
char * const p - változó karakterre mutató konstans pointer
const char * const p - konstans karakterre mutató konstans pointer
lejjebb linkelték a cdecl.org-ot, ott jól lehet ilyenekkel játszani. A fenti példáidban az utolsó 3 ugyanazt írja le, a legutolsóban a második const felesleges.
- A hozzászóláshoz be kell jelentkezni
Tulajdonképpen logikus, hogy az első esetben a dereferált érték a const char, a másodikban a pointer const, ami char-ra mutat, míg az utolsóban const a pointer, majd a dereferált char típus is const. Így látva megvan, de szinte sohasem használta, így fejből nem ment volna, mint ahogy nem is ment. :(
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
nna, szep peldaja, hogy miert is nepszeru a rust. ertem, hogy bonyi, de a c, aminek holt egyszerunek kellene lennie, szinten boven eleg bonyi. :(
- A hozzászóláshoz be kell jelentkezni
Akkor picit pontosítok a kérdésen, mert úgy tűnik, a fenti meglátások mind azt magyarázzák, miért jó, hogy hibát ad a fordító, és nem azt, hogy egy ilyen problémát hogyan kellene megfogalmazni c++ alatt.
Tehát szeretnék definiálni egy konstans stringet, vagyis azt akarom, hogy sem a változó által mutatott cím, sem a string tartalma ne változzon. Ha jól értem, a
const char* FIX="fix";
ezt definiálja. Ha az elejéről a const elmarad, akkor a FIX pointer értéke változhat, de maga a szöveg akkor sem.(?)
De hogyan tudom ezt korrekt módon értékül adni egy olyan változónak, aminek ez - mondjuk csak - a kiindulási értéke kell, hogy legyen, de később kaphat majd más értéket is.
A
char* valtozo = FIX;
kódra fordítási hibát kapok. Nem tudom, a fenti igényt hogyan fogalmazhatnám meg úgy, hogy a fordító le is fordítsa.
Vagy ezt a kifejezést nem lehet a c++ alatt helyesen megfogalmazni?
- A hozzászóláshoz be kell jelentkezni
Igazából ez az egész itt olyan, mintha azt kérdeznéd, hogy milyen a korszerű gőzmozdony. A válasz az, hogy semmilyen, nincs korszerű gőzmozdony.
Tehát ha C++-t akarsz, akkor a string típus a barátod; ha C-t, akkor már jogos a kérdés:
char *valtozo= strdup(fix);
vagy például, ha hosszabb szövegnek akarsz fix helyet foglalni:
char valtozo[1024];
strcpy(valtozo, fix);
- A hozzászóláshoz be kell jelentkezni
1 - Továbbra sem értem, hogy elméleti alapon miért rossz, hogy egy konstans területen lévő értéket használni akarok. A gőzgép elavult, de egy fix érték felhasználása a programkódban nem hiszem, hogy elavult lenne. A C++ - úgy tudtam - a C korrektebb, teljesebbé tett változata. Jobban véd, hogy struktúrálatlan programot írjak, így biztosabb lesz a kód. De a konstans érték felhasználása nem biztonsági kockázat. Semmilyen elvi akadálya nincs, hogy ezt értelmezni lehessen. Nem hibaforrás. Ennek ellenére nem hiszem, hogy csak azért ad rá hibát, mert nem programozták le a fordítóban, hogy ne adjon.
2 - Ha ezt mégis csak valamilyen String objektummal lehet megoldani C++ alatt, akkor az hogyan néz ki?
- A hozzászóláshoz be kell jelentkezni
Meg kéne ismerkedned kicsit a unixok memóriakezelésének alapjaival (stack, heap), mit hol lehet tárolni, mi az a scope, meddig él.
Utána összeáll a kép. Vagy csak simán fogadd el hogy így van.
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
> Továbbra sem értem, hogy elméleti alapon miért rossz, hogy egy konstans területen lévő értéket használni akarok.
> A gőzgép elavult, de egy fix érték felhasználása a programkódban nem hiszem, hogy elavult lenne.
Olvasni lehet. Lemásolni lehet. Felülírni nem lehet/szabad/illik. (Platformfüggő, hogy mi történik.)
Talán az nem ment át, hogy egy pointer-értékadás, az nem a string (karaktereinek) másolása, csak egy pointer ráállítása ugyanarra a stringre.
> A C++ - úgy tudtam - a C korrektebb, teljesebbé tett változata. Jobban véd, hogy struktúrálatlan programot írjak, így biztosabb lesz a kód.
Igazából egy teljesen külön nyelv, amelynek a legfőbb tulajdonsága, hogy mérhetetlenül bonyolult és nehezen tanulható, viszont 30+ éve intenzíven változik, mondhatni gyerekkorát éli.
> De a konstans érték felhasználása nem biztonsági kockázat. Semmilyen elvi akadálya nincs, hogy ezt értelmezni lehessen. Nem hibaforrás. Ennek ellenére nem hiszem, hogy csak azért ad rá hibát, mert nem programozták le a fordítóban, hogy ne adjon.
Olvasni lehet. Lemásolni lehet. Felülírni nem lehet/szabad/illik. (Platformfüggő, hogy mi történik.)
Talán az nem ment át, hogy egy pointer-értékadás, az nem a string (karaktereinek) másolása, csak egy pointer ráállítása ugyanarra a stringre.
Az előbbi kettőhöz adok még egy példát:
char varstr[]= "Kezdeti ertek, felulirhato";
> Ha ezt mégis csak valamilyen String objektummal lehet megoldani C++ alatt, akkor az hogyan néz ki?
Pl.:
const string fix= "Fix";
string var= fix;
- A hozzászóláshoz be kell jelentkezni
char varstr[]= "Kezdeti ertek, felulirhato";
*felülírható maximum ugyanannyi bájttal, mint az eredeti karaktertömb volt és figyelni kell a \0-ra a végén,
https://stackoverflow.com/questions/9593798/proper-way-to-copy-c-strings
De érdemesebb használni az std::string-et C++-ban annak, akinek fogalma sincs a pointerekről.
- A hozzászóláshoz be kell jelentkezni
A C fix stringek csak olvasható területen vannak.
A "FIX STRING"-et nem tudod átírni!
Nézzük a következő példát:
char *strcpy(char *dest, const char *src)
Sima char* előléptethető const char-á, tehát az src lehet const char* is meg sima char* is. Ellenben a dest SEMMI ESETBEN sem lehet const char*, csak char*.
Azért nem tudod sima char*-ra megadni, hogy rámutasson, mert a sima char* viszont már írható területre mutat. És így pl. be tudnád adni a szoveg változódat a dest helyére, viszont ha a szoveg egy csak olvasható területre mutat, akkor már meg is van a baj.
szerk: viszont a fix_string-ben nem a pointer a konstans, hanem a mutatott terület. Annak nincs akadálya, hogy később kiadj egy ilyen utasítást:
fix_string="MÁSIK FIX SZÖVEG";
Ezt simán meg fogja enni.
Az is működik, hogy a szoveg is const char*.
És az is működik, hogy pl. a fenti strcpy-val lemásolod a fix_string-et a szoveg-be. Természetesen előre allokálva neki területet.
szerk2: a char*-ot jó lenne a C++-ban elfelejteni. Sajnos tudom hogy nem lehet mindenhol kiküszöbölni, de akkor minek van az std::string?
szerk3: ne keverd a magyar és az angol változóneveket :)
- A hozzászóláshoz be kell jelentkezni
De a konstans érték felhasználása nem biztonsági kockázat.
Nem az. Hasznald egeszseggel ;) De a "felhasznalas" (adathoz valo hozzaferes) meg a "feluliras" (adat modositasa) ket kulonbozo dolog.
A stringek meg ugy altalaban a konstans tombok kezelesnek a modja az 4 fele lehet, es mind a 4 fele modozat az letezik a gyakorlatban. Azaz vagy van memoriavedelem a (operacios/architekturalis) rendszerben vagy nincs, es vagy hardvard-szeru az architektura, vagy egy adatbuszon log mindenki. Peldak ezekre:
- nincs memoriavedelem + egy adagbusz van: ilyen a klasszikus DOS: csinalhatsz sztringeket, el is ered, oke, nyugodtan felul is irhatod es akkor felul lesz irva latvanyos kovetkezmenyek (vagy ha ugyes vagy, semmilyen kovetkezemey nelkul). De ugye ismerjuk hogy a DOS milyen, milyen volt...
- nincs memoriavedelem de tobb adatbusz van: ilyen pl az AVR rendszer. itt mar figyelni kell hogy mi micsoda, a forditonak nagyon tudnia kell mit csinalsz (mire gondol a kolto), mert mas gepi kodu utasitas eri el a stringeket mint a sima mezei char xyz[...] tomboket (azaz nemcsak tobb adatbusz van, de tobb cimtered is van).
- van memoriavedelem + egy adatbusz van: ilyen a klassizkus UNIX/Linux rendszer, ezt ismerjuk ilyenkor mi a kovetkezmeny (virtualis lapok vannak, amiket az op.rendszer leved, readonly modon (r--) lehet csak a .rodata szegmenst kezelni, a const whatever * tipusok oda mennek, stringek oda mennek alapbol, stbstb).
- van memoriavedelem + tobb adatbusz van: ilyenek peldaul a Cortex-M magok, amik bus errorral vagy hasonlo modon reagalnak ha ezt az egeszet nem jol csinalod (pl a fenti nagyobb peldat ha egy Cortex-M4-on forditod akkor az egy bus exception-ba fog belerohanni, nem segfaultba). Az mas kerdes hogy ezt mennyire hiv(hat)juk memoriavedelemnek, de ezeknel a magoknal ez mar megjelenik - es persze az "adatbusz" is kicsit viszonylagos, mert vannak mindenfele egyeb hardverelemek menetkozben (arbitrerek, bus matrix-ok, stb). Mondjuk azt hogy "egy cimter van de tobb adatbusz".
A lenyeg a lenyeg hogy ha ezt az egeszet jol absztrahalod, kello tampontot adsz a forditonak es/vagy betartod a sztenderdeket akkor olyan C/C++ kodot kapsz, ami mind a 4 fenti alap-architekturan szepen es biztonsaggal fog mukodni. Ha nem, akkor lehet hogy latszolag jo lesz (pl megfelelo trukkozessel, cast-olassal, void *-os jatekokkal, stb ki tudod kerulni a warningokat/errorokat), de nagyon konnyen beleszaladsz egy pofonba, akar sokkal kesobb is. Es nehez kidebuggolni hogy mi a baj.
- A hozzászóláshoz be kell jelentkezni
Attól függ, mire használja.
Arduino alatt a string-et a malloc/free hívása miatt eszembe nem jutna használni. Brutális erőforrás pazarlás például egy 32k-s MCU alatt.
- A hozzászóláshoz be kell jelentkezni
Ez ilyen vak vezet világtalan lesz, mert én is sokszor kipróbálom, amikor ezekre szükségem van.
Egyrészt nem mondtad, mi az a fordítási hiba. Írd le a hibaüzenetet! Másrészt nem mindegy, hogy a string egy read only területen, például flash memóriában tárolt karakter lánc, amelyre mutat egy RAM-ban tárolt, így felülírható pointer, vagy a pointer az, ami nem változtatható meg.
char* valtozo = FIX;
szerintem azért problémás, mert a pointereknek van típusuk, nem void * az összes. Tehát, ha egy olyan típusú pointernek adsz értéket, ami azt tudja magáról, hogy az általa címzett memóriatartalom írható, olvasható, s egy olyan címet kap értékül, amelyen lévő tartalom nem írható, akkor a fordító szorongani kezd, s attól tart, hogy a későbbiekben csinálsz valami ilyesmit:
*(valtozo + 2) = 'A';
Szerintem lehetőséged van erre:
const char *valtozo;
valtozo = FIX;
valtozo++;
printf("%c\n", *valtozo);
Vagy csinálhatod ezt:
char *valtozo;
valtozo = (char *) FIX;
Csak ez utóbbi esetben úttörő becsszóra ne próbálj *valtozo-nak értéket adni, mert abból tényleg segfault lesz.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Látom akinek csak kalapácsa van... :)
Egy szóval nem írta, hogy ez mikrokontroller lenne, meg C++ nem is nagyon gyakori ott, valószínűleg nem az lesz.
Másrészt azt eddig egyedül a microchip 8 bites C fordítójánál láttam, hogy a const-ot betenné a flash-be, és onnan olvasná. Az AVR-GCC pl. nem így csinálja (https://www.nongnu.org/avr-libc/user-manual/FAQ.html, Why do all my "foo...bar" strings eat up the SRAM?). Tekintve hogy ezek harvard architektúrák, annyira valóban nem is egyszerű.
- A hozzászóláshoz be kell jelentkezni
A nagy esküt nem tenném le rá, el kellene olvasni a doksit, de elég nagy pazarlás lenne, ha a konstans stringeket initben bemásolná RAM-ba flashből. Van benne 2 MiB flash és 512 kiB RAM. Szerintem PIC32 is simán flash-be allokálja a konstans cuccokat, stringeket, struktúrákat, bármit. Nagyon remélem legalább is.
Ebből a szempontból mindegy, hogy azért nem írható, mert flash, vagy azért, mert read only szegmensben van.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Az arm-none-eabi-gcc tuti flashbe teszi a const sztringeket.
- A hozzászóláshoz be kell jelentkezni
Egesz konkretan a .rodata-ba teszi, es azt teszi bele kesobb a flash-be. De ugye az ARM mag az nem csak flash-bol tud futtatni hanem RAM-bol is, a RAM-ot meg feltoltheted akar SWD-n keresztul is (es ugy inditod el a rendszert). Na, akkor a .rodata-t is mashova kell linkelni. Szoval ez sem annyira magatol ertetodo teljesen... :]
- A hozzászóláshoz be kell jelentkezni
Az igaz, hogy az .ld fájllal lehet játszani, pl. egy projektben így van:
.rodata :
{
*(.rodata)
} >FLASH
- A hozzászóláshoz be kell jelentkezni
Ja, csak az ARM nál egyetlen címtér van tudtommal, ott ezt sokkal könnyebb megoldani. Az AVR ilyen szempontból nyűgösebb, épp ezt írják le mint probléma.
- A hozzászóláshoz be kell jelentkezni
Tehát szeretnék definiálni egy konstans stringet, vagyis azt akarom, hogy sem a változó által mutatott cím, sem a string tartalma ne változzon. Ha jól értem, a
const char* FIX="fix";
ezt definiálja. Ha az elejéről a const elmarad, akkor a FIX pointer értéke változhat, de maga a szöveg akkor sem.(?)
Rosszul érted. Ez csak azt mondja, hogy a sztring tartalma nem változtatható, de hogy a FIX nevű változód mire mutat, az változhat a program futása során.
Amit akarsz, a következő két módon tudod:
const char c1[] = "x";
const char * const c2 = "y";
Alább meg egy példaprogram, hogy van két nem változtatható sztringed, amire nem változtatható pointerek mutatnak (c1 és c2); és van egy pointer, ami változhat, hogy hová mutat, de csak nem változtatható sztringekre mutathat (v):
#include <iostream>
using namespace std;
const char * const c1 = "x";
const char * const c2 = "y";
int main()
{
const char * v;
v = c1;
cout << v << endl;
v = c2;
cout << v << endl;
return 0;
}
- A hozzászóláshoz be kell jelentkezni
A C-t, bár régen belőle kerestem a kenyeremet, ma már a könnyű öntökönlövés nyelvének érzem. Onnan indul, hogy első ránézésre könnyűnek tűnik, közben egy nagyobb projektet nézve borzasztóan egyszerű benne csendben megbúvó módon téves számítási eredményt adó kódot írni. A benne levő nyers, védelmet mellőző pointer trükkök szintén szépek, de szintén a sunyi hibák melegágya. Bár tény, hogy sokan szeretik megülni a bikát is, mert például macsó dolognak tartják, de én már inkább azt vallom, hogy feleslegesen nem célszerű leesni. Legalábbis nem hiányzanak egy-egy nagyobb projektben a low-level hibák általi kockázatok, éjszakába nyúló hibakeresések. Rust esetén az iménti C++ példa ennyi és közben ugyanolyan gyorsan fut:
const C1: &str = "x";
const C2: &str = "y";
fn main() {
let mut v = C1;
println!("{v}");
v = C2;
println!("{v}");
}
Lehet próbálkozni a kijátszásával.
- A hozzászóláshoz be kell jelentkezni
Szerintem meg a szabadságot jelenti. Gyűlölöm azokat a software-eket, amelyek „kitalálják” helyettem, én mit akarok, persze, mivel szeretem a különutas megoldásokat, sohasem azt akarom, így ezek a segítségek állandóan akadályoznak a munkában.
Nekem ki ne találja egy fordító, hogy szerinte én mire gondolok, mert nem. Nem arra.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Azért ennyi filozófia nincs benne: ha nem akarod, hogy a változó konstans legyen, akkor ne írd oda a `const` módosítót.
- A hozzászóláshoz be kell jelentkezni
Elsősorban erre írtam:
Bár tény, hogy sokan szeretik megülni a bikát is, mert például macsó dolognak tartják, de én már inkább azt vallom, hogy feleslegesen nem célszerű leesni.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni