Is there a difference between char a[n][m] & char a[][m]?

Viewed 78

Also why it is not printing garbage when I provide m > my column values like in the second case? Any specific reason?

#include <iostream>
using namespace std;

int main()
{
    char a[2][5] = {{'a', 'b', 'c', 'd', 's'}, {'e', 'f', 'g', 'h', 'q'}};
    cout << a[0] << endl; // abcdsefghq!V
    cout << a[1] << endl; // efghq!V
    
    char a1[][6] = {{'a', 'b', 'c', 'd', 's'}, {'e', 'f', 'g', 'h', 'q'}};
    cout << a1[0] << endl; // abcds
    cout << a1[1] << endl; // efghq

    return 0;
}
5 Answers

Is there a difference between char a[n][m] & char a[][m]?

Depends on context. First is an array of n arrays of m chars. Second is an array of unknown bound of arrays of m chars. So, they are different types.

However given a braced init list such as in the example, the array of unknown bound will be adjusted to be an array of known bound, where the bound is deduced from the number of initialisers. If the deduced number of initialisers is n, then the adjusted type would be exactly the same as char[n][m].

In Short, the anwer is: In general the difference is that former is an array of known bound and latter is an array of unknown bound, but in this context, there is no difference after the array of unknown bound is adjusted to be an array of known bound.


Also why it is not printing garbage

When you call the stream insertion operator with an array, the operand decays to a pointer to first element. The stream requires that a char* argument is a pointer to a null terminated array of characters. Violating this precondition results in undefined behaviour.

Your arrays in a do not contain the null terminator character. The behaviour of your program is undefined.


You should avoid undefined behaviour. Never pass non-null terminated arrays to character streams.

char a[2][5] explicitly specifies the number of rows. char a1[][6] is telling the compiler to imply the number of rows from the initializer.

For example,

char b[10][5] = {"a", "b", "c"};

creates 10 rows because the number is explicitly specified, but

char b1[][5] = {"a", "b", "c"};

creates only 3 rows because there are 3 rows in the initializer.

The second case has 6 characters in each rows and specified 5 characters for each rows. The remaining 1 character is initialized to zero, and it works as the terminator of C-style strings. On the other hand, the first case has only 5 characters in each rows and the 5 characters are fully specified, so there are no terminating null-characters in the array. Printing that as C-style string will lead to dangerous out-of-range access.

In the case of a, when printing a[0] or a[1], these have type char [5] which decays to char * when passed to the << operator. So they're printed as C strings. However, you don't actually have null terminated strings so operator << reads past the bounds of the array, triggering undefined behavior.

In the case of a1, you don't specify the first dimension so its size is taken from the initializer which has two elements, so the size of this dimension is 2. This variable also has a larger size for the second array dimension, and the initializers don't explicitly initialize all elements, so those not explicitly initialized are set to 0. So when you print a1[0] and a1[1] you do have null terminated strings and so you get the expected output.

For the title

Is there a difference between char a[n][m] & char a[][m]?

No, so long as in the second case you provide n rows in your initialization.


This isn't the actual case in your question though. In your question you have a char a[2][5] and you fill that up with characters. Because none of those character are a null terminator, passing those rows to cout has undefined behavior. cout requires that the char array (c-string) has a null terminator.

In your second case you have a char a[][6] and you provide 2 rows of 5 characters each for the initialization. That makes the type a char a[2][6], but because you only provided 5 initializers for 6 characters, the compiler is automatically going to zero initialize that missing sixth character, which is a null terminator, so passing those rows to cout works correctly.

There are a few different things going on here.

First of all, if you give an explicit array size but don't specify all elements in the initializer, the remaining elements will be zero-initialized. For example:

int a[5] = {1, 2, 3};  // equivalent to {1, 2, 3, 0, 0}

That is the primary difference between the two examples - one specifies the sizes explicitly, and the other infers the sizes based on the initializers.

Next, arrays "decay" into pointers, so a char[] acts like a char*, a.k.a. a c-string. c-strings must be null-terminated. You print garbage in the first example because you are missing the null terminator. You don't print garbage in the second example because the array has a (hidden) null terminator because the final element was zero-initialized.

Related