Tengo una impresión repetida que imprime un montón de char* no terminados en nulo así:
int len1, len2, len3, len4; char *str1, *str2, *str3, *str4; get_vals(message, val1, &str1, &len1); get_vals(message, val2, &str2, &len2); get_vals(message, val3, &str3, &len3); get_vals(message, val4, &str4, &len4); printf("%.*s %.*s %.*s %.*s", len1, str1, len2, str2, len3, str3, len4, str4); donde get_vals establece el puntero char * al valor en la memoria y establece len a la longitud del texto. Es posible que el char * pasado se establezca en NULL y, de ser así, el campo de longitud se establecerá en 0 . Parece que esta impresión ha sucedido muchas veces y no se ha fallado, supongo que debido al hecho de que el especificador de longitud es 0 , por lo que probablemente no haya desreferenciación. Sin embargo, ¿es esto siempre seguro? ¿Quizás depende de la versión del sistema operativo o libc? ¿Vale la pena hacer una verificación de seguridad como esta:
printf("%.*s %.*s %.*s %.*s", len1, str1 ? str1 : "", len2, str2 ? str2 : "", len3, str3 ? str3 : "", len4, str4 ? str4 : "");Cuando escribe printf("%.*s", len1, str1) , donde len1 es cero y str1 es un puntero nulo, está usando un especificador s y configurando la precisión en 0. Revisé las partes relevantes de la sección 7.21 .6 de N1570 . Al documentar el especificador s , dice:
el argumento será un puntero al elemento inicial de una matriz de tipo carácter. Los caracteres de la matriz se escriben hasta (pero sin incluir) el carácter nulo de terminación. Si se especifica la precisión, no se escriben más de esa cantidad de bytes.
Entonces, técnicamente, solo mirando la primera parte de esa cita, necesita proporcionar un puntero a una matriz en lugar de proporcionar un puntero nulo. Entonces su código no sigue esa parte del estándar.
Sin embargo, establece su precisión en 0, por lo que la segunda parte de la cita nos dice que la función printf en realidad no va a escribir ningún carácter de esa matriz en la salida. Esto me implica que tampoco intentará leer ningún carácter: leer más allá del final de la matriz no es seguro, por lo que las implementaciones de printf no deberían hacer eso. Por lo tanto, su código probablemente funcionará en la práctica y es difícil imaginar un caso en el que falle. El mayor problema que se me ocurre en la práctica es que los analizadores o validadores estáticos pueden quejarse de su código.
¿Está garantizado por el estándar C que es seguro hacer
printf("%.*s", 0, NULL)?
No, no del todo.
NULL es una constante de puntero nulo definida por la implementación . Su tipo puede ser void * , int , long , long long , unsigned y algunos otros tipos de enteros. Es probable que tenga el mismo tamaño y ... parámetro compatible con char * o void* , pero no se especifica como tal. Pasar NULL para que coincida con un "%s" incurre en un riesgo de comportamiento indefinido .
Mejor preguntar:
¿Está garantizado por el estándar C que es seguro hacer
printf("%.*s", 0, (char *) NULL)?
En este caso, la respuesta de @David Grayson se aplica bien: comportamiento técnico indefinido , pero a menudo está bien. [Aunque no cumplo con "Esto implica para mí que no intentará leer ningún carácter", ya que las implementaciones de la biblioteca no necesitan seguir las reglas del código de usuario, hacen trampa].
Para una alta portabilidad, no use printf("%.*s", 0, NULL) por estas 2 razones.
Alternativa:
printf("%.*s", 0, "");