Ik las een thread met de titel “strlen vs sizeof” op CodeGuru , en een van de antwoorden stelt dat “het” sowieso [sic] een slechte gewoonte is om [sic] een char array te initialiseren met een letterlijke tekenreeks. “
Is dit waar, of is dat alleen zijn (zij het een” elitelid “) mening?
Hier is de oorspronkelijke vraag:
#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; }
klopt. de grootte moet de lengte zijn plus 1 ja?
dit is de output
the size of september is 8 and the length is 9size zou zeker 10 moeten zijn. het is alsof het de grootte van de string berekent voordat het veranderd wordt door strcpy maar de lengte daarna.
Is er iets mis met mijn syntaxis of wat?
Hier is het antwoord :
Het is sowieso een slechte gewoonte om een tekenreeks te initialiseren met een letterlijke tekenreeks. Voer dus altijd een van de volgende handelingen uit:
const char string1[] = "october"; char string2[20]; strcpy(string2, "september");
Reacties
- Let op de ” const ” op de eerste regel. Zou het kunnen dat de auteur c ++ heeft aangenomen in plaats van c? In c ++ is het ” slechte praktijk “, omdat een letterlijke waarde const zou moeten zijn en elke recente c ++ compiler zal een waarschuwing (of fout) geven over het toewijzen van een letterlijke const aan een niet-const-array.
- @Andr é C ++ definieert letterlijke tekenreeksen als const arrays, omdat dat de enige veilige manier is om te handelen met hen. Dat C niet ‘ t is, is het probleem, dus je hebt een sociale regel die het veilige ding afdwingt
- @Caleth. Ik weet het, ik probeerde meer te beweren dat de auteur van het antwoord de ” slechte praktijk ” benaderde vanuit een c ++ perspectief.
- @Andr é het is geen ‘ een slechte praktijk in C ++, omdat het isn ‘ ta oefenen , het ‘ is een regelrechte typefout. Het zou een typefout in C moeten zijn, maar het is niet ‘ t, dus je hebt een stijlgidsregel nodig die je vertelt ” Het ‘ is verboden ”
Antwoord
Het is sowieso een slechte gewoonte om een tekenreeks te initialiseren met een letterlijke tekenreeks.
De auteur van die opmerking rechtvaardigt het nooit echt, en ik vind de uitspraak raadselachtig.
In C (en je “hebt dit als C getagd), dat” Het is vrijwel de enige manier om initialiseren een array van char met een stringwaarde (initialisatie is anders dan toekennen). Je kunt ofwel
schrijven
char string[] = "october";
of
char string[8] = "october";
of
char string[MAX_MONTH_LENGTH] = "october";
In het eerste geval wordt de grootte van de array ontleend aan de grootte van de initializer. Letterlijke tekenreeksen worden opgeslagen als arrays van char met een afsluitende 0 byte, dus de grootte van de array is 8 (“o”, “c”, “t”, “o”, “b”, “e”, “r”, 0). In de tweede twee gevallen wordt de grootte van de array gespecificeerd als onderdeel van de declaratie (8 en MAX_MONTH_LENGTH, wat dat ook mag zijn).
Wat je niet kunt doen, is iets schrijven als
char string[]; string = "october";
of
char string[8]; string = "october";
enz. In het eerste geval is de declaratie van string incompleet omdat er geen arraygrootte is opgegeven en er geen initialisatieprogramma is om de grootte van te nemen. In beide gevallen zal de = “niet werken omdat a) een array-expressie zoals string mogelijk niet het doelwit van een toewijzing is en b) de = operator is sowieso niet gedefinieerd om de inhoud van de ene array naar de andere te kopiëren.
Met datzelfde token kun je “niet schrijven
char string[] = foo;
waarbij foo een andere array is van char. Deze vorm van initialisatie werkt alleen met letterlijke tekenreeksen.
EDIT
Ik zou dit moeten wijzigen om te zeggen dat je ook kunt initialiseren arrays om een string vast te houden met een array-style initializer, zoals
char string[] = {"o", "c", "t", "o", "b", "e", "r", 0};
of
char string[] = {111, 99, 116, 111, 98, 101, 114, 0}; // assumes ASCII
maar het is gemakkelijker voor de ogen om letterlijke tekenreeksen te gebruiken.
EDIT 2
Om wijs de inhoud van een array buiten een declaratie toe, je zou ofwel strcpy/strncpy (voor 0-beëindigde strings) of (voor elk ander type array):
if (sizeof string > strlen("october")) strcpy(string, "october");
of
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!
Reacties
-
strncpyis zelden het juiste antwoord - @KeithThompson: niet oneens, alleen voor de volledigheid toegevoegd ‘ sake.
- Houd er rekening mee dat
char[8] str = "october";is een slechte gewoonte. Ik moest mezelf letterlijk tellen om er zeker van te zijn dat het geen ‘ t een overloop was en het breekt tijdens onderhoud … bijv. het corrigeren van een spelfout vansepratenaarseparatezal breken als de grootte niet wordt bijgewerkt. - Ik ben het met djechlin eens, het is om de genoemde redenen een slechte gewoonte. JohnBode ‘ s antwoord geeft geen ‘ commentaar op de ” slechte praktijk ” aspect (dat is het belangrijkste deel van de vraag !!), het legt alleen uit wat je wel of niet kunt doen om de array te initialiseren.
- Minor: As ‘ lengte ” waarde geretourneerd van
strlen()bevat geen null-teken, metMAX_MONTH_LENGTHom de maximale grootte vast te houden die nodig is voorchar string[]ziet er vaak er verkeerd uit . IMO,MAX_MONTH_SIZEzou hier beter zijn.
Antwoord
Het enige probleem dat ik me herinner is het letterlijk toewijzen van een string aan 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
Neem bijvoorbeeld dit programma:
#include <stdio.h> int main() { char *var1 = "september"; char *var2 = "september"; var1[0] = "S"; printf("%s\n", var2); }
Dit crasht op mijn platform (Linux) als het probeert te schrijven naar een pagina die is gemarkeerd als alleen-lezen. Op andere platforms kan het “September” enz. Afdrukken.
Dat gezegd hebbende – letterlijke initialisatie maakt het specifieke aantal reserveringen “, zodat dit” niet werkt:
char buf[] = "May"; strncpy(buf, "September", sizeof(buf)); // Result "Sep"
Maar dit zal
char buf[32] = "May"; strncpy(buf, "September", sizeof(buf));
Als laatste opmerking – ik zou strcpy helemaal:
char buf[8]; strcpy(buf, "very long string very long string"); // Oops. We overwrite some random memory
Hoewel sommige compilers het kunnen veranderen in een veilige aanroep is strncpy veel veiliger:
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";
Reacties
- Er is ‘ s nog steeds een risico voor bufferoverschrijding op die
strncpyomdat het niet ‘ t null de gekopieerde tekenreeks beëindigt wanneer de lengte vansomething_elseis groter dansizeof(buf). Ik stel meestal het laatste tekenbuf[sizeof(buf)-1] = 0in om daartegen te beschermen, of alsbufnul-geïnitialiseerd is, gebruik dansizeof(buf) - 1als de kopie lengte. - Gebruik
strlcpyofstrcpy_sof zelfssnprintfals het moet. - Opgelost. Helaas is er geen gemakkelijke draagbare manier om dit te doen, tenzij je de luxe hebt om met de nieuwste compilers te werken (
strlcpyensnprintfzijn niet direct toegankelijk op MSVC staan tenminste bestellingen enstrcpy_sniet op * nix). - @MaciejPiechotka: Nou, godzijdank heeft Unix de door Microsoft gesponsorde bijlage k afgewezen.
Antwoord
Vooral omdat je “niet de grootte van de char[] in een variabele / construct die je gemakkelijk in het programma kunt gebruiken.
Het codevoorbeeld van de link:
char string[] = "october"; strcpy(string, "september");
string wordt op de stapel toegewezen als 7 of 8 tekens. Ik kan me niet herinneren of het op deze manier met null is beëindigd of niet – de thread waarnaar je hebt gelinkt, heeft aangegeven dat het zo is .
Het kopiëren van “september” over die string is een overduidelijke geheugenoverschrijding.
Een andere uitdaging ontstaat als je string doorgeeft aan een andere functie.zodat de andere functie in de array kan schrijven. U moet de andere functie vertellen hoe lang de array is, zodat het geen “overschrijding” veroorzaakt. U kunt string doorgeven samen met het resultaat van strlen() maar de thread legt uit hoe dit kan worden opgeblazen als string niet wordt beëindigd.
Je bent beter af het toewijzen van een string met een vaste grootte (bij voorkeur gedefinieerd als een constante) en vervolgens de array en de vaste grootte doorgeven aan de andere functie. De opmerking (en) van @John Bode is / zijn correct, en er zijn manieren om deze risicos te beperken. Ze vereisen ook meer inspanning van uw kant om ze te gebruiken.
In mijn ervaring is de waarde die ik heb geïnitialiseerd de char[] to is meestal te klein voor de andere waarden die ik daar moet plaatsen. Het gebruik van een gedefinieerde constante helpt om dat probleem te voorkomen.
sizeof string geeft u de grootte van de buffer (8 bytes); gebruik het resultaat van die uitdrukking in plaats van strlen als je je zorgen maakt over het geheugen.
Evenzo kun je een controle uitvoeren voordat je strcpy om te zien of je doelbuffer groot genoeg is voor de source string: if (sizeof target > strlen(src)) { strcpy (target, src); }.
Ja, als je de array aan een functie moet doorgeven, zul je moeten ook de fysieke grootte doorgeven: foo (array, sizeof array / sizeof *array);. – John Bode
Reacties
-
sizeof stringgeeft je de grootte van de buffer (8 bytes); gebruik het resultaat van die uitdrukking in plaats vanstrlenwanneer je ‘ bezorgd bent over het geheugen. Evenzo kunt u vóór de aanroep vanstrcpycontroleren of uw doelbuffer groot genoeg is voor de brontekenreeks:if (sizeof target > strlen(src)) { strcpy (target, src); }. Ja, als je de array aan een functie moet doorgeven, moet je ‘ ook de fysieke grootte doorgeven:foo (array, sizeof array / sizeof *array);. - @JohnBode – bedankt, en dat zijn goede punten. Ik heb uw opmerking in mijn antwoord verwerkt.
- Meer precies, de meeste verwijzingen naar de arraynaam
stringresulteren in een impliciete conversie naarchar*, wijzend naar het eerste element van de array. Hierdoor gaat de informatie over de arraygrenzen verloren. Een functieaanroep is slechts een van de vele contexten waarin dit gebeurt.char *ptr = string;is een andere. Zelfsstring[0]is hier een voorbeeld van; de[]-operator werkt op pointers, niet rechtstreeks op arrays. Voorgestelde lezing: sectie 6 van de comp.lang.c FAQ . - Eindelijk een antwoord dat daadwerkelijk naar de vraag verwijst!
Antwoord
Een ding dat geen van beide discussies naar voren brengt, is dit:
char whopping_great[8192] = "foo";
vs.
char whopping_great[8192]; memcpy(whopping_great, "foo", sizeof("foo"));
De eerste doet zoiets als:
memcpy(whopping_great, "foo", sizeof("foo")); memset(&whopping_great[sizeof("foo")], 0, sizeof(whopping_great)-sizeof("foo"));
De laatste doet alleen de memcpy. De C-standaard houdt vol dat als een deel van een array wordt geïnitialiseerd, dit alles is. Dus in dit geval is het beter om het zelf te doen. Ik denk dat Treuss dat misschien bedoelde.
Zeker
char whopping_big[8192]; whopping_big[0] = 0;
is beter dan:
char whopping_big[8192] = {0};
of
char whopping_big[8192] = "";
ps Voor bonuspunten kunt u het volgende doen:
memcpy(whopping_great, "foo", (1/(sizeof("foo") <= sizeof(whopping_great)))*sizeof("foo"));
om een compilatietijd te delen door nul fout als u op het punt staat de array te overlopen.
Antwoord
Ik denk dat het idee van “slechte praktijken” voortkomt uit het feit dat dit formulier:
char string[] = "october is a nice month";
maakt impliciet een strcpy van de bronmachinecode naar de stapel.
Het is efficiënter om alleen een link naar die string te verwerken. Zoals met:
char *string = "october is a nice month";
of direct:
strcpy(output, "october is a nice month");
(maar natuurlijk in de meeste code maakt het waarschijnlijk niet uit)
Reacties
- Zou het niet ‘ maken om alleen een kopie te maken als je het probeert te wijzigen? Ik denk dat de compiler slimmer is dan dat
- Hoe zit het met gevallen als
char time_buf[] = "00:00";waarin je ‘ gaat een buffer wijzigen? Eenchar *geïnitialiseerd met een letterlijke tekenreeks wordt ingesteld op het adres van de eerste byte, dus als u probeert deze te wijzigen, resulteert dit in ongedefinieerd gedrag omdat de methode van de letterlijke tekenreeks ‘ s opslag is onbekend (implementatie gedefinieerd), terwijl het wijzigen van de bytes van eenchar[]volkomen legaal is omdat de initialisatie kopieert de bytes naar een schrijfbare ruimte die is toegewezen op de stapel. Om te zeggen dat het ‘ s ” minder efficiënt of ” slechte praktijken ” zonder de nuances vanchar* vs char[]is misleidend.
Answer
Nooit is echt lang, maar je moet initialisatie char [] vermijden to string, omdat “string” const char * is, en je wijst het toe aan char *. Dus als je dit char [] doorgeeft aan een methode die gegevens verandert, kun je interessant gedrag vertonen.
Zoals gezegd heb ik een beetje char [] gemengd met char *, dat is niet goed, want ze verschillen een beetje.
Er is niets mis met het toewijzen van gegevens aan de char-array, maar aangezien het de bedoeling is deze array te gebruiken als “string” (char *), is het gemakkelijk om te vergeten dat je dit niet moet wijzigen array.
Commentaar
- Onjuist. De initialisatie kopieert de inhoud van de tekenreeks letterlijk naar de array. Het array-object isn ‘ t
consttenzij je het op die manier definieert.(En letterlijke tekenreeksen in C zijn nietconst, hoewel elke poging om een letterlijke tekenreeks te wijzigen een ongedefinieerd gedrag heeft.)char *s = "literal";heeft de soort gedrag waar je ‘ over praat; het ‘ is beter geschreven alsconst char *s = "literal"; - ” En in het algemeen is ” asdf ” een constante, dus het moet worden gedeclareerd als const. ” – Dezelfde redenering vraagt om een
constopint n = 42;, omdat42is een constante. - Het doet ‘ niet uit op welke machine je ‘ bent. De taalstandaard garandeert dat
caanpasbaar is. Het ‘ is precies een even sterke garantie als degene die1 + 1evalueert naar2. Als het programma waarnaar ik hierboven heb gelinkt iets anders doet danEFGHafdrukken, duidt dit op een niet-conforme C-implementatie. - @Dainus: de MSVC-compiler heeft een optimalisatie genaamd ‘ string pooling ‘ die een enkele kopie van identieke strings in een alleen-lezen segment als het kan garanderen dat het gebruik ervan alleen-lezen is. Schakel de optimalisatie uit om ‘ normaal ‘ gedrag te zien. Ter info: de ” Bewerken en doorgaan ” vereist dat deze optie is ingeschakeld. Meer info hier: msdn.microsoft.com/en-us/library/s0s0asdt.aspx
- Ik denk dat Dainius dat in veel gevallen is de fout dat de variabele zelf gemarkeerd moet worden als
const char *constom wijziging van de bytes of de pointer zelf te voorkomen, maar in veel gevallen laten programmeurs een of beide veranderlijk achter, waardoor sommige runtime-code wijzig wat een getypte constante lijkt te zijn (maar is niet constant).