C-čko a zápis mimo proměnnou

MaLaMuT

C-čko a zápis mimo proměnnou
« kdy: 31. 10. 2017, 17:45:23 »
Ahoj,

Cčko se chová trochu jinak, než bych čekal.

Mám pole znaků (A to začne dělat problémy...):
unsigned char myfield[32];
Vynuluji ho:
memset(myfield, 0, 32);
Vytvořím si proměnnou, která by se neměla měnit:
char vstupniSoubor[] ="in.tx";

A teď začnu zapisovat do toho pole, resp. nastavovat prvky na pozici 0.
Na pozici 0 zapíšu hodnotu 0-255:
myfield[0]=(unsigned char)255;
printf("%s\n",vstupniSoubor);
myfield[0]=(unsigned char)0;
printf("%s\n",vstupniSoubor);
myfield[0]=(unsigned char)127;
printf("%s\n",vstupniSoubor);
myfield[0]=(unsigned char)255;

No a tohle mi velice rychle rozmrcasí tu proměnnou vstupniSoubor :-(


Když to nechám běžet dostatečně dlouho, rozbije to okno terminálu a nakonec to položí celý stroj.

Samotná hodnota myfield je naprosto v pořádku.
Pokud měním jiný prvek než nultý, tak se problém neprojevuje (možná to dělá paseku mimo tu proměnnou, kterou zobrazuji).

Jednak nechápu, proč by to mělo přetékat mimo pole.
Tj. kdyby se hodnota 0-255 nevešla do charu, což by se vejít měla, tak by to mělo ovlivnit sousední prvek.

Nakopněte mě, co je na tomhle špatně:
myfield[0]=(unsigned char)255;
Tato varianta se chová úplně stejně:
myfield[0]=255;

Co je špatně?  :-\


<x axcascaxc

Re:C-čko a zápis mimo proměnnou
« Odpověď #1 kdy: 31. 10. 2017, 17:55:29 »
verze kompileru?

cely zdrojovy kod?

mi to nic spatneho nedela.

vypis:

in.tx
in.tx
in.tx

MaLaMuT

Re:C-čko a zápis mimo proměnnou
« Odpověď #2 kdy: 31. 10. 2017, 18:01:09 »
Překladač: gcc version 6.3.0 20170516 (Debian 6.3.0-18)
Používám syntaxi C99, protože jedu jednořádkové komentáře
Jádro: 4.9.0-3-amd64

Parametry překladu:
gcc -Wall -o jentest jentest.c +knihovny, které ale nevolám

Celý zdroják nemůžu postnout :-( Něco s tím zkusím udělat, ale bojím se, že když ten kód budu upravovat, odmazávat proměnné, že se změna začne dít jinde v paměti a už to nepůjde ukázat.

Jenda

Re:C-čko a zápis mimo proměnnou
« Odpověď #3 kdy: 31. 10. 2017, 18:02:39 »
Zkompilovat s address sanitizerem!!!

Jinak to vypadá, jako kdyby na konci vstupniSoubor nebyl znak \0.

[queue]Něco s tím zkusím udělat, ale bojím se, že když ten kód budu upravovat, odmazávat proměnné, že se změna začne dít jinde v paměti a už to nepůjde ukázat.[/queue]

A během toho asi zjistíš, kde je chyba.

calixaren

Re:C-čko a zápis mimo proměnnou
« Odpověď #4 kdy: 31. 10. 2017, 18:15:00 »
Co zkusit char *vstupnisoubor="file.txt"; ?


MaLaMuT

Re:C-čko a zápis mimo proměnnou
« Odpověď #5 kdy: 31. 10. 2017, 18:20:14 »
Jenda, ač je obvykle totáč mimo, mi vyřešil problém :P

Nakoplo mě sprosté -fsanitize=address

Ona nepřetékalo to pole do té proměnné - na což jsem se soustředil, ale proměnná vstupniSoubor[] do pole :P

Jendo, VELMI TI DĚKUJI!
Já na to koukal jako vrána, zkoušel psí kusy a přitom taková blbost ::)

hu

Re:C-čko a zápis mimo proměnnou
« Odpověď #6 kdy: 31. 10. 2017, 18:22:05 »
Zkus si vyprintit adresy těch promennnejch.

hu

Re:C-čko a zápis mimo proměnnou
« Odpověď #7 kdy: 31. 10. 2017, 18:25:57 »
Hele já to nechápu. Jak se ti podařilo, aby překladač vyrobil ty proměnný aliasnuty? Nehledě na to, že literály bývají v jiném segmentu a ne na stacku.

Jerry

Re:C-čko a zápis mimo proměnnou
« Odpověď #8 kdy: 31. 10. 2017, 18:28:25 »
napadlo tě někdy použít MS Visual Studio ? nebo Eclipse ?

MaLaMuT

Re:C-čko a zápis mimo proměnnou
« Odpověď #9 kdy: 31. 10. 2017, 18:37:27 »
Nj, chybu jsem udělal, když jsem si tady vyrobil moc malé pole: char vstupniSoubor[] ="in.tx";
Jeho poslední prvek zjevně přesahoval do prvního prvku toho druhého pole.
Nevšiml jsem si toho...  ::)

Nebýt Jendy, tak na to koukám jako vrána i teď...

napadlo tě někdy použít MS Visual Studio ? nebo Eclipse ?
Dělám v C# a Visual Studio znám celkem dobře, ale ne pro čistý Cčko.

Moc jsem se soustředil na to, že je problém v tom poli  ::)

@Jenda ještě jednou dík!

Kiwi

Re:C-čko a zápis mimo proměnnou
« Odpověď #10 kdy: 31. 10. 2017, 19:53:46 »
Nj, chybu jsem udělal, když jsem si tady vyrobil moc malé pole: char vstupniSoubor[] ="in.tx";
Jeho poslední prvek zjevně přesahoval do prvního prvku toho druhého pole.
Nevšiml jsem si toho...  ::)

Nebýt Jendy, tak na to koukám jako vrána i teď...

Co to je za blbost? :D Pokud tvrdíš, že vstupniSoubor se nebude měnit, tak ti překladač vyrobí pole přesně tak velké, aby se do něho daný řetězcový literál vešel, včetně ukončovací nuly, a žádnou jinou proměnnou nealokuje tak, že by s tímto polem mohla při korektním zacházení kolidovat. Pokud bys to pole deklaroval s modifikátorem const, tak by nejspíš ta proměnná ukazovala někam do oblasti rodata, tedy úplně jinam než se nachází to druhé pole, jak už tu správně poznamenal předřečník.
Neboli chyba tam někde evidentně je, ale rozhodně nemůže být v tom, co píšeš, protože to pole pro tebe vyrábí překladač a ten se při určování jeho velikosti, narozdíl od lidí, neplete.

neruda

Re:C-čko a zápis mimo proměnnou
« Odpověď #11 kdy: 31. 10. 2017, 20:02:29 »
nechybi ti tam jenom v tom "retezci" na konci \0 ?

MaLaMuT

Re:C-čko a zápis mimo proměnnou
« Odpověď #12 kdy: 31. 10. 2017, 20:26:32 »
Co to je za blbost? :D Pokud tvrdíš, že vstupniSoubor se nebude měnit

No já ten řetězec modifikoval (strcp), tím vznikl přesah a proto se měnil při změnách pole...

Díky Jendově radě jsem si toho všiml hned.

tisnik

Re:C-čko a zápis mimo proměnnou
« Odpověď #13 kdy: 31. 10. 2017, 20:28:38 »
nechybi ti tam jenom v tom "retezci" na konci \0 ?

urcite ne, sizeof(vstupniSoubor) mu vrati 6, tam se nula pridava automaticky, navic je to v jinem segmentu

taky by me zajimaly adresy tech promennych, muzes je pls. vypsat pres %p? Trosku to vypada, jako by neco z toho byla lokalni promenna mimo svou platnost :)

hu

Re:C-čko a zápis mimo proměnnou
« Odpověď #14 kdy: 31. 10. 2017, 20:30:02 »
Tak abychom si to ujasnili. Ve všech případech uvažuji lokální deklarace.
Kód: [Vybrat]
char *s = "bla";
V .data nebo .rodata se alokují 4B s nulou terminovaným literárem. Proměnná s je pointer na stacku a je inicializována adresou toho literálu z příslušné sekce. I přes chybějící const je modifikace takového řetězce undefined a záleží na kompilatoru, zda taková operace skončí pádem aplikace.

Kód: [Vybrat]
char s[] = "bla";
Na stacku se alokuje 4prvkové pole, které je inicializováno příslušnými hodnotami (dost možná též z .rodata segmentu, dle implementace). Je v pořádku takový string měnit.

Ale nic z toho nevysvětluje ten alias; leda by OP deklaroval pole s explicitní délkou kratší, než je délka inicializačního řetězce s nulou.