0

This is my code. It is supposed to first of all scan 2 number m and n. Then scan an array of numbers that is x[n]. Number of scanned numbers in second line should be n. But it doesn't work correctly.

#include <stdio.h>

int main()
{
    int m = 0;
    int n = 0;
    int x[n];
    scanf("%d", &m);
    scanf("%d", &n);
    for(int i = 0 ;i < n;i++)
    {
        scanf("%d", &x[i]);
    }
}

It just scans 1 number and breaks out of the loop. We should have (i)s from 0 until n-1;

Coder
  • 28
  • 4
  • You should always check the return value of `scanf()` after reading user input. If the user entered something other than numbers, your `scanf()`s will have returned 0 *and not have written anything to their argument variables*. – DevSolar Apr 11 '23 at 07:04
  • 2
    Because you have declared `int n = 0;` and immediately `int x[n];` which gets translated to `int x[0];` which is equivalent to `int x;`. Your code is actually scanning all the numbers and is storing it in `int x[n]` but at the end of the loop, the last number is getting stored and all other numbers are getting overwritten – Gaurav Pathak Apr 11 '23 at 07:04
  • This is a FAQ, see [How to declare variable-length arrays correctly?](https://software.codidact.com/posts/283440) – Lundin Apr 11 '23 at 07:54

2 Answers2

3
int n = 0;
int x[n];

This attempts to declare a Variable Length Array of size zero, which already is undefined behaviour.

And even if that (having a VLA with room for zero elements) were somehow possible,

    scanf("%d", &x[i]);

...would also be undefined behavior.

DevSolar
  • 67,862
  • 21
  • 134
  • 209
1

Let me try to explain the behavior of the code considering x86_64 architecture and gcc7.5x compiler.

This answer and this answer to the questions provides the direction of stack growth on different architectures for x86 it is downward, meaning the stack will grow from high memory to low memory address (0xFF --> 0x00)

If we slightly modify your code to print the addresses and values of the variable during runtime as shown:

#include <stdio.h>

int main()
{
    int m = 0;
    int n = 0;
    int x[n];
    
    printf("Size of int: %d\n", sizeof(int));
    printf("Address of\nm: %p\nn: %p\nx: %p\n", &m, &n, &x);
    printf("Enter a value of m: ");
    scanf("%d", &m);
    printf("Enter a value of n: ");
    scanf("%d", &n);
    
    for(int i = 0; i < n; i++)
    {
        scanf("%d", &x[i]);
        printf("Addr: %p\n", &x[i]);
    }
    for(int j = 0; j < n; j++)
    {
        printf("x[%d]: %d\n", x[j]);
    }
    printf("m: %d n: %d\n", m, n);
}

After compiling and running the above code on x86, we can notice that the addresses to the local variables get assigned in decreasing order as shown below for one of the code execution session:

Address of
m: 0x7ffdfb2b1704
n: 0x7ffdfb2b1700
x: 0x7ffdfb2b16f0

If you notice the difference in the address of n and x, they are 16-Bytes apart, ideally they should have been 4-bytes apart on stack.
But as int x[n] is declared to be an array of 0 integers ideally it should occupy 4-bytes of memory, even though the underlying compiler and architecture mechanism is treating it as an array of 4 integers, which gets translated to
4 integers * sizof(int) = 16-bytes

Looking at further execution output of the code, we can see that the value of n at address 0x7ffdfb2b1700 gets overwritten as the code executes further.

Note that the code will work properly for values of n < 5, because for the value of n = 4 the loop will terminate at n = 3 in which case the value of n will not get overwritten because of loop iteration as (n * sizeof(int)) ==> 3 * 4 = 12-bytes:

Enter a value of m: 1
Enter a value of n: 10
1
Addr: 0x7ffdfb2b16f0
2
Addr: 0x7ffdfb2b16f4
3
Addr: 0x7ffdfb2b16f8
4
Addr: 0x7ffdfb2b16fc
5
Addr: 0x7ffdfb2b1700  <--- address of variable `n` getting overwritten with new value
x[1]: 0
x[2]: 1
x[3]: 2
x[4]: 3
x[5]: 4
m: 1 n: 5

And this is the reason why @DevSolar answered that this will result in an undefined behavior.

I hope I was able to provide some clarification.

Gaurav Pathak
  • 1,065
  • 11
  • 28
  • 1
    I am of two minds about "explaining" undefined behavior. For the most part it's an exercise in futility, since you must not **rely** on that knowledge, ever. Hence I tend to reserve such discussions for "among the experienced", like when discussing potential exploits, and not delve into "what happens on CPU X with compiler Y under setting Z" as an answer to a beginner question. – DevSolar Apr 11 '23 at 10:16