Waarom C-strings traag zijn en hoe pointers ze repareren

10

C geeft niets om snaren. Niet echt.

Het behandelt ze als reeksen karakters. Alleen bytes in het geheugen. Als je ze effectief wilt gebruiken, heb je aanwijzingen nodig. Niet omdat het leuk is. Maar omdat je zonder hen extra werk doet.

Een string in C is gewoon char str[100]. Dat lijkt ruimte voor 100 tekens. Dat is het niet. Er is ruimte voor 99 tekens plus een terminator. C gebruikt null-beëindigde tekenreeksen. Elke string eindigt met de ASCII-waarde 0. Geschreven als '\0'.

Dit verandert alles.

Andere talen verwerken tekenreeksen anders. Pascal gebruikt een lengtebyte. Het weet precies hoeveel tekens er zijn opgeslagen. Vraag naar de lengte? Het retourneert die byte. Direct.

C moet tellen. Het leest totdat het '\0' bereikt. Dit maakt C in sommige gevallen langzamer. Bij anderen sneller. Het hangt af van wat je doet.

Er is geen ingebouwde tekenreeksondersteuning in C. U vertrouwt op bibliotheken. verwerkt invoer en uitvoer zoals gets en puts. verzorgt de manipulatie. Sommige systemen gebruiken . Je moet het geheugen zelf beheren. U kunt niet zomaar de ene array aan de andere toewijzen.

Dat is alles. Je kopieert element voor element. Of je gebruikt strcpy. De bibliotheekfunctie doet het zware werk.

strcpy is overal in C aanwezig. Het initialiseert strings. Het kopieert gegevens.

Nadat dit is uitgevoerd, bevat s1 “hallo” en s2 “hallo”. De array slaat ASCII-waarden op. gehele getallen. h is 104. e is 101. C denkt in bytes. Je denkt in tekst. Het maakt de machine niet uit.

Vergelijking maakt gebruik van strcmp. Het retourneert een geheel getal.

Nul betekent gelijk. Negatief betekent dat de eerste string minder is. Positief betekent dat het groter is.

Er bestaan andere functies. strlen retourneert lengte. strcat wordt samengevoegd. Lees de manpagina als je meer nodig hebt.

Maar hoe werken deze functies eigenlijk onder de motorkap? Laten we eens kijken naar strlen.

Een naïeve benadering ziet er als volgt uit:

De meeste C-programmeurs haten dit. Het lijkt inefficiënt. Zij geven de voorkeur aan aanwijzingen.

Je kunt het verder comprimeren.

Een echte expert zou het waarschijnlijk korter kunnen maken.

Ik heb deze gecompileerd op een MicroVAX met gcc. Geen optimalisatie. Elke 20.000 keer uitgevoerd op een reeks van 120 tekens.

Eerste versie: 12,3 seconden.
Tweede versie: 12,3 seconden.
Derde versie: 12,9 seconden.

Aanwijzers winnen niet altijd.

Schrijf code die u begrijpt. Leesbaarheid is belangrijker dan een paar microseconden. Tenzij je in een krappe lus zit. Dan misschien optimaliseren.

strcpy volgt een soortgelijke evolutie.

Begin met het voor de hand liggende:

Let op de <=. Het kopieert de '\0'. Als je het overslaat, heeft de reeks geen einde. Onbekende lengte. Bugs later. Moeilijk te vinden.

Deze versie is inefficiënt. strlen voert elke iteratie uit. Bel het een keer.

Nu aanwijzingen.

Comprimeer het.

Schoon. Snel. Gevaarlijk als je de grenzen niet controleert.

Dat is hoe C-snaren werken. Geen magie. Alleen geheugen en aanwijzingen. En heel voorzichtig tellen.

Pointers versus prestaties in strcpy

Technisch gezien zou je while (s1++ = s2++); kunnen schrijven om het kopiëren van tekenreeksen af te handelen. De prestatiekloof tussen naïeve implementaties en geoptimaliseerde implementaties is verbluffend.

Neem strcpy. De eerste versie heeft 415 seconden nodig om een ​​string van 120 tekens 10.000 keer te kopiëren. De tweede versie? 14,5 seconden. De derde zakt naar 9,8 seconden. De vierde komt neer op 10,3 seconden.

Dat is geen marginaal verschil. Het is een enorme impuls. Pointers zorgen hier voor de snelheid omdat ze onnodige overhead vermijden.

Retourtypen en tekenreeksaanwijzers

Het prototype voor strcpy in de stringbibliotheek onthult zijn bedoeling:

De meeste stringfuncties retourneren een pointer naar een string. strcpy retourneert de waarde van s1 als resultaat. Dit maakt het mogelijk om bewerkingen aan elkaar te koppelen of onmiddellijk gebruik te maken van de gekopieerde string zonder een tweede zoekopdracht.

Voorloopspaties verwijderen zonder gegevens te verplaatsen

Het gebruik van pointers met strings resulteert vaak in duidelijke snelheidsverbeteringen. U kunt hiervan profiteren als u er een beetje over nadenkt.

Stel dat u voorloopspaties uit een tekenreeks wilt verwijderen. Het instinct is om karakters te verschuiven en de lege plekken te overschrijven. In C kun je de beweging helemaal vermijden.

Dit is veel sneller dan de bewegingstechniek. Vooral voor lange snaren. Je verschuift geen bytes. Je verandert alleen de startaanwijzer. De gegevens blijven staan. De uitvoer verandert.

Trucs oppakken

Gaandeweg zul je nog veel meer trucs met snaren leren. Je zult andere code lezen. Je zult zien hoe anderen met het geheugen omgaan.

Oefenen is de sleutel. Er is geen snelkoppeling voor het lezen van code. Je hoeft alleen maar te kijken hoe het wordt gedaan en te begrijpen waarom het werkt. De prestatiewinst is reëel. De leercurve is steil. Maar de resultaten spreken voor zich.