2010-03-06 10 views
19

Die strchr-Funktion in der C-Standardbibliothek sucht nach einer char in einer Zeichenfolge, aber ihre Signatur nimmt eine int für das Suchzeichen. In diesen beiden Ausführungen fand ich, wirft die Umsetzung dieses int auf ein char:Warum nimmt strchr einen int, damit der char gefunden wird?

char *strchr(const char *s, int c) { 
    while (*s != (char)c) 
     if (!*s++) 
      return 0; 
    return (char *)s; 
} 

char *strchr(const char *s, int c) { 
    while (*s && *s != (char)c) 
     s++; 
    if (*s == c) 
     return (char *)s; 
    return NULL; 
} 

Weiß jemand, warum? Warum nicht einfach einen char als Parameter nehmen?

+0

gleichen Grund dafür, warum [Memset einen int nehmen statt von einem char] (https://stackoverflow.com/q/5919735/995714) –

Antwort

34

Die Gründe dafür sind rein historischer Natur. Beachten Sie, dass in den alten Tagen der C-Sprache (K & R C) gab es keine Funktion Prototyp. Eine strchr Funktion in jenen Zeiten werden würde als

erklärt
char *strchr(); 

und in K & R Stil als

char *strchr(s, c) 
    char *s; 
    char c; 
{ 
    /* whatever */ 
} 

jedoch in der Sprache C (in K & RC und in dem modernen als auch) definiert Wenn die Funktion ohne einen Prototyp deklariert wird (wie oben gezeigt), werden die Parameter, die in jedem Funktionsaufruf übergeben werden, sogenannten Standardargument-Promotions unterworfen. Unter Default-Argument-Promotions wird jeder Integraltyp kleiner als int (oder unsigned int) immer in int (oder unsigned int) konvertiert. I.e. Wenn die Parameter nicht deklariert sind und Sie einen Wert von char als Argument übergeben, wird dieser Wert implizit in konvertiert und tatsächlich physisch als int übergeben. Das gleiche gilt für short. (BTW, float wird standardmäßig in Argument-Promotions in double konvertiert). Wenn innerhalb der Funktion der Parameter tatsächlich als char deklariert ist (wie in der obigen R-Stildefinition K &), wird implizit in char konvertiert und als char innerhalb der Funktion verwendet. So funktionierte es in K & R-Zeiten, und so funktioniert es bis heute in modernem C, wenn die Funktion keinen Prototyp hat oder wenn variadische Parameter verwendet werden.

Nun, Stichwort in der modernen C, die Funktionsprototypen hat und verwendet moderne Funktionsdefinition Syntax. Um die "traditionelle" Funktionalität von strchr, wie oben beschrieben, zu bewahren und zu reproduzieren, haben wir keine andere Wahl, als den Parameter als int zu deklarieren und innerhalb der Funktion explizit in char umzuwandeln. Dies ist genau das, was Sie in dem von Ihnen zitierten Code beobachten. Dies ist genau so, wie die Funktionalität von strchr in der Norm beschrieben ist.

Außerdem, wenn Sie haben eine bereits kompilierte Legacy-Bibliothek, wo strchr in K & R Stil definiert ist, wie oben gezeigt, und Sie sich entschieden moderne Prototypen für die Bibliothek zur Verfügung zu stellen, die richtige Erklärung für strchr wäre

char *strchr(const char *s, int c); 

weil int ist, was die obige Legacy-Implementierung physisch als c erhalten erwartet. Die Angabe mit einem char Parameter wäre falsch.

Aus diesem Grund werden Sie nie "traditionelle" Standardbibliotheksfunktionen sehen, die Parameter des Typs char, short oder float erwarten. Alle diese Funktionen werden stattdessen mit Parametern des Typs int oder double deklariert.

Ein sehr ähnliches Grundprinzip ist hinter der Standardgarantie, dass Zeichenzeiger und void * Zeiger die gleichen Darstellungs- und Ausrichtungsanforderungen teilen. Mit dieser Garantie können Sie malloc als void *-Returning-Funktion deklarieren und diese Deklaration dann mit einer vorkompilierten Legacy-Version der Standardbibliothek verwenden, wobei malloc tatsächlich char * zurückgegeben wird.


Referenz: die C99 Gründe, Version 5,10

7.1.4 Verwendung von Bibliotheksfunktionen
/-/
Alle Bibliothek Prototypen werden in Bezug auf die „erweitert“ Typen angegeben: Ein Argument, das früher als char deklariert wurde, wird jetzt als int geschrieben. Diese stellt sicher, dass die meisten Bibliotheksfunktionen können mit oder ohne Prototyp in Umfang genannt werden, so dass die Kompatibilität mit Code pre-C89

Aufrechterhaltung rückwärts
+0

Ok. Vielen Dank für die ausführliche Antwort. Im Grunde scheint es sich um Altlasten/historische Präzedenzfälle zu handeln. Ich bin nicht mehr neugierig ... –

+0

es ist kein historischer Grund - es hat mit der Effizienz der Implementierung von wie Parameterübergabe für Funktionsaufrufe implementiert ist, und wie die Standardregeln für die Typ-Promotion für Ausdrücke arbeiten. Es ist alles noch heute sehr relevant. Die obige Antwort irreführend. Das abstrakte Maschinenmodell für C macht aus einem Grund "int" die optimale Größe, und kleinere Objekte werden am besten behandelt, indem sie zu "int" hochgestuft werden. Es sollte immer darauf geachtet werden, Funktionsparameter mit ihren natürlich beworbenen Typen zu deklarieren, _nicht_ irgendeinem schmaleren Typ, mit dem man sie oft bezeichnen könnte. –

+1

@Greg A. Woods: Das vermisst völlig den Punkt. Die prototypenlosen Deklarationen von Standardfunktionen in K & R C und ihr Verhalten gegenüber kleineren Typen sind unbestrittene historische Fakten. Die Gründe, warum dies in K & R so gemacht wurde (Effizienz oder etwas anderes), sind interessant, aber im Zusammenhang mit der Frage irrelevant. Die Antwort auf die ursprüngliche Frage ist wiederum, dass, als das Konzept des "Prototyps" der Sprache hinzugefügt wurde, die Prototypen für Standardfunktionen gezwungen wurden, kleinere Typen zu vermeiden, um das bereits etablierte Vermächtnisverhalten zu bewahren. Das ist alles. – AnT

3

In c ist der Typ eines Zeichenliterals int. Zum Beispiel: 'a' ist vom Typ int.

+5

Wahr. Und? ... :) – AnT

+1

Wenn der Prototyp explizit nach einem Zeichen fragt, müsste man 'a' explizit in ein Zeichen schreiben: '(char) 'a''. Das ist natürlich nicht optimal. – Spidey

+0

weiterhin, selbst wenn Sie '(char) 'a'' schreiben, können Sie immer noch mit dem Ergebnis dieses Ausdrucks zu" int "befördert werden, und dann möglicherweise nur wieder in den im Prototyp angegebenen Typ zurückkonvertiert werden (Implementationen können die üblichen Integer-Promotions für Funktionsparameter ausführen, selbst wenn ein Prototyp im Umfang ist, um die Aufrufsequenz zu optimieren). Für Variadic-Funktionen werden die Standard-Argument-Promotions für alle nachfolgenden Argumente ausgeführt. Es ist auch _nicht _ ein _Fehler_, um keinen Prototyp im Umfang zu haben, und die Standardargumentpromotionen werden wieder ausgeführt. –

3

Ich denke, das kann nichts mehr als ein Zufall der Geschichte zugeschrieben werden. Sie sind genau richtig, char scheint der offensichtliche Datentyp für das gesuchte Zeichen zu verwenden.

In einigen Situationen in der C-Bibliothek, wie der getc()-Funktion, wird ein int Wert für das gelesene Zeichen vom Eingang zurückgegeben. Dies ist kein char, da ein extra Nicht-Zeichen-Wert (EOF, normalerweise -1) zurückgegeben werden kann, um das Ende des Zeichenstroms anzuzeigen.

Der EOF Fall gilt nicht für die strchr() Funktion, aber sie können nicht wirklich zurückgehen und die Deklaration der Funktion in der C-Bibliothek jetzt ändern.

+0

Das hilft auch - danke –

-2

int c ist das Zeichen, das Sie suchen möchten. Das Zeichen wird als Ganzzahl übergeben, aber tatsächlich werden nur die unteren 8 Bits durchsucht.

char *strchr(const char *s, int c){ 
    while (*s != (char)c) 
     if (!*s++) 
      return 0; 
    return (char *)s; 
} 

Wie man dort sehen kann eine cast von int c zu (char)c ist: Es sollte daher ein

Die strchr Funktion sieht wie folgt aus char übergeben werden.

Nun zu Ihrer Frage zu beantworten, wird Ihre char ch in eine Ganzzahl int c konvertiert und als Ordinalwert eines Zeichens angewendet.

So sollte das folgende Programm in Ordnung sein:

#include<stdio.h> 
#include<string.h> 

int main(void){ 
    char *name = "Michi"; 
    int c = 99; /* 99 is the ANSCI code of c*/ 

    char *ret = strchr(name, c); 

    printf("String after %s\n", ret); 
    return 0; 
} 

Aber die folgenden nicht:

#include<stdio.h> 
#include<string.h> 

int main(void){ 
    char *name = "Michi"; 
    char c = '99'; /* 99 is the ANSCI code of c*/ 

    char *ret = strchr(name, c); 

    printf("String after %s\n", ret); 
    return 0; 
} 

Wegen multi-character character constant das ist overflow in implicit constant conversion

+0

Ich würde gerne wissen, warum ich immer wieder ohne eine Erklärung abstimmen werde. – Michi

Verwandte Themen