Onko merkin [] alustaminen merkkijonolla kirjaimellisesti huono käytäntö?

Luin ketjua ”strlen vs sizeof” CodeGurussa ja yksi vastauksista toteaa, että ”se on joka tapauksessa [sic] huonoa käytäntöä aloittaa [sic] char taulukko merkkijonolla. ”

Onko tämä totta, vai onko se vain hänen (vaikkakin” eliittijäsenen ”) mielipide?


Tässä on alkuperäinen kysymys:

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

oikea. koon tulisi olla pituus plus 1 kyllä?

tämä on ulostulon

the size of september is 8 and the length is 9

koon tulisi olla varmasti 10. Se on sama kuin laskettaessa merkkijonon kokoa, ennen kuin strcpy muuttaa sitä, mutta pituus jälkeen.

Onko syntaksissani jotain vikaa vai mitä?


Tässä on vastaus :

On joka tapauksessa huono käytäntö aloittaa char-taulukko merkkijonon kirjaimella. Tee siis jokin seuraavista:

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

Kommentit

  • Huomaa ” const ” ensimmäisellä rivillä. Voisiko kirjailija olettaa, että c ++ ei ole c? C ++: ssa se on ” huono käytäntö ”, koska literaalin tulisi olla const ja mikä tahansa viimeaikainen c ++ -kääntäjä antaa varoituksen (tai virheen) konst-kirjaimen määrittämisestä muulle kuin const-taulukolle.
  • @Andr é C ++ määrittelee merkkijonon literaaliksi const-matriiseiksi, koska se on ainoa turvallinen tapa käsitellä heidän kanssaan. Se, että C ei ’ t , on ongelma, joten sinulla on sosiaalinen sääntö, joka panee täytäntöön turvallisen asian
  • @Caleth. Tiedän, yritin enemmän väittää, että vastauksen kirjoittaja lähestyi ” huonoa käytäntöä ” c ++ -näkökulmasta.
  • @Andr é se ei ole ’ ta huono käytäntö C ++: ssa, koska se ei ole ’ ta harjoittaa , se ’ on suora tyyppivirhe. Sen pitäisi olla tyypin virhe C: ssä, mutta se ei ole ’ t, joten sinulla on oltava tyylioppaan sääntö, joka kertoo sinulle ” Se ’ on kielletty ”

vastaus

On joka tapauksessa huono käytäntö aloittaa char-taulukko merkkijonon kirjaimella.

Kommentin kirjoittaja ei koskaan oikeastaan perustele sitä, ja pidän lausetta hämmentävänä.

Kohdassa C (ja olet merkinnyt tämän nimellä C), että ” se on melkein ainoa tapa alustaa taulukko char merkkijonolla (alustaminen eroaa tehtävästä). Voit kirjoittaa joko

char string[] = "october"; 

tai

char string[8] = "october"; 

tai

char string[MAX_MONTH_LENGTH] = "october"; 

Ensimmäisessä tapauksessa matriisin koko otetaan alustimen koosta. Merkkijono-literaalit tallennetaan taulukkoina char päättyvällä 0 tavulla, joten taulukon koko on 8 (”o”, ”c”, ”t”, ”o”, ”b”, ”e”, ”r”, 0). Kahdessa toisessa tapauksessa taulukon koko määritetään osana ilmoitusta (8 ja MAX_MONTH_LENGTH, riippumatta siitä mitä tapahtuu).

Mitä et voi tehdä, on kirjoittaa esimerkiksi

char string[]; string = "october"; 

tai

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

jne. Ensimmäisessä tapauksessa string -ilmoitus on epätäydellinen , koska matriisikokoa ei ole määritetty eikä koon ottamiseen ole alustinta. Molemmissa tapauksissa = ei toimi, koska a) matriisilauseke, kuten string, ei ehkä ole tehtävän kohde ja b) = -operaattoria ei ole määritelty kopioimaan yhden taulukon sisältö toiseen.

Samalla tunnuksella voit ”t kirjoittaa”

char string[] = foo; 

jossa foo on toinen ryhmän char joukko. Tämä alustusmuoto toimii vain merkkijono-literaalien kanssa.

MUOKKAA

Haluan muuttaa tätä sanomalla, että voit myös alustaa taulukot, jotka pitävät merkkijonoa taulukotyyppisellä alustusohjelmalla, kuten

char string[] = {"o", "c", "t", "o", "b", "e", "r", 0}; 

tai

char string[] = {111, 99, 116, 111, 98, 101, 114, 0}; // assumes ASCII 

mutta merkkijono-literaaleja on silmissä helpompaa käyttää.

MUOKKAA 2

Jotta Määritä taulukon sisältö ilmoituksen ulkopuolelle, sinun on käytettävä joko strcpy/strncpy (0-päättyville merkkijonoille) tai memcpy (minkä tahansa muun tyyppisen taulukon kohdalla):

if (sizeof string > strlen("october")) strcpy(string, "october"); 

tai

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! 

Kommentit

  • strncpy on harvoin oikea vastaus
  • @KeithThompson: ei ole eri mieltä, vain lisäsi sen täydellisyyden vuoksi ’.
  • Huomaa, että char[8] str = "october"; on huono käytäntö. Minun oli kirjaimellisesti kirjattava itseni varmistaakseni, että se ei ollut ’ ylivuoto ja se rikkoutuu huollon aikana … esim. kirjoitusvirheen korjaaminen seprateseparate rikkoo, jos kokoa ei päivitetä.
  • Olen samaa mieltä djechlinin kanssa on huono käytäntö mainituista syistä. JohnBode ’ ei vastaa ’ t kommentoi ollenkaan ” huonoa käytäntöä ” -näkökulma (joka on kysymyksen pääosa !!), se vain selittää, mitä voit tai ei voi tehdä matriisin alustamiseksi.
  • Pienet: Kuten ’ length ” arvo, joka on palautettu osoitteesta strlen(), ei sisällä nollamerkkiä, käyttämällä MAX_MONTH_LENGTH, jotta char string[] tarvitsisi enimmäiskoon, näyttää usein väärältä. IMO, MAX_MONTH_SIZE olisi parempi tässä.

Vastaa

Ainoa ongelma, jonka muistan, on merkkijonon kirjaimellisen osoittaminen 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 

Ota esimerkiksi tämä ohjelma:

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

Tämä foorumillani (Linux) kaatuu, kun se yrittää kirjoittaa vain luku -merkitylle sivulle. Muilla alustoilla se saattaa tulostaa ”syyskuu” jne.

Sanottu – alustaminen kirjaimella tekee tietyn varauksen määrän, joten tämä ei toimi:

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

Mutta tämä

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

Viimeisenä huomautuksena – en käytä strcpy ollenkaan:

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

Vaikka jotkut kääntäjät voivat muuttaa sen turvalliseksi puheluksi, strncpy on paljon turvallisempaa:

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"; 

Kommentit

  • Siellä ’ on edelleen puskurin ylityksen riski strncpy, koska se ei ’ t nollaa kopioidun merkkijonon, kun something_else on suurempi kuin sizeof(buf). Asetan yleensä viimeisen merkin buf[sizeof(buf)-1] = 0 suojaamaan siitä, tai jos buf on nolla-alusta, käytä sizeof(buf) - 1 kopion pituudeksi.
  • Käytä strlcpy tai strcpy_s tai jopa snprintf, jos joudut.
  • Korjattu. Valitettavasti tähän ei ole helppoa kannettavaa tapaa, ellei sinulla ole ylellisyyttä työskennellä uusimpien kääntäjien kanssa (strlcpy ja snprintf eivät ole suoraan käytettävissä MSVC: llä ainakin tilaukset ja strcpy_s eivät ole käytössä * nix).
  • @MaciejPiechotka: No, kiitos jumalalle, että Unix hylkäsi Microsoftin tukeman liitteen k.

vastaus

Ensinnäkin siksi, että sinulla ei ole kokoa char[] muuttujassa / rakenteessa, jota voit helposti käyttää ohjelmassa.

Koodinäyte linkistä:

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

string on varattu pinolle 7 tai 8 merkin pituiseksi. En voi muistaa, onko sen lopettaminen tällä tavalla vai ei – linkittämäsi ketju ilmoitti, että se on .

”Syyskuun” kopioiminen merkkijonon päälle on selvä muistin ylitys.

Toinen haaste syntyy, jos välität string toiselle funktiolle.joten toinen toiminto voi kirjoittaa taulukkoon. Sinun on kerrottava toiselle funktiolle, kuinka kauan taulukko on niin se ei luo ylitystä. Voit välittää string -kohdan strlen(), mutta säie selittää, miten tämä voi räjähtää, jos string ei ole lopetettu.

Sinulla on parempi varataan merkkijono, jolla on kiinteä koko (mieluiten määritellään vakiona) ja siirretään sitten taulukko ja kiinteä koko toiselle toiminnolle. @John Boden kommentit ovat oikein, ja on olemassa tapoja lieventää näitä riskejä. Niiden käyttö vaatii myös enemmän ponnisteluja.

Kokemukseni mukaan arvo, jonka aloitin char[] on yleensä liian pieni muille arvoille, jotka minun on lisättävä sinne. Määritetyn vakion käyttäminen auttaa välttämään ongelman.


sizeof string antaa puskurin koon (8 tavua); käytä lausekkeen tulosta strlen sijaan, kun olet huolissasi muistista.
Vastaavasti voit tehdä tarkistuksen ennen strcpy nähdäksesi onko kohdepuskuri riittävän suuri lähdemerkkijonoa varten: if (sizeof target > strlen(src)) { strcpy (target, src); }.
Kyllä, jos sinun on välitettävä taulukko funktiolle, täytyy siirtää myös sen fyysinen koko: foo (array, sizeof array / sizeof *array);. – John Bode

Kommentit

  • sizeof string antaa sinulle puskurin koon (8 tavua); käytä lausekkeen tulosta strlen sijaan, kun ’ olet huolestunut muistista. Vastaavasti voit tarkistaa ennen puhelua numeroon strcpy nähdäksesi, onko kohdepuskuri riittävän suuri lähdemerkkijonolle: if (sizeof target > strlen(src)) { strcpy (target, src); }. Kyllä, jos sinun on välitettävä taulukko funktiolle, ’ sinun on välitettävä myös sen fyysinen koko: foo (array, sizeof array / sizeof *array);.
  • @JohnBode – kiitos, ja nämä ovat hyviä kohtia. Olen sisällyttänyt kommenttisi vastaukseeni.
  • Tarkemmin sanottuna suurin osa viitteistä taulukon nimestä string johtaa implisiittiseen muuntamiseen char*, osoittamalla taulukon ensimmäiseen elementtiin. Tämä menettää taulukon rajatiedot. Funktiokutsu on vain yksi monista tilanteista, joissa näin tapahtuu. char *ptr = string; on toinen. Jopa string[0] on esimerkki tästä; [] -operaattori toimii osoittimilla, ei suoraan matriiseilla. Ehdotettu lukeminen: comp.lang.c UKK: n osio 6.
  • Lopuksi vastaus, joka todella viittaa kysymykseen!

Vastaa

Yksi asia, jota kumpikaan säie ei tuo esiin, on tämä:

char whopping_great[8192] = "foo"; 

vs.

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

Edelliset tekevät jotain seuraavista:

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

Jälkimmäinen tekee vain memcpy: n. C-standardi vaatii, että jos jokin matriisin osa alustetaan, kaikki on. Joten tässä tapauksessa on parempi tehdä se itse. Luulen, että treuss saattoi olla siinä.

Varmasti

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

on parempi kuin jompikumpi:

char whopping_big[8192] = {0}; 

tai

char whopping_big[8192] = ""; 

ps For bonuspisteitä, voit tehdä:

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

heittää käännösaika jakamalla nollavirheellä, jos aiot ylittää taulukon.

vastaus

Mielestäni ”huonojen käytäntöjen” idea tulee siitä, että tämä muoto:

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

tekee implisiittisesti strcpy: n lähdekoodikoodista pinoon.

On tehokkaampaa käsitellä vain linkkiä kyseiseen merkkijonoon. Kuten:

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

tai suoraan:

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

(mutta tietysti useimmissa koodaa sillä ei todennäköisesti ole väliä)

Kommentit

  • Ei ’ t vain tekisi kopion jos yrität muokata sitä? Luulen, että kääntäjä olisi sitä älykkäämpi
  • Entä tapaukset, kuten char time_buf[] = "00:00";, joissa ’ muokataanko puskuria? Merkkijonon kirjaimeksi alustettu char * asetetaan ensimmäisen tavun osoitteeksi, joten sen yrittäminen johtaa määrittelemättömään toimintaan, koska merkkijonon literal ’ -tallennusmenetelmää ei tunneta (toteutus määritelty), kun taas char[] -tavujen muokkaaminen on täysin laillista, koska alustaminen kopioi tavut pinoon varattuun kirjoitettavaan tilaan. Sanotaan, että se ’ s ” vähemmän tehokas tai ” huono käytäntö ” käsittelemättä char* vs char[] on harhaanjohtava.

Vastaa

Koskaan ei ole todella pitkä aika, mutta sinun tulisi välttää alustusmerkkiä [] merkkijonoon, koska ”merkkijono” on const char *, ja määrität sen merkille char *. Joten jos välität tämän merkin [] menetelmälle, joka muuttaa tietoja, sinulla voi olla mielenkiintoinen käyttäytyminen.

Kuten kiitosta sanoin, sekoitin vähän char [] char *: een, se ei ole hyvä, koska ne eroavat hieman.

Tietojen osoittamisessa char-taulukolle ei ole mitään vikaa, mutta koska tämän taulukon tarkoituksena on käyttää sitä merkkijonona (char *), on helppo unohtaa, että sinun ei pidä muokata tätä taulukko.

Kommentit

  • Virheellinen. Alustus kopioi merkkijonon sisällön taulukkoon. Taulukko-objekti ei ole ’ t const ellet määritä sitä tällä tavalla.(Ja merkkijonon literaalit C: ssä eivät ole const, vaikka kaikilla merkkijonolitraalin muokkausyrityksillä on määrittelemätön käyttäytyminen.) char *s = "literal"; on sellaisesta käyttäytymisestä, josta ’ puhut; se ’ on parempi kirjoittaa nimellä const char *s = "literal";
  • ” Ja yleensä ” asdf ” on vakio, joten se tulisi ilmoittaa konst. ” – Sama päättely vaatii const kohtaan int n = 42;, koska 42 on vakio.
  • Sillä ’ ei ole väliä mitä konetta ’ käytät. Kielistandardi takaa, että c on muokattavissa. Se ’ on täsmälleen yhtä vahva takuu kuin 1 + 1 arvioksi 2. Jos yllä linkitetty ohjelma tekee muuta kuin tulostamalla EFGH, se tarkoittaa vaatimusten vastaista C-toteutusta.
  • @Dainus: MSVC-kääntäjällä on optimointi nimeltä ’ merkkijonojen yhdistäminen ’, joka laittaa yhden kopion identtiset merkkijonot vain luku -segmentiksi, jos se voi taata, että niiden käyttö on vain luku -tilaa. Katkaise optimointi käytöstä nähdäksesi ’ normaalin ’ käyttäytymisen. FYI ” Muokkaa ja jatka ” vaatii tämän vaihtoehdon olevan päällä. Lisätietoja täältä: msdn.microsoft.com/en-us/library/s0s0asdt.aspx
  • Luulen, että Dainius ehdottaa sitä monissa tapauksissa virhe on, että muuttuja itsessään on merkittävä const char *const estämään tavujen tai itse osoittimen muokkaaminen, mutta monissa tapauksissa ohjelmoijat jättävät yhden tai molemmat muutettaviksi, jolloin osa ajonaikaisesta koodista muokata kirjoitettua vakiota (mutta ei vakiota).

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *