strtoupper - toupper() c++
¿Necesito lanzar a char sin firmar antes de llamar a toupper? (5)
Hace un tiempo, alguien con una gran reputación aquí en StackOverflow escribió en un comentario que es necesario lanzar un argumento de char
a unsigned char
antes de llamar a std::toupper
(y funciones similares).
Por otro lado, Bjarne Stroustrup no menciona la necesidad de hacerlo en C ++ - Lenguaje de programación. Él solo usa toupper
como
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"
}
(Citado de dicho libro, 4ª edición).
La referencia dice que la entrada debe ser representable como unsigned char
. Para mí, esto suena como válido para cada char
ya que char
y unsigned char
tienen el mismo tamaño.
Entonces, ¿este lanzamiento es innecesario o Stroustrup fue descuidado?
Editar: El manual de libstdc ++ menciona que el carácter de entrada debe ser del conjunto de caracteres fuente básico , pero no se emite. Supongo que esto está cubierto por la respuesta de @Keith Thompson, todos tienen una representación positiva como signed char
y unsigned char
.
En C, toupper
(y muchas otras funciones) toman int
s aunque esperas que tomen char
. Además, char
está firmado en algunas plataformas y sin firmar en otras.
El consejo de enviar a unsigned char
antes de llamar a toupper
es correcto para C. No creo que sea necesario en C ++, siempre que lo pases por un No puedo encontrar nada específico sobre si es necesario en C ++. int
que esté dentro del rango.
Si desea eludir el problema, use el toupper
definido en <locale>
. Es una plantilla y toma cualquier tipo de carácter aceptable. También debe pasarlo por std::locale
. Si no tiene idea de qué configuración regional elegir, use std::locale("")
, que se supone que es la configuración regional preferida del usuario:
#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;
}
En lugar de lanzar el argumento como char sin signo, puede lanzar la función. Deberá incluir un encabezado funcional . Aquí hay un código de muestra:
#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;
}
El resultado es:
uppercase name: DANIEL BRüHL
Como era de esperar, toupper no tiene ningún efecto sobre los personajes no ascii. Pero este casting es beneficioso para evitar comportamientos inesperados.
La referencia se refiere al valor que se puede representar como un unsigned char
, no a un unsigned char
. Es decir, el comportamiento no está definido si el valor real no está entre 0 y UCHAR_MAX
(típicamente 255). (O EOF
, que es básicamente la razón por la que toma un int
lugar de un char
.)
Lamentablemente Stroustrup fue descuidado :-(
Y sí, los códigos de letras latinas deberían ser no negativos (y no se requiere molde) ...
Algunas implementaciones funcionan correctamente sin conversión a char sin firmar ...
Por alguna experiencia, puede costar varias horas encontrar la causa de segfault de un toupeper de este tipo (cuando se sabe que hay una segfault) ...
Y también hay isupper, islower, etc.
Sí, el argumento para toupper
debe convertirse en unsigned char
para evitar el riesgo de un comportamiento indefinido.
Los tipos char
, signed char
y unsigned char
son tres tipos distintos. char
tiene el mismo rango y representación que el signed char
o el unsigned char
. (El char
normal está muy comúnmente firmado y puede representar valores en el rango -128 .. + 127).
La función toupper
toma un argumento int
y devuelve un resultado int
. Citando el estándar C, sección 7.4, párrafo 1:
En todos los casos, el argumento es un
int
, cuyo valor debe ser representable como ununsigned char
o debe ser igual al valor de la macroEOF
. Si el argumento tiene otro valor, el comportamiento no está definido.
(C ++ incorpora la mayor parte de la biblioteca estándar C, y difiere su definición al estándar C).
El operador de indexación []
en std::string
devuelve un valor de char
. Si el char
simple es un tipo firmado, y si el valor devuelto por el name[0]
pasa a ser negativo, entonces la expresión
toupper(name[0])
tiene un comportamiento indefinido
El lenguaje garantiza que, incluso si se firma un char
simple, todos los miembros del conjunto de caracteres básicos tienen valores no negativos, por lo que, dada la inicialización
string name = "Niels Stroustrup";
el programa no se arriesga a un comportamiento indefinido. Pero sí, en general, un valor de char
pasado a toupper
(o a cualquiera de las funciones declaradas en <cctype>
/ <ctype.h>
necesita convertirse en unsigned char
, de modo que la conversión implícita a int
no arroje un resultado negativo valorar y causar un comportamiento indefinido.
Las funciones <ctype.h>
se implementan comúnmente utilizando una tabla de búsqueda. Algo como:
// assume plain char is signed
char c = -2;
c = toupper(c); // undefined behavior
puede indexar fuera de los límites de esa tabla.
Tenga en cuenta que la conversión a unsigned
:
char c = -2;
c = toupper((unsigned)c); // undefined behavior
no evita el problema Si int
es de 32 bits, convertir el valor de char
-2
a unsigned
produce 4294967294
. Esto se convierte implícitamente en int
(el tipo de parámetro), que probablemente rinde -2
.
toupper
puede implementarse para que se comporte de manera sensata con los valores negativos (aceptando todos los valores desde CHAR_MIN
hasta UCHAR_MAX
), pero no es obligatorio. Además, las funciones en <ctype.h>
son necesarias para aceptar un argumento con el valor EOF
, que generalmente es -1
.
El estándar C ++ realiza ajustes en algunas funciones de biblioteca estándar de C. Por ejemplo, strchr
y varias otras funciones son reemplazadas por versiones sobrecargadas que imponen la corrección const
. No hay tales ajustes para las funciones declaradas en <cctype>
.