Initialiserer en røye [] med streng streng bokstavelig praksis?

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 9

stø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

  • strncpy er sjelden riktig svar
  • @KeithThompson: ikke uenig, bare la det til for fullstendighet ‘ skyld.
  • Vær oppmerksom på at char[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 fra seprate til separate vil bryte hvis størrelsen ikke oppdateres.
  • Jeg er enig med djechlin, det er dårlig praksis av de oppgitte årsakene. JohnBode ‘ s svar gir ‘ ikke kommentarer til » dårlig praksis » aspekt (som er hoveddelen av spørsmålet !!), det forklarer bare hva du kan eller ikke kan gjøre for å initialisere matrisen.
  • Mindre: Som ‘ lengde » verdi returnert fra strlen() inkluderer ikke nulltegnet, og bruker MAX_MONTH_LENGTH for å holde den maksimale størrelsen som trengs for char string[] ser ofte feil ut. IMO, MAX_MONTH_SIZE ville vært bedre her.

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 strncpy fordi det ikke ‘ t null avslutter den kopierte strengen når lengden på something_else er større enn sizeof(buf). Jeg setter vanligvis den siste char buf[sizeof(buf)-1] = 0 for å beskytte mot det, eller hvis buf er nullinitialisert, bruk sizeof(buf) - 1 som kopilengde.
  • Bruk strlcpy eller strcpy_s eller til og med snprintf hvis 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 (strlcpy og snprintf er ikke direkte tilgjengelige på MSVC er i det minste ordrer og strcpy_s ikke 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 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 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 string resulterer i en implisitt konvertering til char*, 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. Selv string[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? En char * 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 en char[] 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 til char* 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 const med mindre du definerer det slik.(Og strenglitteratur i C er ikke const, 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 som const char *s = "literal";
  • » Og generelt er » asdf » en konstant, så den skal erklæres som konst. » – Den samme resonnementet vil kreve en constint n = 42;, fordi 42 er en konstant.
  • Det spiller ingen rolle ‘ hvilken maskin du ‘ er på. Språkstandarden garanterer at c kan endres. Den ‘ er nøyaktig like sterk garanti som den som 1 + 1 vurderer til 2. Hvis programmet jeg lenket til ovenfor gjør noe annet enn å skrive ut EFGH, 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 *const for å 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).

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *