PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C: sprintf ohne '\0'


mekakic
2008-11-27, 10:50:19
Hi,

wenn ich in einen Buffer etwas schreibe und die Formatierungsfunktionen von sprintf nutzen will, aber gleichzeitig nicht den Platz habe, damit sprintf in den Zielbuffer noch eine Nullterminierung \0 anhängt. Wie mache ich das?

Konkret ein Eingabe-Byte als dessen Hexadezimalrepräsentation in ein char[2] kopieren, also sprintf( buffer, "%02X", value );. sprintf hängt ein \0 dran, wofür der Buffer nicht groß genug sein kann.

Wie macht man das am besten?

MuLuNGuS
2008-11-27, 11:51:07
wie wäre es mit einem zweiten buffer und dann umkopieren, sollte ja wohl kein performanceproblem sein...

pest
2008-11-27, 11:56:14
ne dez2hex funktion kann man sich auch in 5 min selber schreiben

AtTheDriveIn
2008-11-27, 12:49:48
char buffer[SIZE];
int value=16;
snprintf(buffer,SIZE+1, "%02X", value );


liefert in buffer "1 0"

mekakic
2008-11-27, 13:53:39
snprintf(buffer,SIZE+1, "%02X", value );
Danke! Genau das habe ich gesucht...

Berni
2008-11-27, 15:07:39
Wieso "SIZE+1" wenn der buffer ja nur SIZE groß ist? Damit schreibt snprintf dann doch über die Grenzen hinaus? Laut den pages ist der 2. Parameter die "maximum number of characters to produce" und wenn der buffer nur 2 groß ist dann würden ja wiederum maximal 3 Zeichen produziert werden was falsch ist.

MuLuNGuS
2008-11-27, 15:33:27
das +1 braucht man auch nicht.

snprintf hängt die \0 nicht mehr an solange SIZE <= value ist.


value könnte auch eine super lange zeichenkette enthalten, wenn SIZE==2 ist werden auch nur 2 chars geschrieben ohne das ein \0 angehängt wird.

AtTheDriveIn
2008-11-27, 18:42:05
das +1 braucht man auch nicht.

snprintf hängt die \0 nicht mehr an solange SIZE <= value ist.


value könnte auch eine super lange zeichenkette enthalten, wenn SIZE==2 ist werden auch nur 2 chars geschrieben ohne das ein \0 angehängt wird.


ich habe es ausprobiert.

Bei Size=2 wird "1 \0" in den Buffer geschrieben

Erst Size+1 liefert das richtige Ergebnis. So richtige erklären kann ich das nun auch nicht. :)

Berni
2008-11-27, 23:37:45
Ich kanns mir schon vorstellen: snprintf soll ja einen String erstellen. Strings werden in C immer mit \0 (nul) abgeschlossen. Wenn \0 nicht vorhanden ist kann das Ende des String überhaupt nicht erkannt werden und somit versucht snprintf immer, dieses auch einzufügen und verwirft lieber andere Buchstaben. Dieses Verhalten ist auch in den man-pages beschrieben:
http://libslack.org/manpages/snprintf.3.html
If size is zero, nothing is written and str may be null. Otherwise, output characters beyond the n-1st are discarded rather than being written to str, and a nul character is written at the end of the characters actually written to str.

Bei SIZE=1 würde dementsprechend immer nur das \0 drin erscheinen. Wenn du SIZE+1 machst, dann scheint es aus deiner Sicht zwar zu gehen - das \0 wird aber trotzdem IRGENDWO im Speicher (genauergesagt direkt nach dem char-Array aber man weiß halt nicht was da für ne Variable steht) geschrieben und überschreibt möglicherweise eine andere Variable!

Daher darf das so KEINESFALLS gemacht werden! Verwendet (wie schon gesagt wurde) ein temporäres Array mit korrekter Größe und kopiert das um oder bastelt ne Funktion die das macht.

_Gast
2008-11-28, 09:20:56
Erst Size+1 liefert das richtige Ergebnis. So richtige erklären kann ich das nun auch nicht. :)Das ist ein klassischer Pufferüberlauf und solche Techniken sind verantwortlich für die meisten Sicherheitslücken in Systemen. Du verwendest Speicher, der dir nicht gehört. Das kann zufällig funktionieren, muss aber nicht.

Der Puffer muss also vorher ausreichend dimensioniert werden um die Werte und die abschließende \0 aufzunehmen. Das zweite Argument der snprintf-Funktion ist die Größe des Puffers einschließlich der \0, die niemals überschritten wird. Wird diese aber größer angegeben (SIZE+1) als der Puffer selbst lang ist (SIZE), wird irgendwo in den Speicher geschrieben.

mekakic
2008-11-28, 09:41:01
Ich kanns mir schon vorstellen: snprintf soll ja einen String erstellen. Strings werden in C immer mit \0 (nul) abgeschlossen. Wenn \0 nicht vorhanden ist kann das Ende des String überhaupt nicht erkannt werden und somit versucht snprintf immer, dieses auch einzufügen und verwirft lieber andere Buchstaben. Dieses Verhalten ist auch in den man-pages beschrieben: Schade :( Finde es auch etwas eigenartig, daß sich snprintf hier exakt gegensätzlich zu strncpy verhält, dies läßt ja den '\0' weg, wenn der Buffer nicht ausreicht.

MuLuNGuS
2008-11-28, 11:22:41
ich habe es ausprobiert.

Bei Size=2 wird "1 \0" in den Buffer geschrieben

Erst Size+1 liefert das richtige Ergebnis. So richtige erklären kann ich das nun auch nicht. :)

ich hab's auch ausprobiert.
meine buffergrösse ist 8 welchen ich dann mit 0x33 ausfülle, nun lasse ich snprintf nur 2 bytes schreiben, ein \0 wird aber nicht angehängt.

*edit*
vielleicht verhält sich die alte variante von snprintf anders?!


#include <stdio.h>
#include <conio.h>

#define SIZE 8

int main(int argc, char* argv[])
{

printf("GO!\n");

char buffer[SIZE];

for(int i = 0; i < SIZE; i++)
{
buffer[i] = 0x33;
}

_snprintf(buffer, 2, "ABCD");

for(int i = 0; i < SIZE; i++)
{
printf("%02X", *(buffer+i));
printf("\n");
}

return 0;

}

_Gast
2008-11-28, 11:53:13
ich hab's auch ausprobiert.
meine buffergrösse ist 8 welchen ich dann mit 0x33 ausfülle, nun lasse ich snprintf nur 2 bytes schreiben, ein \0 wird aber nicht angehängt.Das ist komisch, vielleicht gibt es verschiedene Versionen? Bei mir hängt der auch keine \0 dran, selbst dann nicht, wenn der Puffer überschritten wird. Gefunden habe ich aber das:If size is zero, nothing is written and str may be null. Otherwise, output characters beyond the n-1st are discarded rather than being written to str, and a nul character is written at the end of the characters actually written to str. If copying takes place between objects that overlap, the behaviour is undefined.

Berni
2008-11-28, 11:53:30
@mekakic: Tja ist halt nunmal so. Daher muss man sich die Beschreibungen der Funktionen anschauen bevor man von einem anderen Verhalten ausgeht.

@MuLuNGuS:
_snprintf ist nicht gleich snprintf. Das ist irgendeine Windowsvariante (NICHT C99-konform!!!) und laut http://msdn.microsoft.com/en-us/library/2ts7cx93(VS.80).aspx addiert _snprintf eben keinen Terminator:
Security Note
Ensure that format is not a user-defined string. Because this function does not guarantee NULL termination (in particular, when the return value is count), ensure that it is followed by code that adds the null terminator. For more information, see Avoiding Buffer Overruns.
_snprintf ist aber als deprecated markiert und stattdessen sollte unter Windows die Funktion _snprintf_s verwendet werden, die dann wieder den Terminator anhängt (siehe http://msdn.microsoft.com/en-us/library/f30dzcf6(VS.80).aspx )

MuLuNGuS
2008-11-28, 12:01:08
@mekakic: Tja ist halt nunmal so. Daher muss man sich die Beschreibungen der Funktionen anschauen bevor man von einem anderen Verhalten ausgeht.

@MuLuNGuS:
_snprintf ist nicht gleich snprintf. Das ist irgendeine Windowsvariante und laut http://msdn.microsoft.com/en-us/library/2ts7cx93(VS.80).aspx addiert _snprintf eben keinen Terminator:

_snprintf ist aber als deprecated markiert und stattdessen sollte unter Windows die Funktion _snprintf_s verwendet werden, die dann wieder den Terminator anhängt (siehe http://msdn.microsoft.com/en-us/library/f30dzcf6(VS.80).aspx )


tja, hab es hier im VS2005 nur mir _snprintf ausprobiert.

am besten man kopiert sich das ganze so zurecht wie ma es braucht, da kann dann nix schief gehen und feddig ist die laube ;)

PHuV
2008-11-28, 23:53:05
Es kann sogar soweit gehen, daß man sich selbst immer ein \0 ranhängen muß. Ich habe da schon die dollsten Dinger erlebt, jede Maschine, jedes OS, jeder Compiler kann sich hier etwas anders verhalten, gerade bei der Speicherverwaltung.

Deshalb hört auf _Gast, immer sauber allocieren, und das \0 gehört immer mit in die Berechnung! Ansonsten kann es in 99 Fällen gut geht, und dann knallt es, aber gewaltig. Gerade Windows ist da etwas gutmütig, und unter Linux oder AIX haut es Euch um die Ohren, und dann an einer Stelle, die nichts damit zu tun hat (siehe Speicherüberlauf und reinschreiben in anderen Speicher).