I have a snippet of code that defines (what I believe to be) an empty array, i.e. an array containing no elements:
int a[] = {};
I compiled the snippet with gcc with no problem
A colleague attempting to get that same code to compile under MSVS made the modification:
int* a = NULL;
No he obviously thought that was an equivalent statemnent that would be acceptable to the MSVS compiler.
However, later in the code I retrieve the no. of elements in the array using the following macro:
#define sizearray(a) (sizeof(a) / sizeof((a)[0]))
when doing so:
sizearray({}) returns 0
this is as I would expect for what I believe to be a definition of an empty array
sizearray(NULL) returns 1
I'm thinking that sizeof(NULL)/sizeof((NULL)[0])) is actually 4/4 == 1
as NULL == (void*)0
My question is whether:
int a[] = {};
is a valid way of expressing an empty array, or whether its poor programming practice.
Also, is it the case that you can't use such an expression with the MSVS compiler, i.e. is this some sort of C99 compatibility issue?
UPDATE:
Just compiled this:
#include <stdio.h>
#define sizearray(a) (sizeof(a) / sizeof((a)[0]))
int main()
{
int a[] = {};
int b[] = {0};
int c[] = {0,1};
printf("sizearray a = %lu\n", sizearray(a));
printf("sizearray b = %lu\n", sizearray(b));
printf("sizearray c = %lu\n", sizearray(c));
return 0;
}
using this Makefile:
array: array.c
gcc -g -o array array.c
My compiler is:
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
compiles without any complaint, output looks like this:
bph#marvin:~/projects/scratch/c/array$ ./array
sizearray a = 0
sizearray b = 1
sizearray c = 2
very curious? could it secretly be a C++ compiler, not a C compiler?
Tried John Bodes suggestion of additional compiler flags and can confirm that the compilation does then fail:
gcc --std=c11 --pedantic -Wall -g -o array array.c
array.c: In function ‘main’:
array.c:7:15: warning: ISO C forbids empty initializer braces [-Wpedantic]
int a[] = {};
^
array.c:7:9: error: zero or negative size array ‘a’
int a[] = {};
^
Makefile:2: recipe for target 'array' failed
make: *** [array] Error 1
Empty initializers are invalid in C. So
int a = {};
is ill-formed. See 6.7.9 Initialization.
sizearray(NULL) is not valid either. Because the sizearray macro would expand to:
sizeof 0 /sizeof 0[0])
If NULL is defined as 0. This is not valid because the 0[0] isn't valid because of there's no pointer or array involved (as required for pointer arithmetic - remember a[b] is equivalent to *(a + b)).
Or, it would expand to:
(sizeof(((void *)0)) / sizeof((((void *)0))[0]))
if NULL was as ((void*)0). This is not valid because pointer arithmetic is not allowed on void pointers. See 6.5.6, 2 and void* is an incomplete type. Similar issue be present for whatever the definition of NULL is in an implementation (C standard is flexible with the definition of null pointer constant i.e., NULL. See 7.19, 3).
So in both cases, what you see is compiler specific behaviours for non-standard code.
This is not an array, but it's not a scalar either: it's a syntax error.
The C11 draft says, in §6.7.9.11 (Initialization Semantics):
The initializer for a scalar shall be a single expression, optionally
enclosed in braces. The initial value of the object is that of the
expression (after conversion); the same type constraints and conversions as
for simple assignment apply, taking the type of the scalar to be the
unqualified version of its declared type.
But there has to be something between the braces, it can't be empty.
So I'd argue that the question is missing something, and this was not the actual code.
It's not an array, it's the brace initialization syntax.
Short word, you can write this:
int a = {1234};
It does not initialize a with an array, it just assigns 1234. If there are 2 or more values, that's be an error.
Brace initialization disables value truncating, so:
char b = 258; // Valid, same as b = 2
char b = {258}; // Wrong, can't truncate value in braces
And empty braces are just zero-initializers, so int a = {} is equivalent to int a = {0}
Related
When declaring a variable or pointer, the compiler assumes the variable or pointer itself is already declared when being assigned as a value during declaration.
I have tried both gcc and clang and they compile the "faulty" code without complaining.
CASE 1: This will not compile since "a" is not declared:
void main()
{
int b=sizeof(a);
}
CASE 2: This compiles without a problem:
void main()
{
int a=sizeof(a);
}
Shouldn't the compiler generate the "a is undeclared" error instead, just like in case 1?
Shouldn't the compiler generate the "a is undeclared" error instead, just like in case 1?
Why? It just saw you declare a.
int a = sizeof(a);
// ^--- here it is, before its first use
The declaration of a variable begins after its declarator is seen, right before its (optional) initializer. So you can even write the truly faulty
int a = a;
Note however that in your case there is nothing faulty being done. The result of sizeof depends only on the type of a, and the type is known. This is a well defined initializtion (with a conversion from size_t to int, but not one to be worried about).
sizeof is not a function depending on the value of a; it is a builtin that is evaluated at compile time, so it becomes equivalent to
int a = 4;
I was writing the following code
#include<stdio.h>
void fun(int n) {
int a[n] = {0};
}
void main() {
int a[4] = {0};
int i = 0;
fun(3);
}
and got this error
test.c: In function 'fun':
test.c:5:5: error: variable-sized object may not be initialized
while if I change the function fun to:-
void fun(int n) {
int a[n], i = 0;
for(i = 0; i < n; i++) {
a[i] = 0;
}
}
it works fine.
I know that the error is occuring because it's not allowed in the compiler's specification but what i want to know is why is it not possible to be implemented?
Is it due to some compile time or run time evaluation issue?
I have seen the answer of other question but i need a more elaborated answer.
Variable Length Array cannot be initialized like this
int a[n]={0};
From C Standards#6.7.9p3 Initialization [emphasis added]
The type of the entity to be initialized shall be an array of unknown size or a complete object type that is not a variable length array type.
Using loop is one way to initialize the variable length array's. You can also use memset like this:
memset(a, 0, sizeof a);
Additional:
The C99 compiler should support the Variable Length Array's but they were made optional in C11 compiler.
An easy way is to send the size of array along with other parameters
Remember that you should send size before an array with that size
void fun(int n,int a[n]){
}
Although you have other alternatives like sizeof()
As an addition to H.S. answer:
From C Standards#6.7.9p3 Initialization [emphasis added]
The type of the entity to be initialized shall be an array of unknown size or a complete object type that is not a variable length array type.
This is probably because Initializers have to be constant expressions. Constant expression have a definite value at compile time.
A {0} is an incomplete Initializer and the compiler would fill up the remaining values with 0.
If you have a VLA the compiler does not know the length of the Array and thus can not generate the initializer for it.
This depends on your compiler actually.
In old C You couldn't have variable size arrays. In function fun you use a as an array with variable size n. This is not allowed in old C. However, C99 and C11 standards support variable size arrays, so perhaps you have an old compiler. (DevC?) If you want to use some type of variable arrays in older C compilers, you have to use malloc and free.
Perhaps you wanted to write this code in C++? C++ doesn't support variable size arrays also, but the gcc compiler can run this code.
Check this out:
Why aren't variable-length arrays part of the C++ standard?
If you are using DevC, I think that if you change your file from test.c to test.cpp this code will work.
When I try to run this, it gives me an error saying that the value in variable a isn't constant. That doesn't make sense to me because I explicitly made the variable a constant. Does the size of an array have to more constant than that? Meaning, only #define a 5, or initializing it as int arr[5] or using malloc? What is wrong with what I did?
int main{
const int a = 5;
int i;
int arr [a];
for (i = 0; i < 5; i++) {
arr[i] = i * 2;
}
printf("%d", arr[1]);
return 0;
}
In C, const should be read as read-only. It doesn't define a compile time.
const int a = 5;
Here a, is not a constant expression as required by the C standard:
6.7.9 Initialization
4 All the expressions in an initializer for an object that has static or thread storage duration shall be constant
expressions or string literals.
So the error indicates you are using a C89/C90 compiler. You can read the input from user for a and declare a variable length array, which is a C99-feature, which has automatic storage duration.
Using #define is another way. But it's simply a textual replacement and defines an array with automatic storage duration. It's same as defining int arr[5]; yourself.
if you want to allocate memory on dynamic storage (commonly known as "heap"), you have to use malloc() family functions, which will have lifetime thoughout the program execution until you call free() on it.
(Note that this behaviour of const is only in C. C++ differs in this and will work as you expected).
If I compile the code in C89, it fails with:
#include <stdio.h>
int main(){
const int a = 5;
int i;
int arr [a];
for (i = 0; i < 5; i++) {
arr[i] = i * 2;
}
printf("%d", arr[1]);
return 0;
}
$ gcc -Wall -Wextra -std=c89 -pedantic-errors test.c
test.c: In function âmainâ:
test.c:7:4: error: ISO C90 forbids variable length array âarrâ [-Wvla]
int arr [a];
^
because C89 doesn't support VLAs (Although gcc supports it as an extension even in C89/C90). So if you are using a compiler that doesn't support C99 then you can't use VLAs.
For example, visual studio doesn't fully support all C99 and C11 features. Although, Visual studio 2015 support most C99 features, VLAs are not one of them.
But the same code compiles in C99 and C11 without any problem:
$ gcc -Wall -Wextra -std=c99 -pedantic-errors t.c
$ gcc -Wall -Wextra -std=c11 -pedantic-errors t.c
It's because variable length arrays (VLAs) were added in C99. Note that VLAs have been made optional in C11 standard. So an implementation may not support VLAs in C11.
You need to test against __STDC_NO_VLA__ to check if VLAs are not supported by your implementation.
From 6.10.8.3 Conditional feature macros
__STDC_NO_VLA__
The integer constant 1, intended to indicate that the implementation does not support variable length arrays or variably
modified types.
I personally do not use VLAs as the allocation failure can't be portably found if the array size is reasonably large. E.g.
size_t size = 8*1024;
int arr[size];
In the above fragment, if arr allocation failed, you won't know it until runtime. What's a "small enough" size for which the memory allocation is platform dependent. So on one machine, 1MB allocation may succeed and another it may fail and worse part is that there's no way to catch this failure.
Thus the use of VLAs is limited and can only be used with small arrays that you know will always succeed on a given platform. But in that I would simply hard-code the array size and take care of the boundary conditions.
Maybe use an enum to define the value of a.
enum { a = 5 };
int arr [a];
Perhaps this is not the intention of an enum but the members of enums are the closest thing to a constant in C. Unlike the common practice of defining everything using #define, the visibility of a is limited by scope and here it is the same as arr.
A const-qualified variable is not the same thing as a constant expression; a constant expression has its value known at compile time, whereas a const-qualified variable (normally) doesn't (even though it looks like it should).
Note that in C99 and later, it's possible to declare variable-length arrays, where the array size isn't known until run time. You have to use a C99 or later compiler, and given that the feature was made optional in the 2011 standard, you have to check a feature macro to see if VLAs are available:
static const int a = 10; // a is not a constant expression
#if defined( __STDC__ ) && defined ( __STDC_VERSION__ ) && __STDC_VERSION__ >= 199901L && !defined( __STDC_NO_VLA__ )
/**
* VLAs are available in this environment
*/
#define USE_VLA 1
#endif
#ifdef USE_VLA
int arr[a];
#else
/**
* VLAs are not available, either because it's a pre-1999 implementation,
* or it's a post-2011 implementation that does not support optional
* VLAs. We'll have to use dynamic memory allocation here, meaning we'll
* also need an explicit free call when we're done with arr
*/
int *arr = malloc( sizeof *arr * a );
#endif
...
do_something_interesting_with( a );
...
#ifndef USE_VLA
free( a );
#endif
At least up until very recently, Microsoft's C compiler did not support VLAs. They have been adding some C99 features, though, such as mixed declarations and code, so maybe the latest version supports VLAs. I don't know.
I'm curious about the following expression:
int ints[] = { 1, 2, 3 };
This seems to compile fine even in c89 land with clang. Is there documentation about this? I can't seem to figure out the correct terminology to use when searching for it (and I'd rather not go through and read the entire c89 spec again).
Is this an extension? Is the compiler simply inferring the size of the array?
EDIT: I just remembered you guys like chunks of code that actually compile so here it is:
/* clang tst.c -o tst -Wall -Wextra -Werror -std=c89 */
int main(int argc, const char *argv[]) {
int ints[] = { 1, 2, 3 };
(void)(ints); (void)(argc); (void)(argv);
return 0;
}
It's part of standard C since C89:
§3.5.7 Initialization
If an array of unknown size is initialized, its size is determined by the number of initializers provided for its members. At the end of its initializer list, the array no longer has incomplete type.
In fact, there is an almost exact example:
Example:
The declaration
int x[] = { 1, 3, 5 };
defines and initializes x as a one-dimensional array object that has three members, as no size was specified and there are three initializers.
Is this an extension?
no, this is standard, for all versions of the C standard
by the = the array type is "incomplete" and then is completed by means of the initialization
Is the compiler simply inferring the size of the
array?
yes
This compiles in gcc with no errors or warnings even with -Wall option
meaning that array bounds are checked at run-time and hence compiler can't detect the error
#include<stdio.h>
int main()
{
int a[2][3][4];
a[1][2][100] = 4 ;
return 0;
}
However,
#include<stdio.h>
int main()
{
int a[2][3];
a[1][2][100] = 4 ;
return 0;
}
this generates an error while compiling as :
$ gcc sample.c -Wall
sample.c: In function ‘main’:
sample.c:7: error: subscripted value is neither array nor pointer
Why is this so ? in both the two codes a[1][2][100] is invalid . Still how can the compiler detect this is code2 and not in code1.
Specially when every compiler flattens all multidimensional array into corresponding single dimension arrays, then how can the compiler be selectively aware of this flaw in the code.
Explanation or mention of some book or article where the proper explanation resides will be gratefully accepted :)
The difference is the types. C does no bounds checking but it does do (static) type checking. The type of a in your first example is int[][][] but the type in the second example is int[][].
The "flattening" that you refer to happens in code generation, which is (conceptually, at least) after type checking.
First, array bounds are not checked at runtime or at compile time. Be careful out there.
Second, your second case gives an error because you have a mismatch in array dimension - you're using three subscript operators ([]) on a 2D array. Just because the array happens to be laid out in memory as an array of arrays doesn't mean there is any actual type changing going on with the variable.
Array subscripting is described in the C standard section 6.5.2.1 Array subscripting.
Given
int a[2][3];
the compiler will determine that a[1][2] is of type int. Therefore, accessing element [100] of this is equivalent to:
int x;
x[100] = 4;
This would give you the same error about the subscripted value.