Ich versuche, zwei Zeichenfolgen in C in einer for-Schleife zu verketten und stoße auf ein Problem, bei dem meine Ganzzahl (i) plötzlich zu einer scheinbar zufälligen Zahl wird, nachdem sie einmal gelesen wurde.

char string[] = "Example String";
char result[] = "";
for (int i = 0; i < 10; i++) {
    printf("%d\n", i); //i prints 0 on the first loop and a random
                       //number the next, which terminates the program.
    strcat(result, string);
    printf("%s\n", result);
}
c
-1
user8620802 19 Apr. 2018 im 20:53

3 Antworten

Beste Antwort

Das Problem ist, dass Sie eine Zeichenfolge der Länge 1 einschließlich des Nullterminators initialisieren.

char result[] = "";

Wenn Sie also versuchen, string in result zu schreiben, und die Zeichenfolge länger als das Ergebnis ist (wie es wahrscheinlich ist, da sizeof result 1 ist), läuft sie über, was undefiniert ist Verhalten:

strcat(result /* length of 1*/ , string /* length of 15*/);

Wenn Sie die in i gespeicherte Zufallszahl in hexadezimal konvertieren und deren ASCII-Werte verwenden, werden möglicherweise Teile der Zeichenfolge angezeigt, die Sie schreiben. Dies liegt daran, dass Sie, wenn Ihre Zeichenfolge lang genug ist, einen ... Stapelüberlauf verursachen. (ba dum chshhhhhh)

Und so wird anderer zufälliger Speicher von Ihrer Zeichenfolge überschrieben, einschließlich des Speichers, in dem i gespeichert ist.

LÖSUNG: Weisen Sie die Variable result als Zeiger mit genügend Speicher zu, um die Zeichenfolge zu speichern, die Sie zum Einfügen benötigen. In diesem Fall:

char string[] = "Example String";
char * result = malloc(sizeof (char*) + (sizeof string * 10)); // 10 being the number of iterations in the for loop

Denken Sie daran, dass Sie auch result = realloc(result, sizeof(char *) + previousResultSize + concatStringLength); verwenden können, wenn Sie die Zuweisung von Speicher ändern müssen. Vergessen Sie nur nicht Ihren Nullterminator mit Zeichenfolgen!

2
Toby Speight 14 Jän. 2020 im 08:47

Sie überschreiben den lokalen Stapelspeicher mit Ihrem Aufruf von sprintf() - dies ist der Grund für diese UB (undefiniertes Verhalten). Und Sie können deutlich erkennen, warum dies als UB bezeichnet wird: Sie können nicht zuverlässig sagen, was passieren wird, bevor Sie das Programm ausführen :)

0
YePhIcK 19 Apr. 2018 im 18:04

Undefiniertes Verhalten.

sprintf()
Wenn zwischen überlappenden Objekten kopiert wird, ist das Verhalten undefiniert. C11 §7.21.6.6 2

//      v--------v   and      v--------v overlap  
sprintf(stackTrace, "%s%s\n", stackTrace, stack[i]); // UB

char stackTrace[] = ""; ist zu klein @Charles Srstka. Das Deklarieren von stackTrace[] mit einem Speicher wie char stackTrace[1000] = ""; ist nicht ausreichend. Der Code muss auf eine bestimmte Weise angehängt werden.


Deklarieren Sie stattdessen einen Puffer und hängen Sie ihn an, indem Sie das Ende der bisher verwendeten Zeichenfolge verfolgen.

char **stack;
char stackTrace[10000];
stackTrace[0] = '\0';
char *st = stackTrace;

for (int i = 0; i < levels /*500*/; i++) {
  printf("debug %i\n", i);

  // In some fashion, perform a safe concatenation
  size_t remaining_size = &stackTrace[sizeof stackTrace] - st;
  sprintf(st, remaining_size, "%s\n", stack[i]);
  // update `st` to the end of the string
  st += strlen(st);

  printf(stackTrace);  // do not use stackTrace as a _format_
  printf("%s", stackTrace);
}

Für die Zwecke einer Stapelverfolgung empfiehlt es sich, den Stapel @YePhIcK zuzuweisen Trace-Puffer zu Beginn eines Programmlaufs - möglicherweise als static.

// char stackTrace[10000];
static char stackTrace[10000];

Für den Zweck eines Stack-Trace ist die Verwendung eines sicheren Anhangs von entscheidender Bedeutung, da die Notwendigkeit eines Stack-Trace häufig impliziert, dass möglicherweise etwas nicht stimmt. snprintf() ist eine komplexe Funktion. Verwenden von nicht standardmäßigen Funktionen wie strlcpy()/strlcat() @Groo oder gleichwertig ist eine gute Idee.

0
chux - Reinstate Monica 19 Apr. 2018 im 20:57