struct KIRJOITUS { // structissa kirjotteluun liittyvää enemmänkin char VIRKE14[30]; }; KIRJOITUS T; // Kutsuva funktio NextText("%2YOU SEE",0); void NextText(char t[],char mode) { if (mode==0) { strcpy(T.VIRKE14,t); } if (mode==1) { strcat(T.VIRKE14,t); } }
Mistä seuraava varoitus johtuu?
Näitä samoja varoituksia saan 144 kappaletta.
D:\UHO\Indoor\look.h|181|warning: deprecated conversion from string constant to 'char*'|
C:ssä (ja C++:ssa) lainausmerkeissä kirjoitettu merkkijonovakio (engl. "string literal") on anonyymi taulukko tyyppiä const char[N]
missä N on merkkijonon pituus + 1 (merkkijonon päädyn osoittavaa nollatavua varten).
Sijoittamalla tämän merkkijonon osoitteen osoittimeen tyyppiä char*
menetät const
-määreen. Historiallisista syistä johtuen tämä ei ole virhe, mutta monet kääntäjät kuitenkin huomauttavat asiasta. Merkkijonovakiota ei missään tapauksessa saa muuttaa määrittelyn jälkeen - tämän yrittäminen johtaa ohjelman määrittelemättömään toimintaan (engl. "undefined behavior") - useissa tapauksissa ohjelman kaatumiseen (ja teoreettisissa tapauksissa vieläkin ikävämpiin lieveilmiöihin, kuten virhetoimintaan jossain muussa ohjelman osassa).
Korjaa virhe tässä tapauksessa kirjoittamalla funktion prototyyppi kuten
void NextText(const char t[], char mode)
Huomioithan, että funktiolle ei voi välittää taulukkoa. Yllä oleva vastaa täysin seuraavaa prototyyppiä:
void NextText(const char* t, char mode)
Kyseessä ei suinkaan ole teoreettinen ongelma, vaan odottamattomia vaikutuksia saa helpostikin. Seuraava testi on ajettu Linuxissa GCC 4.5.2:lla.
#include <stdio.h> #include <string.h> static void muuta(const char* str) { // Muunnos char*-tyyppiin ja laiton muokkaus. char* tmp = (char*) str; tmp[strlen(str)] = '!'; } int main() { // Muutetaan tekstiä ja tulostetaan sama teksti. muuta("ABCDEFG"); puts("ABCDEFG"); return 0; }
gcc b.c && ./a.out # Ohjelma kaatuu: Segmentation fault
gcc b.c -Wl,-N -static && ./a.out # Tulostaa: ABCDEFG!FATAL: kernel too old # Ei kuitenkaan kaadu, vaan pelottava teksti on sattumaa.
gcc b.c -Wl,-N -static -O && ./a.out # Tulostaa: ABCDEFG
Mielenkiintoista, että kun optimointi on käytössä, GCC poistaa koko muuta-funktion. (Jos funktio ei ole static eikä inline, tarvitaan -O3, jotta GCC ajaa riittävän tarkan analyysin; alemmilla asetuksilla saadaan keskimmäinen tulos.)
Metabolix kirjoitti:
Kyseessä ei suinkaan ole teoreettinen ongelma, vaan odottamattomia vaikutuksia saa helpostikin. Seuraava testi on ajettu Linuxissa GCC 4.5.2:lla.
Sanavalinta oli ehkä huonohko, mutta perustelen sitä sillä, että hain lauseella hieman salakavalampia ongelmia kuin "saman" merkkijonon muuttuminen myöhemmin ohjelmassa.
Eipä warningit hävinny mihinkään vaikka laitoin constin ja char *t?????
DumTom kirjoitti:
Eipä warningit hävinny mihinkään vaikka laitoin constin ja char *t?????
Varoitusten, jotka viittasivat tämän funktion tällä tavalla virheelliseen käyttöön, olisi pitänyt poistua. Koodissa on mahdollisesti muita samanlaisia tai erilaisia virheitä, mutta kristallipalloni ei suostu enää kertomaan että missä ja minkälaisia (ts. näkemättä koodia ja näkemättä virheilmoituksia on mahdotonta sanoa).
Huomioithan, että funktion argumenttilistaa pitää muuttaa sekä esittelyssä, että varsinaisessa määrittelyssä, sikäli kun molemmat koodissa esiintyvät. Esimerkiksi
void NextText(char t[], char mode); /* ... myöhemmin, muualla ... */ void NextText(char t[], char mode) { /* itse funktio */ }
Tulee muuttaa esim. seuraavasti:
void NextText(const char t[], char mode); /* ... myöhemmin, muualla ... */ void NextText(const char t[], char mode) { /* itse funktio */ }
eq kirjoitti:
Sanavalinta oli ehkä huonohko, mutta perustelen sitä sillä, että hain lauseella hieman salakavalampia ongelmia kuin "saman" merkkijonon muuttuminen myöhemmin ohjelmassa.
Ei ollut tarkoitukseni moittia hyvää selostustasi, vaan suuntasin konkreettisen esimerkin enemmänkin DumTomille. Tuo esittämäni tekstin muuttuminen on kieltämättä Linuxissa edelleen aika teennäinen juttu (vaatii juuri oikeat käännösasetukset), vaikka jossain muussa järjestelmässä (DOSissa ainakin, heh) ongelma onkin todellinen. Linuxissa tuskin pääsee kovin helposti tapahtumaan muuta kuin segfault, ellei ohjelmassa ole samaan aikaan jotain muutakin merkittävää bugia.
Sain warningit häviämään. Siellä oli useassa otteessa noita constin puutteita.
Aihe on jo aika vanha, joten et voi enää vastata siihen.