2010-03-25 25 views
16

Código de portado de 32 bits a 64 bits. Hay muchos lugares conint vs size_t en 64bit

int len = strlen(pstr); 

todos éstos generan advertencias ahora porque strlen() devuelve size_t que es de 64 bits y de 32 bits int es todavía. Así que he estado reemplazándolos con

size_t len = strlen(pstr); 

Pero me he dado cuenta de que esto no es seguro, ya que no está firmado size_t y puede ser tratado como firmado por el código (que en realidad encontré con un caso donde causó una problema, gracias, pruebas unitarias!).

Fundición a ciegas strlen volver a (int) se siente sucio. O tal vez no debería?
Entonces la pregunta es: ¿hay una solución elegante para esto? Probablemente tenga mil líneas de código así en la base de código; No puedo verificar manualmente cada uno de ellos y la cobertura de prueba actualmente está entre el 0.01 y el 0.001%.

+1

¿Tiene un ejemplo en esta longitud es tratado como firmado? – kroimon

+0

El ejemplo probablemente sea algo así como: 'len--; if (len <0) {break} ' – Tim

Respuesta

5

Como solución de compromiso, puede usar ssize_t (si está disponible). Falsifíquelo si no, usando long long, int_fast64_t, intmax_t, o tenga un encabezado de puerto de plataforma que permita especificar un tipo adecuado para una plataforma. ssize_t está en POSIX no es C o C++ estándar, pero si alguna vez tocas una plataforma que no tiene un tipo firmado del mismo tamaño que size_t, entonces simpatizo.

Un lanzamiento a int es casi seguro (asumiendo 32 bit int en su plataforma de 64 bits, lo que parece razonable), porque es poco probable que una cadena tenga más de 2^31 bytes de longitud. Un lanzamiento a un tipo firmado más grande es aún más seguro. Los clientes que pueden permitirse el lujo de 2^63 bytes de memoria son lo que se conoce en el comercio como "un buen problema para tener" ;-)

Por supuesto, podrías comprobar que:

size_t ulen = strlen(pstr); 
if (ulen > SSIZE_MAX) abort(); // preferably trace, log, return error, etc. 
ssize_t len = (ssize_t) ulen; 

Claro que hay una sobrecarga , pero si tiene 1000 instancias, entonces no todas pueden ser críticas para el rendimiento. Para los que están (si los hay), puede hacer el trabajo para investigar si realmente importa len que se firma. Si no lo hace, cambie a size_t. Si lo hace, reescriba o corra el riesgo de no cumplir nunca un objeto tan absurdamente grande. El código original casi seguramente habría hecho lo incorrecto de todos modos en la plataforma de 32 bits, si len hubiera sido negativo como resultado de strlen devolviendo un valor mayor que INT_MAX.

+0

. Estoy de acuerdo con que lanzar a int es casi seguro, pero no entiendo cuál es el punto de ssize_t: también es * nerly * safe. Es ligeramente más seguro que int, pero aún así - size_t puede ser más grande que ssize_t. –

+0

@MK, 'ssize_t' debe tener el mismo tamaño que' size_t' – osgx

+2

@MK: creo que la intención general de 'ssize_t' es que, en la práctica, las implementaciones POSIX no permitan objetos individuales de más de la mitad del tamaño del espacio de direcciones disponible. Es bastante fácil hacer cumplir que esto es 'malloc', aunque no creo que esté garantizado. Es útil tener un tipo de tamaño con signo para representar compensaciones que pueden ser negativas. –

1

Puede tratar el site_t firmado con seguridad en la mayoría de los casos. El tamaño sin signo_t se tratará como negativo solo cuando (o el resultado intermedio en expresiones) sea más grande que 2^31 (para 32 bits) o 2^63 para 64 bits.

ACTUALIZACIÓN: Disculpe, size_t no será seguro en construcciones como while ((size_t)t >=0). Así que la respuesta correcta es usar ssize_t.

+1

Me refiero al caso en el que luego disminuyo el len a un punto donde se vuelve negativo. Al igual que en un bucle while (len> 0) –

+0

loop 'while (len> 0)' debe detenerse en 'len == 0'. Por favor, muéstrenos su ejemplo, problema en el que se detectó con pruebas unitarias. – osgx

+2

Bla, lo siento, quise decir si (len <0). Tenía un bucle con esa comprobación inversa de "if (len <0) skip something;" en lugar de "if (len> = 0) haz algo;" –

5

Al configurar las advertencias del compilador al nivel máximo debe obtener un buen informe de cada conversión de signo incorrecto. En gcc, '-Wall -Wextra' debería hacer.

También puede usar un analizador de código estático como cppcheck para ver si todo está bien.

+0

y -wall encontrará todos los lugares donde size_t se está utilizando en un contexto firmado. Deberías estar usando size_t – pm100

4

Puede usar ssize_t (la variante con signo de size_t).

7

Hace algún tiempo he publicado una breve nota sobre este tipo de cuestiones en mi blog y la respuesta corta es:

Always use proper C++ integer types

Respuesta larga: Al programar en C++, que es una buena idea usar tipos enteros apropiados relevantes al contexto particular. Un poco de rigor siempre paga. No es raro ver una tendencia a ignorar los tipos integrales definidos como específicos para los contenedores estándar, a saber, size_type. Está disponible para varios contenedores estándar como std :: string o std :: vector. Tal ignorancia puede vengarse fácilmente.

A continuación se muestra un ejemplo simple del tipo incorrectamente utilizado para capturar el resultado de la función std :: string :: find. Estoy bastante seguro de que muchos esperarían que no haya nada de malo con el int sin firmar aquí. Pero, en realidad, esto es solo un error. Ejecuto Linux en una arquitectura de 64 bits y cuando compilo este programa tal como está, funciona como se esperaba. Sin embargo, cuando se sustituye la cadena en la línea 1 con ABC, que todavía funciona, pero no como se esperaba :-)

#include <iostream> 
#include <string> 
using namespace std; 
int main() 
{ 
    string s = "a:b:c"; // "abc" [1] 
    char delim = ':'; 
    unsigned int pos = s.find(delim); 
    if(string::npos != pos) 
    { 
    cout << delim << " found in " << s << endl; 
    } 
} 

Fix es muy simple. Simplemente reemplace unsigned int con std :: string :: size_type. El problema podría evitarse si alguien que escribió este programa se encargó del uso del tipo correcto. Sin mencionar que el programa sería portátil de inmediato.

He visto este tipo de problemas muchas veces, especialmente en el código escrito por antiguos programadores de C que no les gusta usar el bozal de rigor que el sistema de tipos de C++ exige y exige. El ejemplo anterior es trivial, pero creo que presenta bien la raíz del problema.

Recomiendo brillante artículo 64-bit development escrito por Andrey Karpov donde puede encontrar mucho más sobre el tema.

+2

Aunque generalmente estoy de acuerdo con "usar los tipos correctos", 'std :: some_container :: size_type' se reduce a' size_t' en todas las implementaciones decentes.Por lo que puedo ver, al menos 'std :: bitset :: size_type',' std :: array :: size_type', 'std :: initializer_list', y' std :: allocator :: size_type' son typedefs para ' size_t'. Así que a menos que estés usando un alocator loco o parámetros de plantilla muy especiales, 'size_t' es suficiente. – rubenvb

1

Si su compilador soporta C++ 0x:

auto len = strlen(pstr);