Jeg læste en tråd med titlen “strlen vs sizeof” på CodeGuru og et af svarene siger, at “det er alligevel [sic] dårlig praksis at initialisere [sic] et char
array med en streng bogstavelig. “
Er dette sandt, eller er det bare hans (omend et” elitemedlem “) opfattelse?
Her er det originale spørgsmål:
#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øjre. størrelsen skal være længden plus 1 ja?
dette er output
the size of september is 8 and the length is 9
størrelse skal helt sikkert være 10. det er ligesom det beregner størrelsen på streng, før den ændres med strcpy, men længden efter.
Er der noget galt med min syntaks eller hvad?
Her er svaret :
Det er under alle omstændigheder dårlig praksis at initialisere et char-array med en streng-bogstav. Så gør altid et af følgende:
const char string1[] = "october"; char string2[20]; strcpy(string2, "september");
Kommentarer
- Bemærk ” const ” på første linje. Kan det være, at forfatteren antog c ++ i stedet for c? I c ++ er det ” dårlig praksis “, fordi en bogstavelig skal være const og enhver nyere c ++ – kompilator vil give en advarsel (eller fejl) om at tildele en const-bogstavelig til en ikke-const-array.
- @Andr é C ++ definerer strenglitteraler som const-arrays, fordi det er den eneste sikre måde at håndtere på med dem. At C ikke ‘ t er problemet, så du har en social regel, der håndhæver den sikre ting
- @Caleth. Jeg ved, jeg prøvede mere at argumentere for, at forfatteren af svaret nærmede sig ” dårlig praksis ” fra et c ++ perspektiv.
- @Andr é det er ikke ‘ en dårlig praksis i C ++, fordi det ikke er ‘ til praksis , det ‘ er en lige fejl i typen. Det skal være en typefejl i C, men det er ikke ‘ t, så du bliver nødt til at have en stilguide-regel, der fortæller dig ” Det ‘ er forbudt ”
Svar
Det er under alle omstændigheder dårlig praksis at initialisere et char-array med en streng-bogstav.
Forfatteren af denne kommentar retfærdiggør det aldrig rigtig, og jeg finder udsagnet forvirrende.
I C (og du har tagget dette som C), at ” s stort set den eneste måde at initialisere en matrix på char
med en strengværdi (initialisering er forskellig fra tildelingen). Du kan skrive enten
char string[] = "october";
eller
char string[8] = "october";
eller
char string[MAX_MONTH_LENGTH] = "october";
I det første tilfælde tages størrelsen på arrayet fra initialiseringsstørrelsen. Strenglitteratur gemmes som arrays af char
med en slutter 0 byte, så størrelsen af arrayet er 8 (“o”, “c”, “t”, “o”, “b”, “e”, “r”, 0). I de to andet tilfælde specificeres arrayets størrelse som en del af erklæringen (8 og MAX_MONTH_LENGTH
, uanset hvad der sker).
Hvad du ikke kan gøre er at skrive noget som
char string[]; string = "october";
eller
char string[8]; string = "october";
osv. I det første tilfælde er erklæringen om string
ufuldstændig fordi der ikke er angivet nogen matrixstørrelse, og der ikke er nogen initialisering til at tage størrelsen fra. tilfælde fungerer =
ikke, fordi a) et array-udtryk som string
muligvis ikke er målet for en opgave og b) =
-operatøren er ikke defineret til at kopiere indholdet af en matrix til en anden alligevel.
Med det samme token kan du ikke skrive
char string[] = foo;
hvor foo
er en anden matrix af char
. Denne form for initialisering fungerer kun med strengbogstaver.
REDIGER
Jeg bør ændre dette for at sige, at du også kan initialisere arrays for at holde en streng med en initialisering af en array-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 øjnene at bruge strenglitteratur.
REDIGER 2
For at tildel indholdet af en matrix uden for en erklæring, skal du bruge enten strcpy/strncpy
(til 0-afsluttede strenge) eller memcpy
(for enhver anden type array):
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 problem, jeg husker, er at tildele streng 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
Tag f.eks. Dette program:
#include <stdio.h> int main() { char *var1 = "september"; char *var2 = "september"; var1[0] = "S"; printf("%s\n", var2); }
Dette på min platform (Linux) går ned, da det forsøger at skrive til en side, der er markeret som skrivebeskyttet. På andre platforme udskriver den muligvis “september” osv.
Når det er sagt – initialisering med bogstavelig talt gør det specifikke beløb for reservation, så dette fungerer ikke:
char buf[] = "May"; strncpy(buf, "September", sizeof(buf)); // Result "Sep"
Men dette vil
char buf[32] = "May"; strncpy(buf, "September", sizeof(buf));
Som sidste bemærkning – jeg ville ikke bruge strcpy
overhovedet:
char buf[8]; strcpy(buf, "very long string very long string"); // Oops. We overwrite some random memory
Mens nogle compilere kan ændre det til sikkert opkald strncpy
er meget mere sikkert:
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
- Der er ‘ stadig risiko for bufferoverskridelse på
strncpy
, fordi den ikke ‘ t nulstiller den kopierede streng, når længden påsomething_else
er større endsizeof(buf)
. Jeg indstiller normalt det sidste tegnbuf[sizeof(buf)-1] = 0
for at beskytte mod det, eller hvisbuf
er initialiseret nul, skal du brugesizeof(buf) - 1
som kopilængde. - Brug
strlcpy
ellerstrcpy_s
eller enddasnprintf
hvis du skal. - Rettet. Desværre er der ingen nem bærbar måde at gøre dette på, medmindre du har en luksus at arbejde med de nyeste kompilatorer (
strlcpy
ogsnprintf
er ikke direkte tilgængelige på MSVC er i det mindste ordrer ogstrcpy_s
ikke på * nix). - @MaciejPiechotka: Nå, gudskelov Unix afviste det microsoft-sponsorerede bilag k.
Svar
Primært fordi du ikke har størrelsen på char[]
i en variabel / konstruktion, som du nemt kan bruge i programmet.
Kodeprøven fra linket:
char string[] = "october"; strcpy(string, "september");
string
er tildelt på stakken som 7 eller 8 tegn. Jeg kan ikke huske, om den ikke er afsluttet på denne måde eller ej – den tråd, du linkede til, sagde, at den er .
Kopiering af “september” over den streng er en åbenlyst hukommelsesoverskridelse.
En anden udfordring opstår, hvis du sender string
til en anden funktionså den anden funktion kan skrive i arrayet. Du er nødt til at fortælle den anden funktion, hvor længe arrayet er så det skaber ikke en overskridelse. Du kan videregive string
sammen med resultatet af strlen()
men tråden forklarer, hvordan dette kan sprænge, hvis string
ikke nulstilles.
Du har det bedre tildele en streng med en fast størrelse (fortrinsvis defineret som en konstant) og derefter overføre arrayet og den faste størrelse til den anden funktion. @John Bodes kommentar (er) er korrekte, og der er måder at afbøde disse risici på. De kræver også en større indsats fra din side for at bruge dem.
Efter min erfaring er den værdi, jeg initialiserede char[]
til er normalt for lille til de andre værdier, jeg skal placere derinde. Brug af en defineret konstant hjælper med at undgå dette problem.
sizeof string
giver dig bufferstørrelsen (8 byte); brug resultatet af dette udtryk i stedet for strlen
når du er “bekymret for hukommelsen.
På samme måde kan du foretage en kontrol inden opkaldet til strcpy
for at se, om din målbuffer er stor nok til kildestrengen: if (sizeof target > strlen(src)) { strcpy (target, src); }
.
Ja, hvis du skal videregive arrayet til en funktion, skal du “ll skal også passere sin fysiske størrelse: foo (array, sizeof array / sizeof *array);
. – John Bode
Kommentarer
-
sizeof string
giver dig størrelsen på bufferen (8 byte); Brug resultatet af dette udtryk i stedet forstrlen
når du ‘ er bekymret for hukommelsen. På samme måde kan du foretage en check før opkaldet tilstrcpy
for at se, om din målbuffer er stor nok til kildestrengen:if (sizeof target > strlen(src)) { strcpy (target, src); }
. Ja, hvis du skal videregive matrixen til en funktion, skal du ‘ også have den fysiske størrelse:foo (array, sizeof array / sizeof *array);
. - @JohnBode – tak, og det er gode punkter. Jeg har indarbejdet din kommentar i mit svar.
- Mere præcist resulterer de fleste henvisninger til arraynavnet
string
til en implicit konvertering tilchar*
, der peger på det første element i arrayet. Dette mister informationen om arraygrænserne. Et funktionsopkald er blot en af de mange sammenhænge, hvor dette sker.char *ptr = string;
er en anden. Selvstring[0]
er et eksempel på dette;[]
operatøren arbejder på markører, ikke direkte på arrays. Foreslået læsning: Afsnit 6 i comp.lang.c FAQ . - Endelig et svar, der faktisk henviser til spørgsmålet!
Svar
En ting, som ingen af trådene bringer op, er dette:
char whopping_great[8192] = "foo";
vs.
char whopping_great[8192]; memcpy(whopping_great, "foo", sizeof("foo"));
Førstnævnte gør noget som:
memcpy(whopping_great, "foo", sizeof("foo")); memset(&whopping_great[sizeof("foo")], 0, sizeof(whopping_great)-sizeof("foo"));
Sidstnævnte gør kun memcpy. C-standarden insisterer på, at hvis en del af et array initialiseres, er det hele. Så i dette tilfælde er det bedre at gøre det selv. Jeg tror, det kan have været, hvad treuss fik til.
Sikker
char whopping_big[8192]; whopping_big[0] = 0;
er bedre end enten:
char whopping_big[8192] = {0};
eller
char whopping_big[8192] = "";
ps For bonuspoint kan du gøre:
memcpy(whopping_great, "foo", (1/(sizeof("foo") <= sizeof(whopping_great)))*sizeof("foo"));
for at kaste en kompileringstid divideret med nul-fejl, hvis du er ved at løbe over arrayet.
Svar
Jeg tror, at ideen om “dårlig praksis” stammer fra det faktum, at denne form:
char string[] = "october is a nice month";
gør implicit en strcpy fra kildemaskinkoden til stakken.
Det er mere effektivt kun at håndtere et link til den streng. 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 betyder det sandsynligvis ikke noget
Kommentarer
- Ville ‘ t det kun lave en kopi hvis du forsøger at ændre det? Jeg ville tro, at compileren ville være klogere end det
- Hvad med sager som
char time_buf[] = "00:00";
hvor du ‘ Vil du ændre en buffer? Enchar *
initialiseret til en streng-bogstav er indstillet til adressen på den første byte, så forsøg på at ændre den resulterer i udefineret adfærd, fordi metoden til strengen bogstavelig ‘ s lagring er ukendt (implementeringsdefineret), mens ændring af bytes i enchar[]
er helt lovlig, fordi initialisering kopierer byte til et skrivbart område, der er allokeret på stakken. At sige, at det ‘ s ” mindre effektivt eller ” dårlig praksis ” uden at uddybe nuancerne ichar* vs char[]
er vildledende.
Svar
Aldrig er det virkelig lang tid, men du bør undgå initialisering char [] til streng, fordi “streng” er const char *, og du tildeler den til char *. Så hvis du sender denne char [] til metode, der ændrer data, kan du have en interessant opførsel.
Som anbefalet sagde jeg, at jeg blandede lidt char [] med char *, det er ikke godt, da de adskiller sig lidt.
Der er ikke noget galt med at tildele data til char array, men da hensigten med at bruge dette array er at bruge det som “string” (char *), er det let at glemme at du ikke skal ændre dette array.
Kommentarer
- Forkert. Initialiseringen kopierer indholdet af strengen bogstaveligt i arrayet. Matrixobjektet er ikke ‘ t
const
medmindre du definerer det på den måde.(Og strenglitteraler i C er ikkeconst
, selvom ethvert forsøg på at ændre en strenglitteratur har udefineret adfærd.)char *s = "literal";
har dog slags opførsel, du ‘ taler om; det ‘ er bedre skrevet somconst char *s = "literal";
- ” Og generelt er ” asdf ” en konstant, så det skal erklæres som konst. ” – Den samme ræsonnement ville kræve en
const
påint n = 42;
, fordi42
er en konstant. - Det betyder ikke ‘ uanset hvilken maskine du ‘ er på. Sprogstandarden garanterer, at
c
kan ændres. Det ‘ er nøjagtig lige så stærk garanti som den, som1 + 1
vurderer til2
. Hvis det program, jeg linkede til ovenfor , gør andet end at udskriveEFGH
, indikerer det en ikke-overensstemmelse C-implementering. - @Dainus: MSVC-compileren har en optimering kaldet ‘ strengpooling ‘ som vil sætte en enkelt kopi af identiske strenge i et skrivebeskyttet segment, hvis det kan garantere, at anvendelsen af dem er skrivebeskyttet. Sluk for optimeringen for at se ‘ normal ‘ adfærd. FYI ” Rediger og fortsæt ” kræver, at denne mulighed er aktiveret. Mere info her: msdn.microsoft.com/en-us/library/s0s0asdt.aspx
- Jeg tror, Dainius antyder det i mange tilfælde er fejlen, at variablen i sig selv skal markeres
const char *const
for at forhindre ændring af bytes eller selve markøren, men i mange tilfælde vil programmører lade en eller begge være mutable, så nogle runtime-koder kan rediger hvad der synes at være en indtastet konstant (men ikke er konstant).
strncpy
er sjældent det rigtige svarchar[8] str = "october";
er dårlig praksis. Jeg var nødt til bogstaveligt talt at tælle mig selv for at sikre, at det ikke var ‘ t et overløb, og det bryder under vedligeholdelse … f.eks. at rette en stavefejl fraseprate
tilseparate
bryder, hvis størrelsen ikke opdateres.strlen()
inkluderer ikke nulstegnet, der brugerMAX_MONTH_LENGTH
for at holde den maksimale størrelse, der er nødvendig forchar string[]
ser ofte forkert ud . IMO,MAX_MONTH_SIZE
ville være bedre her.