c++ - sont - ti connect ce



Dois-je lancer un jeton sans signe avant d'appeler toupper? (4)

Il y a quelque temps, quelqu'un avec une réputation élevée sur StackOverflow a écrit dans un commentaire qu'il est nécessaire de lancer un argument char à unsigned char avant d'appeler std::toupper (et des fonctions similaires).

Par ailleurs, Bjarne Stroustrup ne mentionne pas la nécessité de le faire dans le langage de programmation C ++. Il utilise simplement le toupper comme

string name = "Niels Stroustrup";

void m3() {
  string s = name.substr(6,10);  // s = "Stroustr up"
  name.replace(0,5,"nicholas");  // name becomes "nicholas Stroustrup"
  name[0] = toupper(name[0]);   // name becomes "Nicholas Stroustrup"
} 

(Cité dudit livre, 4ème édition.)

La référence indique que l'entrée doit être représentable en tant que caractère unsigned char . Pour moi, cela ressemble à tous les caractères car char et unsigned char ont la même taille.

Donc, cette distribution est-elle inutile ou Stroustrup est-il négligent?

Edit: Le manuel libstdc ++ mentionne que le caractère en entrée doit provenir du jeu de caractères source de base , mais pas être lancé. Je suppose que cela est couvert par la réponse de @Keith Thompson, ils ont tous une représentation positive en tant que caractère signed char et caractère unsigned char ?


Answer #1

Au lieu de convertir l'argument en caractère non signé, vous pouvez convertir la fonction. Vous devrez inclure un en- tête fonctionnel . Voici un exemple de code:

#include <string>
#include <algorithm>
#include <functional>
#include <locale>
#include <iostream>

int main()
{
    typedef unsigned char BYTE; // just in case

    std::string name("Daniel Brühl"); // used this name for its non-ascii character!

    std::transform(name.begin(), name.end(), name.begin(),
            (std::function<int(BYTE)>)::toupper);

    std::cout << "uppercase name: " << name << '\n';
    return 0;
}

La sortie est la suivante:

uppercase name: DANIEL BRüHL

Comme prévu, toupper n'a aucun effet sur les caractères non-ascii. Mais ce casting est bénéfique pour éviter les comportements inattendus.


Answer #2

En C, toupper (et de nombreuses autres fonctions) int même si vous vous attendez à ce qu'elles prennent des caractères. De plus, char est signé sur certaines plates-formes et non signé sur d'autres.

Le conseil pour lancer un toupper sans toupper avant d'appeler toupper est correct pour C. Je ne pense pas que cela soit nécessaire en C ++, à condition que vous lui passiez un int qui est dans l'intervalle. Je ne trouve rien de spécifique sur la nécessité en C ++.

Si vous voulez éviter le problème, utilisez le toupper défini dans <locale> . C'est un modèle et prend n'importe quel type de caractère acceptable. Vous devez également lui passer un std::locale . Si vous n'avez aucune idée de la région à choisir, utilisez std::locale("") , qui est supposé être le paramètre régional préféré de l'utilisateur:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <locale>
#include <string>

int main()
{
    std::string name("Bjarne Stroustrup");
    std::string uppercase;

    std::locale loc("");

    std::transform(name.begin(), name.end(), std::back_inserter(uppercase),
                   [&loc](char c) { return std::toupper(c, loc); });

    std::cout << name << '\n' << uppercase << '\n';
    return 0;
}

Answer #3

Malheureusement, Stroustrup était négligent :-(
Et oui, les codes des lettres latines doivent être non négatifs (et aucun plâtre n'est requis) ...
Certaines implémentations fonctionnent correctement sans faire appel à des caractères non signés ...
Par l'expérience, il peut falloir plusieurs heures pour trouver la cause du défaut de segmentation d'un tel toupper (quand on sait qu'un segfault est là) ...
Et il y a aussi isupper, islower etc.


Answer #4

Oui, l'argument de toupper doit être converti en caractères unsigned char pour éviter le risque de comportement indéfini.

Les types char , signed char et unsigned char sont trois types distincts. char a la même plage et la même représentation que le caractère signed char ou le caractère unsigned char . (Le caractère simple est très couramment signé et peut représenter des valeurs comprises entre -128 et +127.)

La fonction toupper prend un argument int et retourne un résultat int . Citant la norme C, section 7.4, paragraphe 1:

Dans tous les cas, l'argument est un int dont la valeur doit être représentable sous la forme d'un caractère unsigned char ou doit être égale à la valeur de la macro EOF . Si l'argument a une autre valeur, le comportement est indéfini.

(C ++ incorpore la majeure partie de la bibliothèque standard C et diffère sa définition au standard C.)

L'opérateur d'indexation [] sur std::string renvoie une valeur de caractère. Si le caractère brut est un type signé et si la valeur renvoyée par le name[0] est négative, alors l'expression

toupper(name[0])

a un comportement indéfini.

Le langage garantit que, même si le caractère simple est signé, tous les membres du jeu de caractères de base ont des valeurs non négatives.

string name = "Niels Stroustrup";

le programme ne risque pas un comportement indéfini. Mais oui, en général, une valeur de caractère transmise à toupper (ou à l'une des fonctions déclarées dans <cctype> / <ctype.h> doit être convertie en caractère unsigned char , de sorte que la conversion implicite en int ne produise pas de résultat négatif). valeur et provoquer un comportement indéfini.

Les fonctions <ctype.h> sont généralement implémentées à l'aide d'une table de consultation. Quelque chose comme:

// assume plain char is signed
char c = -2;
c = toupper(c); // undefined behavior

peut indexer en dehors des limites de cette table.

Notez que la conversion en unsigned :

char c = -2;
c = toupper((unsigned)c); // undefined behavior

n'évite pas le problème. Si int est de 32 bits, la conversion de la valeur de caractère -2 en rendements unsigned 4294967294 . Ceci est implicitement converti en int (le type de paramètre), ce qui donne probablement -2 .

toupper peut être implémenté de manière à ce qu'il se comporte de manière sensible pour les valeurs négatives (acceptant toutes les valeurs de CHAR_MIN à UCHAR_MAX ), mais il n'est pas nécessaire de le faire. De plus, les fonctions de <ctype.h> doivent accepter un argument avec la valeur EOF , qui est généralement -1 .

Le standard C ++ apporte des ajustements à certaines fonctions de la bibliothèque standard C. Par exemple, strchr et plusieurs autres fonctions sont remplacées par des versions surchargées qui imposent la correction de const . Il n'y a pas de tels ajustements pour les fonctions déclarées dans <cctype> .





c++