Waarom gebruiken strings char *?

Waarom gebruiken de meeste stringfuncties in de C/C ++ -stdlibs char * -aanwijzers?

De ondertekende char is niet eens gespecificeerd in de standaard, hoewel de meeste moderne compilers (GCC, MSVC) char behandelen zoals standaard is ondertekend.

Wanneer zou het zinvol zijn om strings als (mogelijk) ondertekende bytes te behandelen? AFAIK er zijn geen betekenisvolle tekenwaarden onder nul in een tekenset. Voor bepaalde stringbewerkingen moeten de waarden hoe dan ook worden gegoten in unsigned char .

Dus waarom gebruiken de stdlibs char * ? Zelfs C ++ -specifieke methoden, zoals string :: string (const char *); ?

21
Waarom null-terminated-strings in plaats van een pascal-style length-array-paar? Ik ben er zeker van dat iemand de mooie verklaringen zal bedenken, maar het is duidelijk dat veel ervan alleen maar komt door historische en achterwaartse compatibiliteitsproblemen.
toegevoegd de auteur hugomg, de bron
Opmerking: Of char is ondertekend of niet, is de implementatie gedefinieerd.
toegevoegd de auteur sepp2k, de bron
Je naam doet vermoeden dat je bevooroordeeld bent;)
toegevoegd de auteur huon, de bron
De instructies op de PDP-11 die bytes behandelen, behandelden ze als gesigneerde hoeveelheden, dus dat is hoe de vroege C-compilers ze behandelden, en unsigned bestond niet eens.
toegevoegd de auteur Jim Balter, de bron
@missingno, een deel van de redenering was dat het hebben van een lengte je zou dwingen om te beperken tot korte (<256 tekens) strings of een overhead van twee bytes had die te veel zou zijn voor het meeste doel op dat moment en op de machine waar C was ontworpen (met een adresruimte van 64 KB).
toegevoegd de auteur AProgrammer, de bron
@Maxime - Ik vermoed dat toupper / tolower van vóór het type unsigned char was en daarom int gebruikte om in staat zijn om alle mogelijke tekenwaarden 0-255 te houden
toegevoegd de auteur Unsigned, de bron
@dbaupp - Haha, leuk, daar dacht ik niet eens aan!
toegevoegd de auteur Unsigned, de bron
Ik zou ook graag willen weten waarom toupper , tolower enzovoort een argument als een geheel getal beschouwt.
toegevoegd de auteur Maxime Chéramy, de bron

7 antwoord

  1. Ik ben er vrij zeker van dat de meeste van de tekenreeksfuncties ouder zijn dan het bestaan ​​van unsigned char .
  2. Plain char is mogelijk een ondertekend of een niet-ondertekend type. De C- en C ++ -standaarden staan ​​beide expliciet toe (het is altijd een ander type dan unsigned char of signed char , maar heeft hetzelfde bereik als de een of de andere).
  3. Terwijl de C-reeksfuncties char * gebruiken, is std :: string wat in de meeste C ++ wordt gebruikt.
10
toegevoegd
De string-functies dateren van vóór de toevoeging van 'niet-ondertekend' aan de taal, en de PDP-11-hardware maakte het efficiënter om tekens te behandelen als ondertekend, en dat waren de dagen van 7-bits ASCII.
toegevoegd de auteur Jim Balter, de bron
En natuurlijk is er geen verschil in geheugen tussen een pointer naar een ondertekende/niet-ondertekende char
toegevoegd de auteur Martin Beckett, de bron

De C-standaard is agnostisch over de vraag of gewoon char is ondertekend of niet ondertekend en behandelt char op unieke wijze in plaats van signed char . Verder bestaat de basis-ASCII-tekenset, die de meeste belangrijke besturings- en Engelstalige afdrukbare tekens bevat, uit 128 tekens en kan daarom adequaat worden weergegeven door een ondertekende char (tenminste op elk systeem dat 8 biedt). bits per byte). Zoals Jim Balter opmerkt (zie opmerkingen hieronder), vormt ASCII niet de volledige basistekenset van de C-taal, maar ik vermoed dat het de meerderheid van de tekens bevat die veel worden gebruikt. Er is ook een groot aantal C-codes die steunen op eigenschappen van (hoewel niet noodzakelijk uniek voor) ASCII (bijv. Het speciale teken NUL met een waarde nul, alfanumerieke tekens die opeenvolgend en in oplopende volgorde worden gerangschikt bestelling, etc.).

10
toegevoegd
In de huidige vraag wordt ASCII niet genoemd, en de vraag impliceert het gebruik van tekens met meer dan 7 bits. Uw antwoord wekt de indruk dat 7-bits ASCII de basistekenset van de C-taal is, wat onjuist is.
toegevoegd de auteur Jim Balter, de bron
De vooraf bewerkte vraag lijkt voor mij niet om het vermoeden te stellen dat u vermeldt. En nadat ik op X3J11 heb gediend, weet ik heel goed wat de standaard zegt over char. Wat normatief versus historisch betreft, is de vraag zinvol als de vrager zich niet bewust is dat de keuze afhing van historische onvoorziene omstandigheden. Het was een goede vraag.
toegevoegd de auteur Jim Balter, de bron
Postscriptum Omdat de vraag nu erkent dat de standaard agnostisch is, zou je je antwoord moeten aanpassen om te passen. DUS vragen en antwoorden zijn voor iedereen, voor altijd, niet alleen voor de vrager op dit moment.
toegevoegd de auteur Jim Balter, de bron
1. Ik waardeer je inspanningen.
toegevoegd de auteur Jim Balter, de bron
P.P.S. Normaal gesproken zijn getekende tekens een slechte keuze, en de meeste talen die ze als numerieke waarden behandelen, kiezen anders (en bieden soms een byte-type met handtekening).
toegevoegd de auteur Jim Balter, de bron
"er was melding gemaakt van ASCII in de oorspronkelijke vraag" - Natuurlijk weet ik dat, Greg, want zoals je weet heb ik het gelezen, en ik zei "huidige" vraag. Wat de rest betreft, ben ik het daar niet mee eens, maar dat is het leven. Heb een goede en stop met alles zo persoonlijk te nemen.
toegevoegd de auteur Jim Balter, de bron
De C-standaard is agnostisch over de tekenset. De enige vermelding van ASCII is met betrekking tot trigraphs, die betrekking hebben op de bronkarakterset, niet op de uitvoeringstekenset. char sets kunnen alle 8 bits gebruiken.
toegevoegd de auteur Jim Balter, de bron
Ik zie niet waar je een verkeerde premisse hebt gevonden in de vraag. De vraag is eigenlijk heel geldig, en het antwoord heeft te maken met geschiedenis. Als de PDP-11 instructies had die bytes als niet-ondertekende waarden behandelden, dan waren de tekens niet ondertekend en zou er veel minder buggy-code zijn met betrekking tot chars (bijv. Elke call van de ctype.h is ... of ... functies hebben een char doorgegeven).
toegevoegd de auteur Jim Balter, de bron
@JimBalter, er was ASCII vermeld in de oorspronkelijke vraag, en je zou ontzettend veel in mijn antwoord moeten lezen om het te interpreteren als een algemene uitspraak over C's basistekenset. Verder denk ik dat mijn verklaring met betrekking tot ASCII waarde toevoegt, nog steeds relevant is voor de vraag zoals die nu is geformuleerd, en is AFAIK volledig feitelijk.
toegevoegd de auteur Greg E., de bron
@JimBalter, de vraag is sindsdien aangepast, maar de titel en de oorspronkelijke inhoud leken te veronderstellen dat char standaard wordt gedefinieerd als een ondertekend type, wat niet het geval is. Zoals anderen hebben uitgelegd, is char anders dan bijv. int , omdat de ISO C-standaard niet het standaardteken opgeeft, dus char en signed char zijn aparte entiteiten voor zover het de standaard betreft. Dat is iets dat in eerdere vragen over SO is behandeld en dat ook gemakkelijk kan worden beantwoord door een eenvoudige Google-zoekopdracht. Er was ook een verwijzing naar ASCII in de oorspronkelijke vraag, die ik heb aangehaald.
toegevoegd de auteur Greg E., de bron
@JimBalter, verder, terwijl de vraag wat meer geldig is zoals momenteel staat vermeld, is deze ook gedeeltelijk subjectief ("waarom zou het ooit logisch zijn om strings als ondertekende bytes te behandelen?" Luidt als een vraag die om een ​​normatieve reactie vraagt, geen les in gegevensverwerkingsgeschiedenis, maar misschien is dat slechts mijn indruk).
toegevoegd de auteur Greg E., de bron
@JimBalter, bedankt. Trouwens, ik geloof nog steeds dat je een apart antwoord op het OP moet formuleren. U hebt deskundige kennis van dit domein en de informatie die u heeft verstrekt, mag niet verborgen blijven in de onduidelijke diepten van een gedeelte met opmerkingen.
toegevoegd de auteur Greg E., de bron
@JimBalter, natuurlijk, ik zal mijn antwoord bijwerken.
toegevoegd de auteur Greg E., de bron
@JimBalter, reageert de huidige versie van mijn antwoord op uw bezorgdheid over: ASCII?
toegevoegd de auteur Greg E., de bron
@JimBalter, je hebt gelijk, ik zal proberen wat context toe te voegen aan mijn statement re: ASCII, in het licht van de verandering in de vraag van het OP. Bedankt en excuses voor mijn argumentatieve toon.
toegevoegd de auteur Greg E., de bron
@JimBalter, waarom zouden deze opmerkingen niet samenvallen in een reactie op het OP?
toegevoegd de auteur Greg E., de bron
@JimBalter, het OP heeft specifiek gewezen op de ASCII-tekenset in relatie tot de ondertekende char die ik probeerde aan te pakken en ik heb geen uitspraken gedaan over ASCII w/r/t eventuele C-normen. Heb ik je op de een of andere manier beledigd, of is dat mijn verbeelding? Ik herhaal, aangezien u duidelijk veel over het onderwerp te zeggen heeft, zou u een volledig antwoord op het OP willen geven in plaats van mij te overspoelen met opmerkingen. Dat is nuttiger voor iedereen.
toegevoegd de auteur Greg E., de bron

Jim Balter merkt in een opmerking dat op

De instructies op de PDP-11 die bytes behandelen, behandelden ze als gesigneerde hoeveelheden, dus dat is hoe de vroege C-compilers ze behandelden, en unsigned bestond niet eens.

I strongly suspect that this is the answer to why the default character type char isn’t required to be unsigned, but one would need a quote from some written historical account in order to be sure.

As to why it isn’t required to be signed either (!), on a non-two's complement machine such as (the only one I know that's possibly still in use) a Clearpath Dorado, a signed char cannot hold all values of an unsigned char, since it's wasting one bitpattern on a negative zero, or whatever that bitpattern is put to use for. If char were required to be signed then this would be a problem for reinterpreting general data as a sequence of char value. Consequently, on such a machine char has to be unsigned, or else the software will have to be engaging in extreme contortions to deal with it.

5
toegevoegd
@Alex: veel mensen lijken verschillende aspecten van de standaard met een bizarre achting te beschouwen. Veel van de flexibiliteit die de standaard toestaat, was bedoeld om te voorkomen dat bestaande ongebruikelijke implementaties zouden veranderen op manieren die ze minder bruikbaar zouden kunnen maken voor de doeleinden waarvoor ze al dienstbaar waren (en waren dus duidelijk geschikt), en niet om compilers uit te nodigen om creatief te worden bij het genereren van code voor alledaagse platforms.
toegevoegd de auteur supercat, de bron
@ Cheersandhth.-Alf: Op processors met registers die groter zijn dan bytes, hebben sommigen instructies voor "load byte in een woordenregister met nul opvulling", sommige hebben "load byte in woordregister met tekenuitbreiding", sommige hebben "load byte in een deel van het woordregister, waardoor de rest onaangetast blijft ", en sommige hebben twee of meer van de bovenstaande. Voor verwerkers die slechts een van de eerste twee formulieren hebben, zou ik die vorm beschouwen als de 'promotieregel' die wordt gebruikt voor assembleertaal die op die processors is gericht.
toegevoegd de auteur supercat, de bron
Gewoon een disclaimer: het is ~ 30 jaar geleden dat ik voor het laatst een PDP-11-assembler heb aangeraakt en ik kan me niet echt herinneren hoe het met bytes omging, of als het wel b.v. single-byte vermenigvuldiging en verdeling, d.w.z. of het klopt om te zeggen dat het bytes behandelde als getekende hoeveelheden. Mijn vermoeden is dus volledig gebaseerd op het idee dat @ Jim Balter weet waar hij het over heeft en dat het niet ver gezocht lijkt. Ik ken niet langer de PDP-11-dingen uit mijn eigen herinnering (zo ongeveer alles wat ik me herinner is dat de PDP-11-assembly @ signs betrof, en de registers waren genummerd en geheugen in kaart gebracht).
toegevoegd de auteur Cheers and hth. - Alf, de bron
@AProgrammer: er zijn geen promotieregels in de assembler. wanneer we het hebben over assembleertaal, of liever, machinecodevoorschriften, hebben we het over wat handig is voor het genereren van een compilercode, en in het bijzonder voor een compiler op het moment dat de C-taal werd gevormd, die in het begin en midden jaren 70, terwijl de eerste C-standaard in 1989 verscheen. de niet-negatieve aanname voor char waarden is aanwezig in een aantal standaard bibliotheekfuncties zoals isupper (waar het rist nieuwelingen).
toegevoegd de auteur Cheers and hth. - Alf, de bron
@Alf, promotieregels verzekeren dat alle berekeningen worden gemaakt op int, niet op short of char. Dus enige vraag is, is het makkelijker om verlenging te tekenen of een teken uit te breiden naar een int. (Over de noodzaak om char toe te staan ​​om niet ondertekend te worden, zelfs als alleen C ++ de vereiste uitdrukt dat tekens in de basisset niet-negatief zijn, ben ik er vrij zeker van dat het overeenkomt met de praktijk voor C en implementaties die toestaan ​​dat EBCDIC char ongecodeerd heeft).
toegevoegd de auteur AProgrammer, de bron
@ Cheersandhth.-Alf: Ik ben dol op de historische kijk op je post en soms zouden computerwetenschapslessen zoveel meer ruimte in beslag nemen, als mensen erop zouden wijzen dat bepaalde dingen werken zoals ze dat doen vanwege de geschiedenis en niet vanwege de logica.
toegevoegd de auteur Alex, de bron

Anderen zijn ingegaan op de historische redenen waarom het zo is geweest toen C voor het eerst werd bedacht en (later) werd gestandaardiseerd, maar er is nog een reden waarom deze schijnbare anomalie tot op de dag van vandaag voortduurt.

Het is gewoon dat wanneer u char voor tekens gebruikt, u niet nodig heeft om te weten of het is ondertekend of niet ondertekend. De standaardbibliotheek biedt draagbare functies voor het werken met tekens, ongeacht hun weergave. Als je die functies negeert en erop staat vergelijkingen en rekenkundige bewerkingen met personages te doen, verdien je elke bug die je krijgt.

To take a simple example, it's quite commonplace to check whether a character is printable using the expression c >= ' ' or equivalently c >= 0x20, but you should just use isprint(c) instead. That way, you're not exposing yourself to signed/unsigned confusion and potentially introducing platform-dependent errors into your program.

Zodra u de gewoonte hebt aangenomen om signed char en unsigned char te gebruiken als kleine (meestal 8-bit) gehele getallen voor rekenen, en u gebruikt alleen char wanneer u werkt met tekengegevens, lijkt het volkomen natuurlijk dat char een apart type is met door implementatie gedefinieerde ondertekening, en zelfs nog natuurlijker dat tekenreeksverwerkingsfuncties altijd char gebruiken en char * in plaats van de ondertekende of niet-ondertekende varianten. De ondertekening van char lijkt ongeveer net zo relevant als de ondertekening van bool .

2
toegevoegd
-1 het bovenstaande is niet correct. de C-standaard vereist dat het argument voor een classificatiefunctie niet-negatief moet zijn of anders EOF. Om deze functies correct te gebruiken, moet het daadwerkelijke argument dus worden gecast naar unsigned char . anders hebt u formeel ongedefinieerd gedrag voor niet-ASCII-tekens. en b.v. de visuele c ++ debug runtime-bibliotheek vangt dit op voor sommige functies, en (hoewel het programma zou werken als niet voor dit!) crasht uw programma ...
toegevoegd de auteur Cheers and hth. - Alf, de bron

Zoals Bjarne zei in De C ++ programmeertaal , of een char wordt genomen als ondertekend of niet ondertekend, is afhankelijk van de implementatie en de C ++ -taal biedt twee typen voor elke implementatie.

2
toegevoegd

Char is neither signed nor unsigned by standard. See https://stackoverflow.com/a/2054941/396583

0
toegevoegd
@ JohannesSchaub-litb ideone.com/FZ2Ms erkent het verschil tussen de char en integrale types. Dus je zou in staat moeten zijn om functies apart te overladen voor (gewoon) char en (u) int8_t aka (un) ondertekende char , nee? Volledig off-topic, maar hey, mild relevant :)
toegevoegd de auteur rubenvb, de bron
Correctie: char is ondertekend of niet ondertekend (maar het is een ander type van zowel signed char en unsigned char ) .
toegevoegd de auteur Keith Thompson, de bron
@alex nee het is niet in tegenspraak met het eerste deel. Er is geen formele definitie van ondertekend type , dus mensen en ook de standaard zelf beschouwen dit als "type dat negatieve waarden kan vertegenwoordigen". Maar er is een definitie van integraal type ondertekend waarin alle soorten expliciet worden vermeld. char en bool zijn niet in die lijst opgenomen. U zult zien dat numeric_limits :: is_signed opbrengsten waar zijn en het wordt gespecificeerd door "T is ondertekend".
toegevoegd de auteur Johannes Schaub - litb, de bron
@keith het is een beetje gecompliceerd in C ++ (zeker niet als het op dezelfde manier is in c). in c ++ char is ofwel ondertekend of niet ondertekend en het is een integer type. maar het is niet een ondertekend integer type en ook niet een unsigned integer type. Dus je moet heel voorzichtig zijn met hoe je specifieke regels noemt.
toegevoegd de auteur Johannes Schaub - litb, de bron
@ JohannesSchaub-litb Hoe komt het dat char is ondertekend of niet ondertekend en het is een integer type. maar het is geen integer type met teken of een niet-ondertekend integer type ? Het laatste deel is in tegenspraak met het eerste deel?
toegevoegd de auteur Alexey Frunze, de bron

Waarom gebruiken de meeste stringfuncties in de C/C ++ -stdlibs char * -aanwijzingen?

In C ++ gebruik je std :: string. In C waren de gebruikspatronen al te gevestigd toen niet-ondertekende typen werden geïntroduceerd en ik een efficiëntiegevoel niet zou uitsluiten.

geen betekenisvolle tekenwaarden onder nul

Welnu, er is ergens een beperking in de C ++ -standaard dat tekens in de basis tekenset positief zijn. Maar het is naïef om te denken dat die beperking geldt voor alle personages.

Die beperking dwingt implementaties waardoor EBCDIC als coderingssysteem hun char ongecodeerd kan hebben.

De meeste moderne compilers (GCC, MSVC) behandelen tekens als standaard ondertekend.

gcc-gedrag is afhankelijk van het doel en heeft opties om de standaardwaarde van het doel te wijzigen.

0
toegevoegd
"er zit ergens een beperking in de standaard dat tekens in de basis tekens positief zijn" - Nee, er is geen beperking. De enige contraint (naast het toevoegen van een minimale reeks tekens en de gehele getallen die aan elkaar grenzen) is dat ze in een byte passen.
toegevoegd de auteur Jim Balter, de bron
Sorry, ik was vergeten dat dit zowel C als C ++ was getagd en dat u zowel C ++ als C had geadresseerd. Bedankt voor de kwalificatie. De C-standaard zegt wel dat de argumenten voor de ctype-functies moeten worden gerepresenteerd als niet-ondertekende tekens (of EOF), wat in de praktijk kan worden opgevat als implicerend dat de tekenset positief is.
toegevoegd de auteur Jim Balter, de bron
Ik denk dat het een semantische klacht is. Zeker chars met de 8ste bitreeks hebben negatieve waarden als ze worden opgeslagen in een ondertekend teken, of char geïmplementeerd als ondertekend, maar dat betekent niet dat de tekenset negatieve waarden bevat; de API (getc en ctype) houdt anders in. Ik denk dat de C ++ -beperking dit verduidelijkt.
toegevoegd de auteur Jim Balter, de bron
@JimBalter, zie C ++ 1998, 2.2/3, C ++ 2011 2.3/3 (het gebruikt niet-negatief, uiteraard heeft \ 0 een nulwaarde) maar ik heb in mijn archieven opgemerkt dat ik het niet heb gevonden corresponderende beperking in de C-normen (deze notitie dateert van vóór C11 dus ik heb daar niet gezocht, maar ik heb waarschijnlijk gekeken in C90 en C99; het staat niet in 5.2.1/3 in C11, wat het directe equivalent is van 2.3/3 in C ++ 11). Ik heb een kwalificatie toegevoegd.
toegevoegd de auteur AProgrammer, de bron
@JimBalter, ik denk het niet. ctype-functies nemen voor argument een int die ofwel EOF of een char-cast is naar unsigned char (wat precies is wat getc als resultaat geeft). Ik heb ze gebruikt met Latin1 locale op implementaties met een ondertekende char, dus met negatieve karakters.
toegevoegd de auteur AProgrammer, de bron