-3

I didn't write C code for a long time, I'm rusty. Anybody knows why the following code prints "rtyaze" to stdout ? I was expecting "rty".

#include <stdio.h>

int main (void) {
  char s[] = "aze";
  char ss[][3] = { "rty" };
  printf("%s\n", ss[0]);
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335

6 Answers6

4

By making your string at the first element of ss have 3 characters you are eliminating the null terminator.

So printf continues until it finds a null terminator. By chance, your other string must have been placed in memory right after your first one.

If you change the 3 in ss[][3] to a 4 you should get expected behaviour.

TomE
  • 345
  • 2
  • 13
3

Your array declaration doesn't leave room for the terminating null character, so there's no null character at the end of "rty". Since %s format requires a null-terminated string as the argument, you're causing undefined behavior.

In this case, the memory for s happened to be right after the memory for ss, so printf() printed it when it searched for the null terminator.

Change your declaration to:

char ss[][4] = { "rty" };
Barmar
  • 741,623
  • 53
  • 500
  • 612
2

A string in C consists of a sequence of characters terminated by a null byte. The elements of ss don’t have enough room to store the given string which occupies 4 bytes including the null terminator. When you then attempt to print ss[0] you read past the end of the array. This invokes undefined behavior.

Change the size of the second array dimension to 4 to leave enough space.

dbush
  • 205,898
  • 23
  • 218
  • 273
2

char ss[][3] = { "rty" }; defines an array of arrays of 3 char. Since the number of arrays is not specified (nothing is inside []), it is determined by counting the initializers. There is just one initializer, the string literal "rty". Thus, the result is an array of 1 array of 3 char, which contains r, t, and y. Although the string literal "rty" implicitly contains a null character, the array is defined to explicitly contain just three characters, so the null character does not become part of the array.

printf("%s\n", ss[0]); passes the address of the first character of ss[0] to printf. The resulting behavior is undefined because printf should be passed the first character of a string, which means a sequence of characters terminated by a null character, but ss[0] does not contain a null character.

On some occasions when you do this, the other object, defined by char s[] = "aze"; may happen to follow ss in memory, and printf, while it is attempting to print the string, may continue beyond r, t, and y to print a, z, and e, after which it finds the null terminator.

On other occasions when you do this, the other object, s, might not follow ss in memory. The compiler might have removed s during optimization, since it is not used and hence is not needed in the program. Or the compiler might have put it in a different location. On such occasions, printf might continue on to other memory and print different characters, or it might continue on to inaccessible memory and cause a segment violation or other program termination.

On yet other occasions when you do this, the compiler might recognize that the printf call is undefined due to the lack of a terminating null character, and it might remove the printf call from the program entirely, because the C standard allows a C implementation to substitute any behavior it wants for undefined behavior.

Ultimately, the behavior is not defined by the C standard.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
2

The format specifier %s is used to output strings that is sequences of characters terminated with zero characters.

You declared the array the single (first) element of which does not contain a string.

char ss[][3] = { "rty" };

In fact the array is declared the following equivalent way

char ss[][3] = { { 'r', 't', 'y' } };

that is the terminating zero of the string literal was excluded from the list of initializers because the size of the internal array is equal to only 3.

To output the array you could write

printf("%3.3s\n", ss[0]);

explicitly specifying the number of characters you are going to output.

If you want to output it as a string you should enlarge it like

char ss[][4] = { "rty" };

that to include the terminating zero of the string literal "rty".

In case of the original program it seems that the compiler placed in the stack the arrays in the following order ss and then s. That is the memory allocated to the arrays looks the following way.

{ 'r', 't', 'y', 'a', 'z', 'e', '\0' }
  |___________|  |_________________|
      ss                  s

Pay attention to that this declaration

char s[] = "aze";

is equivalent to

char s[] = { 'a', 'z', 'e', '\0' };

that is the string literal includes the terminating zero and consequently the array s will contain a string.

Also you should know that such a declaration

char ss[][3] = { "rty" };

is not allowed in C++. In C++ you have to write at least like

char ss[][4] = { "rty" };
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

The reason your program "prints a variable not passed as an argument" is that your "rty" is not null terminated. This causes printf to continue printing characters until if finds a null terminator.

I ran this experiment:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char end[] = "\0";
    char layout[7] = " layout";
    char stack[6] = " stack";
    char the[4] = " the";
    char is[3] = " is";
    char this[4] = "This";

    printf("%s\n", this);
    return 0;
}

macOS Output (LLVM)

This is the stack layout

Linux Output (gcc)

This stack layout

Messing around with GDB on Linux showed that the variables were declared on the stack in a different order than in the code. Specifically

(gdb) print &this[0]
$8 = 0x7fffffffe287 "This stack layout"
(gdb) print &is[0]
$9 = 0x7fffffffe280 " is theThis stack layout"

I wrote this example program because sometimes a practical example makes it easier to visualize this kind of behavior.

  • What compilers did you use? – S.S. Anne Jun 25 '19 at 18:27
  • We saw a question recently where the stack layout appeared to depend on whether an object was a variable-length array or not. We can expect compilers may arrange objects based on alignment and other considerations as well. Simple experiments are not greatly informative. Additionally, this answer does not answer the question. – Eric Postpischil Jun 25 '19 at 18:33