Is Int Arr[ ] Valid C++

Is int arr[ ] valid C++?

Let us look at each of the cases.

Case 1

Here we have the statement

int a[]; //this is a definition so size must be known

This is not valid.

Case 2

Here we have the statement:

extern int b[];//this is a declaration that is not a definition

This is valid. Here the type of b is incomplete. Also, b has external linkage.

Case 3

Here we have:

int (*ptrB)[]; 

This is valid. We say that ptrB is a pointer to an incomplete type.

Case 4

Here we have:

struct Name
{
int k[]; //NOT VALID
};

This is not valid as from cppreference:

Any of the following contexts requires type T to be complete:

  • declaration of a non-static class data member of type T;

Case 5

Here we have:

void func()
{
ptrB++; //NOT VALID
}

This is not valid as from postfix increment's documentation:

The operand expr of a built-in postfix increment or decrement operator must be a modifiable (non-const) lvalue of non-boolean (since C++17) arithmetic type or pointer to completely-defined object type.

Case 6

Here we have:

void bar()
{
ptrB = &b;//NOT VALID
}

This is not valid as from cppreference:

The declared type of an array object might be an array of unknown bound and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points ("array of unknown bound of T" and "array of N T") are different types.

Case 7

Here we have:

void bar()
{
ptrB++; //NOT VALID

This is not valid as from cppreferene:

The type of a pointer to array of unknown bound, or to a type defined by a typedef declaration to be an array of unknown bound, cannot be completed.

So we will get the same error as in case 5.

Case 8

Here we have:

int main()
{
int c[];
}

This is not valid since this is a definition and so size must be known.

Case 9

Here we have:

int main()
{
extern int d[]; non-defining declaration
}

This is valid. d has external linkage.

The difference of int arr[] = {} and int arr[] in C

By default, the array elements are uninitialized, which means they will contain garbage values:

int arr[4];

Using a curly brace initializer, you can set the initial values explicitly, e.g.

int arr[4] = {1, 2, 3, 4};

But if the number of numbers in the braces is less than the length of the array, the rest are filled with zeroes. That's what's going on in this case:

int arr[4] = {};

Note that this is not valid in C, only in C++, but your compiler apparently allows it anyway. In standard C, you must write at least one value:

int arr[4] = {0};

Is this array declaration valid in C language?

These need to be at the end of the structure so that the structure's memory can be extended to include them. There's no way to "accordion" out from the middle.

The way you add to this is to "over-allocate" for that structure, like sizeof(struct site) + sizeof(char) * N. The additional memory you allocate becomes available for use as part of that name property.

In general this is a bad plan for simple things like this where what you really need is char* and to copy any string buffer allocations in there either with malloc() and strcpy() or strdup() if you have access to POSIX functions.

So you have two options:

struct site
{
int no_of_pages;
char name[];
};

Where this necessarily means you cannot have any other "extensible" properties, or this:

struct site
{
char* name;
int no_of_pages;
};

Where you can have as many as you want.

A better example for where this technique is useful is if you have a variable length array with metadata, like this:

struct page {
char *name;
int links_count;
struct link links[];
}

Where now you can over-allocate and make use of this links array at the end easily. This could be converted to struct link links* and allocated independently, but that involves two allocations instead of one, so it might have drawbacks. You need to consider use of this technique very carefully.

Error while declaring array int arr[]; in C++ (valid in Java though)?

In C++, we have to specify the size of the array when we declare it. This is a design choice from the creator of C++ that goes back to its C origins.

On the other hand, in Java, we only declare a reference to an array object with int arr[]. Then we allocate memory for the array with something like arr = new int[10] to specify its size.

In Java, arrays are objects, just like String or ArrayList. But that is not the case in C++.

Is it OK/valid to intialize an array of integer type inside of a structure to NULL?

"Is it OK to make a global stack allocated array point to NULL?"

"Is the init(ialization) of the struct at foo_arr[2] valid?"

No, it is not OK and not valid. At least not in the way you did (C syntax violation with {NULL} - it needs to be {{NULL}} to get compiled).

typedef struct foo
{
bool bar[4];
} foo;

foo foo_arr[] = {
{{true, true, false, true}},
{{false, true, false, true}},
{{NULL}}
};

Indeed, under some requirements this would initialize all elements of bar to 0, but lets start at the beginning.

Why would you want to use NULL? NULL is used to compare and set pointers, not data objects. bar is not an array of pointers.

I indeed had a similar question myself, a few months ago:

  • Can I use NULL as substitution for the value of 0?

It is implementation-defined whether it is valid to use NULL to initialize data objects to 0, since the NULL macro either expands to 0 or (void*) 0. If it is the latter, the implementation needs to specify what happens if you assign a pointer to void to an integer type.

"Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type."

Source: C18, 6.3.2.3/6

So if either,

  1. NULL expands to 0 on your specific implementation, or
  2. The implementation specifies conversions from pointers to integers (the conversion from (void*) 0 to an data object),

this would indeed initialize all elements of bar in the third structure to 0, because of following:

In the first place, {{NULL}} set the first bool object in the array bar in the third structure of the array foo_arr to 0.

Implicitly it will also initialize all following three bool object to 0, because:

"If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than thereare elements in the array, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration."

Source: C18, 6.7.9/20

This means if you intialize the first bool object it will automatically initialize all others.



"If not, what is the correct way to point such array to NULL?"

As said above, not to NULL, to 0 or false. The array (elements) do not point to anywhere. They are not pointers! As indicated above you can just use {{0}} or {{false}}:

typedef struct foo
{
bool bar[4];
} foo;

foo foo_arr[] = {
{{true, true, false, true}},
{{false, true, false, true}},
{{0}};
};

How arrays with an empty size works in c++?

Does that mean that any array like a[n][10], a[m][10] can be passed to the above function?

Yes. The function signature void foo(char[][10]); is allowed as long as you pass a compatible argument, i.e. a char array with 2 dimensions in which the second has size 10.

In fact, technically, the argument will decay to a pointer, so it's the same as having void foo(char (*)[10]);, a pointer to array of ten, in this case chars. An argument of type char[] will also decay, this time to a pointer to char (char*).


Furthermore omitting the first dimension of an array is permitted on declaration as long as you initialize it. The fist dimension of the array will be deduced by the compiler based on the initialization content, i.e.:

int arr[]{1,2};

will have size 2 (arr[2]) whereas

int arr[][2]{{1,2},{2,4}};

will become arr[2][2] based on the aggregate initialization.

Is new int[][] a valid thing to do in C++?

The first example:

auto a = new int[10][10];

That allocates a multidimensional array or array of arrays as a contiguous block of memory.

The second example:

int** a = new int*[10];
for (int i = 0; i < 10; i++) {
a[i] = new int[10];
}

That is not a true multidimensional array. It is, in fact, an array of pointers and requires two indirections to access each element.

If arr[3] is an array and ptr is a pointer, then why do arr and &arr give same result but ptr and &ptr don't

Arrays are not pointers!

Arrays do decay to a pointer to their first element in all sorts of circumstances. For example std::cout << arr; actually prints the memory address of the first element of the array. std::cout << &arr; prints the memory address of the array. As the address of the first element is the same as the address of the array you see the same value.

However, just because they have the same value, does not mean they are the same. arr can decay to a int*, while &arr is a pointer to an array, a int(*)[3].

I hope the following will help to clear things up a little:

#include <iostream>
#include <type_traits>

void make_it_decay(int x[]) {
std::cout << std::is_same_v< decltype(x), int*> << "\n";
}

int main() {
int arr[3] = {1,2,3};

//std::cout << (arr == &arr) << "\n"; // does not compile !
std::cout << (arr == &(arr[0])) << "\n";

std::cout << std::is_same_v< decltype(arr), int[3]> << "\n";
std::cout << std::is_same_v< decltype(&arr),int(*)[3]> << "\n";
std::cout << std::is_same_v< decltype(&arr[0]), int* > << "\n";
make_it_decay(arr);
}

output:

1
1
1
1
1

I use decltype to infer the type of certain expressions and std::is_same_v to see if the expressions are of same type.

arr is of type int[3]. It is an array. It is not a pointer.

&arr is the address of the array. It is a pointer to an array with three elements, a int(*)[3].

&arr[0] even though it has the same value as &arr is of different type. It is a pointer to int, an int*.

When we pass arr to a function then it decays to a pointer to the first element of the array. And we can see that inside the function x is int*.


Now to your quesiton...

Above I tried to lay out what happens when you write std::cout << arr. Pointers are different, because ... well arrays are not pointers.

std::cout << ptr;  // prints the value ptr
std::cout << &ptr; // prints the address of ptr

Perhaps some visualization helps. The difference in types gets most apparent when incrementing the pointers

 -------------------
| arr |
-------------------
| 1 | 2 | 3 |
-------------------
^ ^ ^
&arr | &arr + 1
&arr[0] |
&arr[0] + 1


Related Topics



Leave a reply



Submit