Initierar en char [] med en sträng bokstavlig dålig praxis?

Jag läste en tråd med titeln ”strlen vs sizeof” på CodeGuru och ett av svaren säger att ”det är ändå [sic] dålig praxis att initialisera [sic] en char array med en sträng bokstavlig. ”

Är detta sant, eller är det bara hans (om än en” elitmedlem ”) åsikt?


Här är den ursprungliga frågan:

#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; } 

rätt. storleken ska vara längden plus 1 ja?

detta är utgången

the size of september is 8 and the length is 9

storlek bör säkert vara 10. det är som att det beräknar storleken på strängen innan den ändras med strcpy men längden efter.

Är det något fel med min syntax eller vad?


Här är svaret :

Det är ändå dålig praxis att initialisera en char-array med en strängbokstav. Så gör alltid något av följande:

const char string1[] = "october"; char string2[20]; strcpy(string2, "september"); 

Kommentarer

  • Notera ” const ” på första raden. Kan det vara så att författaren antog c ++ istället för c? I c ++ är det ” dålig praxis ”, eftersom en bokstav bör vara const och alla nya c ++ kompilatorer kommer att ge en varning (eller fel) om att tilldela en const literal till en non-const array.
  • @Andr é C ++ definierar stränglitteraler som const arrays, eftersom det är det enda säkra sättet att hantera med dem. Att C inte ’ t är problemet, så du har en social regel som verkställer den säkra saken
  • @Caleth. Jag vet, jag försökte mer argumentera för att svarets författare närmade sig ” dålig praxis ” ur ett c ++ perspektiv.
  • @Andr é det är inte ’ en dålig praxis i C ++, eftersom det inte är ’ som övning , det ’ är ett rakt typfel. Det bör vara ett typfel i C, men det är inte ’ t, så du måste ha en stilguide som säger dig ” Det ’ är förbjudet ”

Svar

Det är ändå dålig praxis att initialisera en char-array med en sträng bokstavlig.

Författaren till den kommentaren motiverar det egentligen aldrig, och jag tycker att uttalandet är förbryllande.

I C (och du har taggat detta som C), att ” s ganska mycket det enda sättet att initialisera en matris med char med ett strängvärde (initialisering skiljer sig från tilldelning). Du kan skriva antingen

char string[] = "october"; 

eller

char string[8] = "october"; 

eller

char string[MAX_MONTH_LENGTH] = "october"; 

I det första fallet tas storleken på matrisen från storleken på initialiseraren. Strängbokstäver lagras som arrayer av char med en slutande 0 byte, så storleken på matrisen är 8 (”o”, ”c”, ”t”, ”o”, ”b”, ”e”, ”r”, 0). I de andra två fallen anges storleken på matrisen som en del av deklarationen (8 och MAX_MONTH_LENGTH, oavsett vad som råkar vara).

Vad du inte kan göra är att skriva något som

char string[]; string = "october"; 

eller

char string[8]; string = "october"; 

etc. I det första fallet är deklarationen av string ofullständig eftersom ingen matrisstorlek har angetts och det inte finns någon initialiserare att ta storleken från. I båda fall fungerar = inte eftersom a) ett arrayuttryck som string kanske inte är målet för en uppgift och b) = -operatören är inte definierad för att kopiera innehållet i en matris till en annan.

Med samma tecken kan du inte skriva

char string[] = foo; 

där foo är en annan array med char. Denna form av initialisering fungerar bara med strängbokstäver.

REDIGERA

Jag borde ändra detta för att säga att du också kan initialisera matriser för att hålla en sträng med en initialiserare i 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 är lättare för ögonen att använda stränglitteratur.

REDIGERA 2

För att tilldela innehåll i en matris utanför en deklaration, du måste använda antingen strcpy/strncpy (för 0-avslutade strängar) eller memcpy (för alla andra typer av 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 är sällan rätt svar
  • @KeithThompson: inte instämmande, bara lagt till det för fullständighet ’ skull.
  • Observera att char[8] str = "october"; är dålig praxis. Jag var tvungen att bokstavligen räkna mig själv för att se till att det inte var ’ t ett överflöde och att det bryts under underhåll … t.ex. korrigering av ett stavfel från seprate till separate kommer att brytas om storleken inte uppdateras.
  • Jag håller med djechlin, det är dålig praxis av de anförda skälen. JohnBode ’ s svar kommenterar inte alls ’ på ” dålig praxis ” aspekt (som är huvuddelen av frågan !!), det förklarar bara vad du kan eller inte kan göra för att initialisera matrisen.
  • Mindre: Som ’ längd ” värde som returneras från strlen() inkluderar inte nulltecken, med MAX_MONTH_LENGTH för att hålla den maximala storlek som behövs för char string[] ser ofta fel ut. IMO, MAX_MONTH_SIZE skulle vara bättre här.

Svar

Det enda problemet jag minns är att tilldela sträng bokstavlig till 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 till exempel detta program:

#include <stdio.h> int main() { char *var1 = "september"; char *var2 = "september"; var1[0] = "S"; printf("%s\n", var2); } 

Detta på min plattform (Linux) kraschar när det försöker skriva till en sida markerad som skrivskyddad. På andra plattformar kan det skriva ut ”september” osv.

Som sagt – initialisering med bokstavlig ordning gör det specifika beloppet för bokningen så det här fungerar inte:

char buf[] = "May"; strncpy(buf, "September", sizeof(buf)); // Result "Sep" 

Men detta kommer

char buf[32] = "May"; strncpy(buf, "September", sizeof(buf)); 

Som sista anmärkning – jag skulle inte använda strcpy alls:

char buf[8]; strcpy(buf, "very long string very long string"); // Oops. We overwrite some random memory 

Medan vissa kompilatorer kan ändra det till ett säkert samtal strncpy är mycket säkrare:

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 finns ’ fortfarande risk för buffertöverskridande på det strncpy eftersom det inte ’ t null avslutar den kopierade strängen när längden på something_else är större än sizeof(buf). Jag ställer vanligtvis in den sista tecknet buf[sizeof(buf)-1] = 0 för att skydda mot det, eller om buf är nollinitierad, använd sizeof(buf) - 1 som kopieringslängd.
  • Använd strlcpy eller strcpy_s eller till och med snprintf om du måste.
  • Fast. Tyvärr finns det inget enkelt bärbart sätt att göra detta om du inte har lyx att arbeta med de senaste kompilatorerna (strlcpy och snprintf är inte direkt tillgängliga på MSVC finns åtminstone order och strcpy_s inte på * nix).
  • @MaciejPiechotka: Tack och lov, Unix avvisade den microsoft-sponsrade bilagan k.

Svar

Främst eftersom du inte har storleken på char[] i en variabel / konstruktion som du enkelt kan använda i programmet.

Kodprovet från länken:

 char string[] = "october"; strcpy(string, "september"); 

string tilldelas på stacken som 7 eller 8 tecken lång. Jag kan inte komma ihåg om den inte har avslutats på det här sättet eller inte – den tråd du länkade till uppgav att den är .

Kopiering av ”september” över den strängen är en uppenbar minnesöverskridande.

En annan utmaning uppstår om du skickar string till en annan funktionså att den andra funktionen kan skriva in i matrisen. Du måste berätta för den andra funktionen hur länge matrisen är så den skapar inte ett överskridande. Du kan skicka string tillsammans med resultatet av strlen() men tråden förklarar hur detta kan sprängas om string inte avslutas.

Du har det bättre allokera en sträng med en fast storlek (företrädesvis definierad som en konstant) och skicka sedan matrisen och den fasta storleken till den andra funktionen. @John Bodes kommentar (er) är korrekta, och det finns sätt att mildra dessa risker. De kräver också mer ansträngning från din sida för att använda dem.

Enligt min erfarenhet initierade värdet div id = ”e010afea17”>

till är vanligtvis för liten för de andra värden jag behöver placera där. Att använda en definierad konstant hjälper till att undvika problemet.


sizeof string ger dig buffertens storlek (8 byte); använd resultatet av det uttrycket istället för strlen när du är orolig för minnet.
På samma sätt kan du göra en kontroll före samtalet till strcpy för att se om din målbuffert är tillräckligt stor för källsträngen: if (sizeof target > strlen(src)) { strcpy (target, src); }.
Ja, om du måste skicka arrayen till en funktion, kommer du ”ll måste klara dess fysiska storlek också: foo (array, sizeof array / sizeof *array);. – John Bode

Kommentarer

  • sizeof string ger dig storleken på bufferten (8 byte); använd resultatet av det uttrycket istället för strlen när du ’ oroar dig för minnet. På samma sätt kan du göra en kontroll före samtalet till strcpy för att se om din målbuffert är tillräckligt stor för källsträngen: if (sizeof target > strlen(src)) { strcpy (target, src); }. Ja, om du måste skicka matrisen till en funktion måste du ’ också skicka dess fysiska storlek: foo (array, sizeof array / sizeof *array);.
  • @JohnBode – tack, och det är bra poäng. Jag har införlivat din kommentar i mitt svar.
  • Mer exakt resulterar de flesta referenser till arraynamnet string till en implicit konvertering till char*, pekar på det första elementet i matrisen. Detta förlorar information om arraygränserna. Ett funktionsanrop är bara en av de många sammanhang där detta händer. char *ptr = string; är en annan. Även string[0] är ett exempel på detta; [] operatören fungerar på pekare, inte direkt på matriser. Föreslagen läsning: Avsnitt 6 i comp.lang.c FAQ .
  • Slutligen ett svar som faktiskt hänvisar till frågan!

Svar

En sak som ingen tråd ger upp är detta:

char whopping_great[8192] = "foo"; 

vs.

char whopping_great[8192]; memcpy(whopping_great, "foo", sizeof("foo")); 

Den förra kommer att göra något som:

memcpy(whopping_great, "foo", sizeof("foo")); memset(&whopping_great[sizeof("foo")], 0, sizeof(whopping_great)-sizeof("foo")); 

Det senare gör bara memcpy. C-standarden insisterar på att om någon del av en matris initialiseras är det allt. Så i det här fallet är det bättre att göra det själv. Jag tror att det kan ha varit vad treuss fick.

Visst

char whopping_big[8192]; whopping_big[0] = 0; 

är bättre än antingen:

char whopping_big[8192] = {0}; 

eller

char whopping_big[8192] = ""; 

ps För bonuspoäng kan du göra:

memcpy(whopping_great, "foo", (1/(sizeof("foo") <= sizeof(whopping_great)))*sizeof("foo")); 

för att kasta en kompileringstidsdelning med nollfel om du ska överflödar arrayen.

Svar

Jag tror att ”dålig praxis” -idén kommer från det faktum att denna form:

char string[] = "october is a nice month"; 

gör implicit en strcpy från källmaskinkoden till stacken.

Det är effektivare att bara hantera en länk till den strängen. Som med:

char *string = "october is a nice month"; 

eller direkt:

strcpy(output, "october is a nice month"); 

(men naturligtvis i de flesta fall kod spelar det förmodligen ingen roll)

Kommentarer

  • Skulle ’ t bara göra en kopia om du försöker ändra det? Jag tror att kompilatorn skulle vara smartare än så
  • Vad sägs om fall som char time_buf[] = "00:00"; där du ’ kommer att ändra en buffert? En char * initierad till en strängbokstav är inställd på adressen till den första byten, så att försöka ändra den resulterar i odefinierat beteende eftersom metoden för strängens bokstavliga ’ s lagring är okänd (implementeringsdefinierad), medan ändring av byte i en char[] är helt laglig eftersom initialisering kopierar byten till ett skrivbart utrymme som tilldelats på stacken. Att säga att det ’ s ” mindre effektivt eller ” dålig praxis ” utan att utarbeta nyanserna i char* vs char[] är vilseledande.

Svar

Det är aldrig riktigt lång tid, men du bör undvika initialisering char [] till sträng, eftersom ”sträng” är const char *, och du tilldelar den till char *. Så om du skickar denna char [] till metod som ändrar data kan du ha intressant beteende.

Som berömt sagt blandade jag lite char [] med char *, det är inte bra eftersom de skiljer sig lite.

Det är inget fel med att tilldela data till char array, men eftersom avsikten att använda den här arrayen är att använda den som ”string” (char *) är det lätt att glömma att du inte bör ändra detta array.

Kommentarer

  • Felaktigt. Initieringen kopierar innehållet i strängen bokstavligt in i arrayen. Matrisobjektet är inte ’ t const om du inte definierar det så.(Och stränglitteraler i C är inte const, även om alla försök att ändra en stränglitteratur har odefinierat beteende.) char *s = "literal"; har dock typ av beteende som du ’ talar om; det ’ skrivs bättre som const char *s = "literal";
  • ” Och i allmänhet är ” asdf ” en konstant, så det bör förklaras som konst. ” – Samma resonemang skulle kräva en constint n = 42;, eftersom 42 är en konstant.
  • Det spelar ingen roll ’ vilken maskin du ’ är på. Språkstandarden garanterar att c kan ändras. Det ’ är exakt lika stark garanti som den som 1 + 1 utvärderar till 2. Om programmet jag länkade till ovan gör något annat än att skriva ut EFGH, indikerar det en icke-överensstämmande C-implementering.
  • @Dainus: MSVC-kompilatorn har en optimering som heter ’ strängpooling ’ som kommer att lägga en enda kopia av identiska strängar i ett skrivskyddat segment om det kan garantera att användningen av dem är skrivskyddad. Stäng av optimeringen för att se ’ normalt ’ beteende. FYI ” Redigera och fortsätt ” kräver att detta alternativ är på. Mer info här: msdn.microsoft.com/en-us/library/s0s0asdt.aspx
  • Jag tror att Dainius föreslår att det i många fall fallet är felet att själva variabeln ska markeras const char *const för att förhindra modifiering av byte eller själva pekaren, men i många fall kommer programmerare att låta en eller båda vara mutabla så att någon runtime-kod kan ändra vad som verkar vara en skriven konstant (men inte är konstant).

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *