3
#include<stdio.h>

void main()
{
    int i;
    char str[4] = "f4dfkjfj";
    char str2[3] = "987";
    char str3[2] = {'j','j','\0'};

    //printf("%c\n",str[1]);
    //printf("%c\n",1[str]);

    puts(str);
    puts(str2);
    puts(str3);           
}

Output observation:

  • Printing str2 prints the contents from both str2 and str.
  • Printing str3 prints str3, str2 and str.

Why this behaviour while printing the not nul ended string, which is concatenated with strings previously defined until "\0" character is encountered by the puts() function (this function prints till it encounters a nul )?

(Note: I deliberately initialised them with too long initializer strings)

Pavithran Ravichandiran
  • 1,711
  • 1
  • 17
  • 20
  • Shouldn't the compiler issue warnings or even errors for declarations as `char str2[3] = "987";`? – Jabberwocky Dec 09 '16 at 07:12
  • yes, it does issues warning of that too-long initialiser without nul character,but then I did that on purpose for learning purpose.. – Pavithran Ravichandiran Dec 09 '16 at 07:35
  • Regarding the `str2` case, [see this](http://stackoverflow.com/questions/31296727/inconsistent-gcc-diagnostic-for-string-initialization). – Lundin Dec 09 '16 at 09:37

3 Answers3

5

(Note: I deliberately initialised them with too long initializer strings)

Then you should be aware of the side-effects, too.

The problem with all your arrays are, they are not null-terminated, so they are not strings.

Quoting C11, chapter §7.1.1, Definitions of terms, (emphasis mine)

A string is a contiguous sequence of characters terminated by and including the first null character. [...]

Using them with string handling functions (like puts()) would invoke undefined behavior, as the functions, in search of the null-terminator, would go out of bound (i.e., outside allowed memory region) and cause the invalid memory access.

Quoting the standard again, chapter 7.21.7.9,

The puts function writes the string pointed to by s to the stream pointed to by stdout, and appends a new-line character to the output. The terminating null character is not written.

The expected argument is a string, which, none of the arguments in your code is.

That said, FWIW, for a hosted environment, the recommended signature of main() is int main(void), at least.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • It isn't particularly undefind behaviour...What it prints is exactly the same content of previous defined string...First execute this and see – Pavithran Ravichandiran Dec 09 '16 at 06:59
  • 1
    @PavithranRavichandiran can you quote something which says this is __not__ UB? I'm interested. – Sourav Ghosh Dec 09 '16 at 07:00
  • I can't find anything, That's why I came here...But what I guess is if its UB , then it would return something else...Why particularly the contents of strings defined previously.. – Pavithran Ravichandiran Dec 09 '16 at 07:04
  • @PavithranRavichandiran Did you read the linked wiki page? It has the answer you're looking for, I guess. :) – Sourav Ghosh Dec 09 '16 at 07:06
  • Shouldn't the compiler issue warnings or even errors for declarations as `char str2[3] = "987";`? – Jabberwocky Dec 09 '16 at 07:12
  • @MichaelWalz Nope, it shouldn't, as per a bug in the C standard, [see this](http://stackoverflow.com/questions/31296727/inconsistent-gcc-diagnostic-for-string-initialization). – Lundin Dec 09 '16 at 09:42
3

Initialization

  char str[4] = "f4dfkjfj";

as well as

  char str2[3] = "987";

and

  char str3[2] = {'j','j','\0'};

are incorrect, because expression char str[4] allocates 4 bytes for data, but data - "f4dfkjfj" requires 9 bytes - 8 bytes for visible characters and one more byte for '\0'.

UPDATE:

Lets consider the following example

#include<stdio.h>

void main()
{
    int i;
    char str[4] = "f4dfkjfj";
    char str2[3] = "987";
    printf("Address of str2 = %p and size is %d bytes\n", str2, sizeof(str2));
    printf("Addres     |  Data in memory\n");
    char * ptr;
    for (ptr = str2 - 2; ptr <= str2 + 5; ptr++)
    {
        printf("%p   | %c\n", ptr, *ptr);
    }
}

In my Visual Studio 2013 under Windows 7 I see the following:

enter image description here

But if I change char str2[3] = "987"; to char str2[4] = "987"; result will be

enter image description here

Try puts(str2) for char str2[4] = "987"; and you will see the difference.

Note: each time memory addresses (in the stack for local variables) are (can be) different, but data around allocated memory (changed or not) are more important.

VolAnd
  • 6,367
  • 3
  • 25
  • 43
  • 3
    no, `char str2[3] = "987";` is also incorrect, because one more byte - `'\0'`. I.e. `char str2[4] = "987";` would be correct – VolAnd Dec 09 '16 at 07:00
  • 1
    @SouravGhosh Partly agree! Using `puts()` with incorrectly initialysed data leads to incorrect output – VolAnd Dec 09 '16 at 07:03
  • @SouravGhosh `str1` and `str3` definitions are constraint violations, `str2` is not – M.M Dec 09 '16 at 07:42
  • The point here is that `str2` is a bug, but it is a bug that the C standard (stupidly) allows. See @M.M's answer. – Lundin Dec 09 '16 at 09:29
  • Anyway, proving how C works by compiling code in the notoriously bad Visual compiler isn't a good idea. All you proved with this is that Visual C doesn't follow the standard - we already knew that. – Lundin Dec 09 '16 at 09:34
  • My update illustrates, how VC++ compiler works in case of `char str2[3] = "987";` - only 3 bytes are initialized and as a result "end of string" (`\0`) is lost. I suppose similar behavior can be not only in Visual Studio. – VolAnd Dec 09 '16 at 10:28
2

These lines are constraint violations. The compiler should give an error message and the behaviour of the program is completely undefined:

char str[4] = "f4dfkjfj";
char str3[2] = {'j','j','\0'};

The constraint being violated is that there are too many initializers for the array. (C11 6.7.9/2)

However char str2[3] = "987"; is correct, there is a special case that when an array is initialized from a string literal, it is allowed to ignore the null terminator if there is no room in the array. (C11 6.7.9/14)

Going on to pass str2 to a function that expects a null-terminated string would cause undefined behaviour however.

Prof. Falken
  • 24,226
  • 19
  • 100
  • 173
M.M
  • 138,810
  • 21
  • 208
  • 365
  • 1
    Up-voted for mentioning the obscure "C standard bug" in 6.7.9/14. I don't think many know of this special case. – Lundin Dec 09 '16 at 09:28
  • @Lundin I guess it had similar use cases to `strncpy`, e.g. FAT filenames (8-byte non-terminated buffer, right-padded with null only if the filename was under 8 chars) – M.M Dec 09 '16 at 10:36
  • Then why did they make the same odd rule for `wchar_t` in the next paragraph? Did "fixed-width strings" of `wchar_t` ever exist in Unix or elsewhere? – Lundin Dec 09 '16 at 10:54