0

Hello I am new to coding and was just wondering why the below code:

#include <stdio.h>

int main() {

for(int i = 0; 1 ; i++) {

    char x;
    char z[1+i];
    x=getchar();
    if (x == '\n'){
        *(z+i) = '\0';
        printf("%s",z);
        break;}
    *(z+i) = x;
    printf("%s, %d = %c, i = %d\n",z, (z+i),*(z+i),i);
}

return 0;
}

of C does not work for inputs that are more than 15 characters? (I don't think it is beacuse of the current state of my pc since I tried it with an online compiler and it still breaks at 16 chars, but then again I am new to coding.) Would appreciate any help. Thank you. (If the answer is something like: "Thats not possible in C" or something like that please don't leave it at that and explain why it is not possible).

PS: I cheked some of the similar questions to mine but I don't get what any of them are doing, if this post is a duplicate, I would really appreciate if you could link me to the post that solves my issue, I will delete this post shortly after if that is the case.

This is the input and output of my program for 16 chars,

1234567890123456
1, 6421952 = 1, i = 0
12, 6421953 = 2, i = 1
123, 6421954 = 3, i = 2
1234, 6421955 = 4, i = 3
12345, 6421956 = 5, i = 4
123456, 6421957 = 6, i = 5
1234567, 6421958 = 7, i = 6
12345678, 6421959 = 8, i = 7
123456789, 6421960 = 9, i = 8
1234567890, 6421961 = 0, i = 9
12345678901, 6421962 = 1, i = 10
123456789012, 6421963 = 2, i = 11
1234567890123, 6421964 = 3, i = 12
12345678901234, 6421965 = 4, i = 13
123456789012345, 6421966 = 5, i = 14
1234567890123456p@, 6421967 = 6, i = 15

Process returned 0 (0x0)   execution time : 4.076 s
Press any key to continue.

And this is the input and output for 15 chars,

123456789012345
1, 6421952 = 1, i = 0
12, 6421953 = 2, i = 1
123, 6421954 = 3, i = 2
1234, 6421955 = 4, i = 3
12345, 6421956 = 5, i = 4
123456, 6421957 = 6, i = 5
1234567, 6421958 = 7, i = 6
12345678, 6421959 = 8, i = 7
123456789, 6421960 = 9, i = 8
1234567890, 6421961 = 0, i = 9
12345678901, 6421962 = 1, i = 10
123456789012, 6421963 = 2, i = 11
1234567890123, 6421964 = 3, i = 12
12345678901234, 6421965 = 4, i = 13
123456789012345, 6421966 = 5, i = 14
123456789012345
Process returned 0 (0x0)   execution time : 4.150 s
Press any key to continue.

As you can see it works fine for 15 chars of input, but breaks at 16, this is the problem that I want to be solved.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
soda2718
  • 1
  • 2
  • 3
    First, you create a new `z` on each iteration. The fact that it "keeps" the old values from the previous iteration is just an accident (undefined behavior). Second, you try to use `z` as a C-style string, but you don't terminate it with `\0`. The fact that it's de-facto terminated in your case and does not crash on first iterations is another accident. Thirdly, local arrays with dynamic variables is compiler extension, not standard C, but that is irrelevant here. Try compiling with `-fsanitize=address`. – yeputons Jul 16 '23 at 12:54
  • 2
    @yeputons: It is only a compiler extension in the sense that choosing `int` to be 16 bits or 32 bits or some other size is a compiler extension: It is specified by the C standard but some choice is permitted by the implementation. Variable-length arrays are specified in the C standard. They were a mandatory feature in C 1999 but are optional now. – Eric Postpischil Jul 16 '23 at 13:05
  • @EricPostpischil Good to know, thank you. I thought it was not specified at all. – yeputons Jul 16 '23 at 13:07
  • It isn't clear what the purpose of this code is, so we don't know whether it is possible in C. Just a wild guess: is it to read a line from the standard input? Then use the standard function `fgets`. – n. m. could be an AI Jul 16 '23 at 13:12
  • Well I just thought defining an array size without knowing how long the input will be was annoying so I thought what if the array size increased itself as the characters from the buffer fed themselves into the for loop. It is rather annoying that you can't just assign a string to a variable like you can in python and have to deal with the problem which is making sure a new assignment to the array won't exceed the predefined size of it, this was an attempt to mimic that assigning strings to variables feature of python. – soda2718 Jul 16 '23 at 21:05
  • As a side note I was trying to do this while only using standart i/o library sort of like a challenge. – soda2718 Jul 16 '23 at 21:21
  • In C it is rarely productive to try and mimic other languages. – n. m. could be an AI Jul 16 '23 at 21:46

4 Answers4

2

You are lucky that it went so well

  1. You are printing strings without ensuring that there is a terminal 0 somewhere. Except when you see a \n, but for all other cases, nothing says there is a terminal 0

  2. Each z is a different array (with a different size). You cannot count on it to inherit to whatever you already put in its predecessor. Sure, you cannot swear it won't neither. And for a while, it works: the address of z is apparently the same for some of the iterations (probably because of some alignment considerations, but you are not supposed to count on that any way. Compiler works in mysterious ways. You are not supposed to make any assumptions about where each z will be located.

So, if you want to print z at each iteration, you must ensure that, at each iterations, it contains all the chars, followed by a terminal 0.

What you did, is printing a each ith iteration, a string z, whose ith char contain what getch gave. And all other chars of z (the i-1 first ones, but also whatever is after i, including what should be a terminal 0) are indeterminate. And happens to be, for a while, what was left from previous iteration for the i-1 first one, and some initial 0 for whatever is after (because, well, nothing says that this memory is initialized to 0, but, more often than not, it happens to be initial values of things)

chrslg
  • 9,023
  • 5
  • 17
  • 31
  • 1
    Re “And all other chars of `z`… are undefined”: The values of the elements of `z` are indeterminate, not undefined. – Eric Postpischil Jul 16 '23 at 13:07
  • I wasn't even aware that those were two different words (at least in C. Of course, in javascript, for example, undefined means `undefined`). I meant that printing them is UB. I edited my post. – chrslg Jul 16 '23 at 13:08
0

As this call of printf

printf("%s, %d = %c, i = %d\n",z, (z+i),*(z+i),i);

occurs in each iteration of the for loop even when the new line character '\n' was not yet detected then the array does not contain a string except the last iteration of the for loop that is broken by the break statement. And the conversion specification %s expects a pointer to a string.

Also to output a pointer you have to use the conversion specifier p.

You could change the call of printf the following way

printf("%.*s, %p = %c, i = %d\n", i + 1, z, ( void * )(z+i),*(z+i),i);

This call outputs exactly i + 1 characters stored in the array in a current iteration.

Though in any case the program has undefined behavior because it is unspecified whether the variable length array defined in each itertion of the for loop preserves data stored in it in a previous iteration.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

I know that I didn't explain the purpose of the code but here is the final version of the code which is highly based on FuzzFoo's answer.

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

int str_input(char **y);

int main()
{
    char * y = NULL;

    str_input(&y);

    printf("%s\n", y);

    str_input(&y);

    printf("%s", y);

    free(y);

    return (0);
}

int str_input(char **y)
{
    int i;
    char x;

    for (i = 0; 1 ; i++)
    {
        x = getchar();
        *y = (char *)realloc(*y, (2 + i) * sizeof(char));

        if (*y == NULL)

            break;

        if (x == '\n')
        {
            (*y)[i] = '\0';
            break;
        }

        (*y)[i] = x;
        (*y)[i + 1] = '\0';
    }

    return (0);
}

Hopefully this version of the code isn't trying to utilize any undefined behaviour and works for (virtually) all lengths of input. Purpose of it is to make the str_input function sort of assign strings to a pointer variable.(I know it is a little more complicated than that, but it is what it is.)(And also memory is assigned dynamically so it actually works for longer inputs.)

soda2718
  • 1
  • 2
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 20 '23 at 07:26
-1

Taking the above into consideration try this instead.

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

int main()
{
    int i;  
    char x;
    char *z = NULL;

    for (i = 0; 1; i++)
    {
        x = getchar();
        z = (char *) realloc(z, (2 + i) * sizeof(char));
        if (z == NULL)
            break;
        z[i+1] = '\0';
        if (x == '\n')
        {
            z[i] = '\0';
            printf("%s", z);
            break;
        }
        z[i] = x;
        printf("%s, %p = %c, i = %d\n", z, (z+i), z[i] , i);
    }

    free(z);

    return (0);
}
FuzzFoo
  • 1
  • 2
  • This code works fine and it is what I wanted to achieve. However doesn't this code also try to utilize the undifined behaviour of z being redefined in each iteration. If it isn't can somebody explain. Also, I deleted the #include part and it still works does C auto import it or is it not needed at all? – soda2718 Jul 16 '23 at 21:33
  • It does warned me now, don't know why it didn't the first time. – soda2718 Jul 16 '23 at 21:57
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 18 '23 at 21:07