Jeg leste en tråd med tittelen «strlen vs sizeof» på CodeGuru , og et av svarene sier at «det er uansett [sic] dårlig praksis å initialisere [sic] en char matrise med en streng bokstavelig. «
Er dette sant, eller er det bare hans (om enn et» elitemedlem «) mening?
Her er det originale spørsmålet:
#include <stdio.h> #include<string.h> main() { char string[] = "october"; strcpy(string, "september"); printf("the size of %s is %d and the length is %d\n\n", string, sizeof(string), strlen(string)); return 0; }
høyre. størrelsen skal være lengden pluss 1 ja?
dette er utdata
the size of september is 8 and the length is 9størrelse bør helt sikkert være 10. det er som det beregner størrelsen på strengen før den endres av strcpy, men lengden etter.
Er det noe galt med syntaksen min eller hva?
Her er svaret :
Det er uansett dårlig praksis å initialisere et tegnrute med en streng bokstavelig. Så gjør alltid ett av følgende:
const char string1[] = "october"; char string2[20]; strcpy(string2, "september");
Kommentarer
- Legg merke til » const » på første linje. Kan det være at forfatteren antok c ++ i stedet for c? I c ++ er det » dårlig praksis «, fordi en bokstav skal være const og enhver nylig c ++ kompilator vil gi en advarsel (eller feil) om å tilordne en const literal til en non-const array.
- @Andr é C ++ definerer strengliteraler som const arrays, fordi det er den eneste sikre måten å håndtere med dem. At C ikke ‘ t er problemet, så du har en sosial regel som håndhever den trygge tingen
- @Caleth. Jeg vet, jeg prøvde mer å argumentere for at forfatteren av svaret nærmet seg » dårlig praksis » fra et c ++ perspektiv.
- @Andr é det er ikke ‘ en dårlig praksis i C ++, fordi det ikke er ‘ å øve , det ‘ er en rett opp feil. Det skal være en typefeil i C, men det er ikke ‘ t, så du må ha en stilguideregel som forteller deg » Det ‘ er forbudt »
Svar
Det er uansett dårlig praksis å initialisere et tegn med en streng bokstavelig.
Forfatteren av den kommentaren rettferdiggjør det egentlig ikke, og jeg synes utsagnet er forvirrende.
I C (og du har merket dette som C), at » s ganske mye den eneste måten å initialisere en matrise med char med en strengverdi (initialisering er forskjellig fra tildeling). Du kan skrive enten
char string[] = "october";
eller
char string[8] = "october";
eller
char string[MAX_MONTH_LENGTH] = "october";
I det første tilfellet blir størrelsen på matrisen hentet fra størrelsen på initialisereren. Strengbokstaver lagres som matriser med char med en avslutning på 0 byte, så størrelsen på matrisen er 8 («o», «c», «t», «o», «b», «e», «r», 0). I de to andre tilfellene er størrelsen på matrisen spesifisert som en del av erklæringen (8 og MAX_MONTH_LENGTH, uansett hva som skjer).
Det du ikke kan gjøre er å skrive noe sånt som
char string[]; string = "october";
eller
char string[8]; string = "october";
etc. I det første tilfellet er erklæringen om string ufullstendig fordi ingen matrisestørrelse er spesifisert og det ikke er noen initialisering å ta størrelsen fra. I begge tilfeller vil = ikke fungere fordi a) et arrayuttrykk som string kanskje ikke er målet for en oppgave og b) = -operatøren er ikke definert for å kopiere innholdet i en matrise til en annen uansett.
På samme måte kan du ikke skrive
char string[] = foo;
der foo er et annet utvalg av char. Denne formen for initialisering vil bare fungere med strengtekster.
EDIT
Jeg bør endre dette for å si at du også kan initialisere matriser for å holde en streng med en initialiserende matrise-stil, som
char string[] = {"o", "c", "t", "o", "b", "e", "r", 0};
eller
char string[] = {111, 99, 116, 111, 98, 101, 114, 0}; // assumes ASCII
men det er lettere for øynene å bruke strenglitteratur.
REDIGER 2
For å tilordne innholdet i en matrise utenfor en erklæring, må du bruke strcpy/strncpy (for 0-terminerte strenger) eller memcpy (for alle andre typer matriser):
if (sizeof string > strlen("october")) strcpy(string, "october");
eller
strncpy(string, "october", sizeof string); // only copies as many characters as will // fit in the target buffer; 0 terminator // may not be copied, but the buffer is // uselessly completely zeroed if the // string is shorter!
Kommentarer
Svar
Det eneste problemet jeg husker er å tilordne streng bokstavelig til char *:
char var1[] = "september"; var1[0] = "S"; // Ok - 10 element char array allocated on stack char const *var2 = "september"; var2[0] = "S"; // Compile time error - pointer to constant string char *var3 = "september"; var3[0] = "S"; // Modifying some memory - which may result in modifying... something or crash
Ta for eksempel dette programmet:
#include <stdio.h> int main() { char *var1 = "september"; char *var2 = "september"; var1[0] = "S"; printf("%s\n", var2); }
Dette på plattformen min (Linux) krasjer når den prøver å skrive til en side merket som skrivebeskyttet. På andre plattformer kan det skrives ut «september» osv.
Når det er sagt – initialisering med bokstavelig ordning gjør det spesifikke reservasjonsbeløpet, så dette vil ikke fungere:
char buf[] = "May"; strncpy(buf, "September", sizeof(buf)); // Result "Sep"
Men dette vil
char buf[32] = "May"; strncpy(buf, "September", sizeof(buf));
Som siste bemerkning – jeg vil ikke bruke strcpy i det hele tatt:
char buf[8]; strcpy(buf, "very long string very long string"); // Oops. We overwrite some random memory
Mens noen kompilatorer kan endre det til trygt, er strncpy mye tryggere:
char buf[1024]; strncpy(buf, something_else, sizeof(buf)); // Copies at most sizeof(buf) chars so there is no possibility of buffer overrun. Please note that sizeof(buf) works for arrays but NOT pointers. buf[sizeof(buf) - 1] = "\0";
Kommentarer
- Det er ‘ fortsatt risiko for bufferoverskridelse på det
strncpyfordi det ikke ‘ t null avslutter den kopierte strengen når lengden påsomething_elseer større ennsizeof(buf). Jeg setter vanligvis den siste charbuf[sizeof(buf)-1] = 0for å beskytte mot det, eller hvisbufer nullinitialisert, bruksizeof(buf) - 1som kopilengde. - Bruk
strlcpyellerstrcpy_seller til og medsnprintfhvis du må. - Fast. Dessverre er det ingen enkel bærbar måte å gjøre dette med mindre du har lyst til å jobbe med de nyeste kompilatorene (
strlcpyogsnprintfer ikke direkte tilgjengelige på MSVC er i det minste ordrer ogstrcpy_sikke på * nix). - @MaciejPiechotka: Vel, takk gud Unix avviste det microsoft-sponsede vedlegget k.
Svar
Primært fordi du ikke har størrelsen på char[] i en variabel / konstruksjon som du enkelt kan bruke i programmet.
Kodeprøven fra lenken:
char string[] = "october"; strcpy(string, "september");
string er tildelt på stakken som 7 eller 8 tegn. Jeg kan ikke huske om den ikke ble avsluttet på denne måten eller ikke – tråden du koblet til uttalte at den er .
Kopiering av «september» over den strengen er en åpenbar minneoverskridelse.
En annen utfordring oppstår hvis du overfører string til en annen funksjon.slik at den andre funksjonen kan skrive inn i matrisen. Du må fortelle den andre funksjonen hvor lang matrisen er så den skaper ikke et overskridelse. Du kan sende string sammen med resultatet av strlen() men tråden forklarer hvordan dette kan sprenge hvis string ikke er null-terminert.
Du har det bedre tildele en streng med en fast størrelse (fortrinnsvis definert som en konstant) og deretter overføre matrisen og fast størrelse til den andre funksjonen. @John Bodes kommentar (er) er riktige, og det er måter å redusere disse risikoene på. De krever også mer innsats fra din side for å bruke dem.
Etter min erfaring er verdien jeg initialiserte char[] to er vanligvis for lite til de andre verdiene jeg trenger å plassere der inne. Ved å bruke en definert konstant kan du unngå problemet.
sizeof string vil gi deg størrelsen på bufferen (8 byte); bruk resultatet av det uttrykket i stedet for strlen når du er bekymret for minne.
På samme måte kan du foreta en sjekk før samtalen til strcpy for å se om målbufferen din er stor nok for kildestrengen: if (sizeof target > strlen(src)) { strcpy (target, src); }.
Ja, hvis du må overføre matrisen til en funksjon, vil du «ll må også passere den fysiske størrelsen: foo (array, sizeof array / sizeof *array);. – John Bode
Kommentarer
-
sizeof stringvil gi deg størrelsen på bufferen (8 byte); bruk resultatet av det uttrykket i stedet forstrlennår du ‘ er bekymret for minne. På samme måte kan du foreta en sjekk før samtalen tilstrcpyfor å se om målbufferen din er stor nok til kildestrengen:if (sizeof target > strlen(src)) { strcpy (target, src); }. Ja, hvis du må overføre matrisen til en funksjon, må du ‘ også passere den fysiske størrelsen:foo (array, sizeof array / sizeof *array);. - @ JohnBode – takk, og det er gode poeng. Jeg har innlemmet kommentaren din i svaret mitt.
- Mer presist, de fleste referanser til matrisenavnet
stringresulterer i en implisitt konvertering tilchar*, og peker på det første elementet i matrisen. Dette mister informasjon om grenseområdet. En funksjonsanrop er bare en av mange sammenhenger der dette skjer.char *ptr = string;er en annen. Selvstring[0]er et eksempel på dette;[]operatøren fungerer på pekere, ikke direkte på matriser. Foreslått lesing: Avsnitt 6 i comp.lang.c FAQ . - Endelig et svar som faktisk refererer til spørsmålet!
Svar
En ting som ingen av trådene tar opp er dette:
char whopping_great[8192] = "foo";
vs.
char whopping_great[8192]; memcpy(whopping_great, "foo", sizeof("foo"));
Førstnevnte vil gjøre noe sånt som:
memcpy(whopping_great, "foo", sizeof("foo")); memset(&whopping_great[sizeof("foo")], 0, sizeof(whopping_great)-sizeof("foo"));
Sistnevnte gjør bare memcpy. C-standarden insisterer på at hvis en del av en matrise initialiseres, er det hele. Så i dette tilfellet er det bedre å gjøre det selv. Jeg tror det kan ha vært det treuss fikk.
Sikkert
char whopping_big[8192]; whopping_big[0] = 0;
er bedre enn enten:
char whopping_big[8192] = {0};
eller
char whopping_big[8192] = "";
ps For bonuspoeng, kan du gjøre:
memcpy(whopping_great, "foo", (1/(sizeof("foo") <= sizeof(whopping_great)))*sizeof("foo"));
for å kaste en kompileringstid divisjon med null feil hvis du skal overflyte matrisen.
Svar
Jeg tror ideen om «dårlig praksis» kommer av at dette skjemaet:
char string[] = "october is a nice month";
gjør implisitt en strcpy fra kildemaskinkoden til stabelen.
Det er mer effektivt å bare håndtere en lenke til den strengen. Som med:
char *string = "october is a nice month";
eller direkte:
strcpy(output, "october is a nice month");
(men selvfølgelig i de fleste kode spiller det sannsynligvis ingen rolle
Kommentarer
- Ville ikke ‘ t bare lage en kopi hvis du prøver å endre det? Jeg tror kompilatoren ville være smartere enn det
- Hva med tilfeller som
char time_buf[] = "00:00";der du ‘ kommer til å endre en buffer? Enchar *initialisert til en streng bokstavelig er satt til adressen til den første byten, så prøver å endre den resulterer i udefinert oppførsel fordi metoden for strenge bokstavelig ‘ s lagring er ukjent (implementeringsdefinert), mens endring av byte til enchar[]er helt lovlig fordi initialisering kopierer bytene til en skrivbar plass som er tildelt på bunken. Å si at den ‘ s » mindre effektiv eller » dårlig praksis » uten å utdype nyansene tilchar* vs char[]er misvisende.
Svar
Det er aldri veldig lang tid, men du bør unngå initialisering char [] til streng, fordi «streng» er const char *, og du tilordner den til char *. Så hvis du sender denne røyken [] til metoden som endrer data, kan du ha interessant oppførsel.
Som anbefalt sa jeg blandet litt røye [] med røye *, det er ikke bra da de skiller seg litt ut.
Det er ikke noe galt med å tilordne data til char array, men da intensjonen med å bruke denne arrayen er å bruke den som «string» (char *), er det lett å glemme at du ikke bør endre dette array.
Kommentarer
- Feil. Initialiseringen kopierer innholdet av strengen bokstavelig inn i arrayet. Matrixobjektet er ikke ‘ t
constmed mindre du definerer det slik.(Og strenglitteratur i C er ikkeconst, selv om ethvert forsøk på å modifisere en strenglitteratur har udefinert oppførsel.)char *s = "literal";har slags oppførsel du ‘ snakker om; det ‘ er bedre skrevet somconst char *s = "literal"; - » Og generelt er » asdf » en konstant, så den skal erklæres som konst. » – Den samme resonnementet vil kreve en
constpåint n = 42;, fordi42er en konstant. - Det spiller ingen rolle ‘ hvilken maskin du ‘ er på. Språkstandarden garanterer at
ckan endres. Den ‘ er nøyaktig like sterk garanti som den som1 + 1vurderer til2. Hvis programmet jeg lenket til ovenfor gjør noe annet enn å skrive utEFGH, indikerer det en ikke-samsvarende C-implementering. - @Dainus: MSVC-kompilatoren har en optimalisering kalt ‘ strengpooling ‘ som vil sette en enkelt kopi av identiske strenger i et skrivebeskyttet segment hvis det kan garantere at bruken av dem er skrivebeskyttet. Slå av optimaliseringen for å se ‘ normal ‘ oppførsel. FYI » Rediger og fortsett » krever at dette alternativet er på. Mer info her: msdn.microsoft.com/en-us/library/s0s0asdt.aspx
- Jeg tror Dainius antyder det i mange tilfeller er feilen at selve variabelen skal merkes
const char *constfor å forhindre modifisering av byte eller selve pekeren, men i mange tilfeller vil programmerere la en eller begge være mutable slik at noe kjøretidskode kan endre det som ser ut til å være en skrevet konstant (men ikke er konstant).
strncpyer sjelden riktig svarchar[8] str = "october";er dårlig praksis. Jeg måtte bokstavelig talt røye meg for å forsikre meg om at det ikke var ‘ t et overløp og det bryter under vedlikehold … f.eks. å rette en stavefeil frasepratetilseparatevil bryte hvis størrelsen ikke oppdateres.strlen()inkluderer ikke nulltegnet, og brukerMAX_MONTH_LENGTHfor å holde den maksimale størrelsen som trengs forchar string[]ser ofte feil ut. IMO,MAX_MONTH_SIZEville vært bedre her.