Initialiserer en char [] med streng streng bogstavelig praksis?

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

  • strncpy er sjældent det rigtige svar
  • @KeithThompson: ikke uenig, bare tilføjet det for fuldstændighed ‘ skyld.
  • Vær opmærksom på at char[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 fra seprate til separate bryder, hvis størrelsen ikke opdateres.
  • Jeg er enig med djechlin, det er dårlig praksis af de anførte grunde. JohnBode ‘ s svar kommenterer slet ikke ‘ til ” dårlig praksis ” aspekt (som er hoveddelen af spørgsmålet !!), det forklarer bare, hvad du kan eller ikke kan gøre for at initialisere arrayet.
  • Mindre: Som ‘ længde ” værdi returneret fra strlen() inkluderer ikke nulstegnet, der bruger MAX_MONTH_LENGTH for at holde den maksimale størrelse, der er nødvendig for char string[] ser ofte forkert ud . IMO, MAX_MONTH_SIZE ville være bedre her.

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 end sizeof(buf). Jeg indstiller normalt det sidste tegn buf[sizeof(buf)-1] = 0 for at beskytte mod det, eller hvis buf er initialiseret nul, skal du bruge sizeof(buf) - 1 som kopilængde.
  • Brug strlcpy eller strcpy_s eller endda snprintf 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 og snprintf er ikke direkte tilgængelige på MSVC er i det mindste ordrer og strcpy_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 for strlen når du ‘ er bekymret for hukommelsen. På samme måde kan du foretage en check før 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 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 til char*, 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. Selv string[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? En char * 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 en char[] 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 i char* 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 ikke const, 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 som const char *s = "literal";
  • ” Og generelt er ” asdf ” en konstant, så det skal erklæres som konst. ” – Den samme ræsonnement ville kræve en constint n = 42;, fordi 42 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, som 1 + 1 vurderer til 2. Hvis det program, jeg linkede til ovenfor , gør andet end at udskrive EFGH, 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).

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *