0
#include<bits/stdc++.h>

using namespace std;

int arr[10][2];

int main(){
    memset(arr,-1,sizeof(arr));
    for(int i=0;i<10;i++)
        cout<<arr[i][i]<<" ";
    return 0;
}
result = -1 -1 -1 -1 -1 -1 -1 0 0 0

I declared an array of size 2 * 10 as a global variable and initialized it to -1 using the memset function. However, how did this result come out when I approached the index like arr[i][i] while using the for statement?

Question.

  1. How can there be values of arr[3][3], arr[4][4]... that are not declared?
  2. Why are the values of arr[7][7], arr[8][8], arr[9][9] different from the previous values?

Thank you.

Oleg Cherednik
  • 17,377
  • 4
  • 21
  • 35
burigori
  • 13
  • 2
  • 4
    Reading outside of array bounds causes undefined behavior: anything can happen, including random values. Some compilers can try to check array bounds for you, if you configure them to do so. – HolyBlackCat Jan 23 '22 at 13:10
  • 1
    [Why should I not #include ?](https://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h) Don't do it even for small examples. – Some programmer dude Jan 23 '22 at 13:11
  • Also prefer `std::fill` to `memset` for anything other than arrays of bytes. It works in this case, but if you select any value other than -1 or 0, it will do a wrong thing. – HolyBlackCat Jan 23 '22 at 13:13
  • Also, you say that you "declared an array of size 2 * 10" which is wrong. First you *defined* an array. And you defined an array of **`10`** elements. Each element in `arr` is in turn an array of two elements. Also note that `memset` works on a byte-level, it sets all *bytes* to the byte value corresponding to `-1`. Basically only `-1` and `0` will work as expected when using `memset`, anything else will most likely give you unexpected results. – Some programmer dude Jan 23 '22 at 13:14
  • 1
    @Someprogrammerdude A definition also counts as a declaration. It's a less specific wording, but not wrong. :P – HolyBlackCat Jan 23 '22 at 13:15

2 Answers2

3

Your program has undefined behavior because you're accessing elements outside the bounds of the array arr.

When you wrote:

int arr[10][2]; //arr is a 2D array

The above statement defines a 2D array. This means, arr has 10 elements and each of those 10 elements are themselves an array of 2 int elements.

This also means that you can only access(safely) the elements:

arr[0][0]   arr[0][1]
arr[1][0]   arr[1][1]
arr[2][0]   arr[2][1]
...
...
arr[9][0]   arr[9][1]

And if you try to(which you do in your program inside for loop) access any other elements outside the above bounds, you'll get undefined behavior.

Undefined behavior means anything1 can happen including but not limited to the program giving your expected output. But never rely(or make conclusions based) on the output of a program that has undefined behavior.

So the output that you're seeing(maybe seeing) is a result of undefined behavior. And as i said don't rely on the output of a program that has UB.

So the first step to make the program correct would be to remove UB(in this case by making sure that index don't go out of bound). Then and only then you can start reasoning about the output of the program.

Solution 1

One alternative is to use std::vector as shown below:

//create a 2D vector with 10 rows and 2 columns where each int element has value -1
std::vector<std::vector<int>> arr(10, std::vector<int>(2, -1)); 

Solution 2

You should consider using std::fill instead of memset:

std::fill(*arr, *arr + 10*2, -1);

1For a more technically accurate definition of undefined behavior see this where it is mentioned that: there are no restrictions on the behavior of the program.

Jason
  • 36,170
  • 5
  • 26
  • 60
0

The answer is pretty simple when you clearly

  • Array is the object in memory that holds the continuous memory region.
  • 2D array is like 1D array of 1D arrays, each next row is placed next to the previous one in the memory.
  • The name of the array is a reference to the memory region.
  • The array type (size of this value) is how the compiler knows how many bytes are taken by each element.
  • cpp does not check the element size and you are free to use any index, this is just to retrieve the unpredictable value that holds next to the array in the memory.

E.g. you have byte arr[3][2] - it means that we define an array with int elements with 3 rows with 2 columns each.

sizeof(byte) = 1 - each element in the array takes exactly 1 byte and this is how memory will be allocated.

0xA 0xB 0xC 0xD 0xE 0xF 0xAA 0xBB 0xCC

(first 6 bytes are the array; list 3 bytes - are another data in the memory; arr is the pointer to the first element)

memset(arr,0,sizeof(arr)) - you clear or initialize the array; the memory will be like this:

0x0 0x0 0x0 0x0 0x0 0x0 0xAA 0xBB 0xCC

arr[1][0] = 3 - set an element to 1; the memory will be like this:

0x0 0x0 0x3 0x0 0x0 0x0 0xAA 0xBB 0xCC

So how would you read this element:

arr[1][0];  // 3
*(arr + sizeof(byte) * 2);  // 3

As you can see you can easily read smth. behind the array and get unpredictable results:

arr[3][0];  // 0xAA
*(arr + sizeof(byte) * 7);  // 0xBB

P.S. I describe the general answer why you get this result. I do not make a point on another aspect like rotating, because according to CPU addressing rules, where byte could take 4 bytes in the memory, but the last 3 bytes will not be used).

Oleg Cherednik
  • 17,377
  • 4
  • 21
  • 35