I am learning C and I am playing around with pointers and arrays. I am trying to create an array of pointers with the code below:
const int NUM_P = 50; // Line 10
char *pS[NUM_P] = { NULL }; // Line 11
I am getting the following warnings and errors when I compile:
→ gcc array.c -o array
array.c: In function ‘main’:
array.c:11: error: variable-sized object may not be initialized
array.c:11: warning: excess elements in array initializer
array.c:11: warning: (near initialization for ‘pS’)
I cannot figure this error out, I have looked online and been unable to find a explanation and solution of the problem.
Can anyone help out?
Cheers
Eef
Your main problem is the initialization line (= {NULL};) because apparently you can't initialize an array that way when the array's size is a variable. If you use #define NUM_P 50 to force the array size to be a true compile-time constant, then the array would not have a variable size, and your initialization method would compile fine.
As a side note, the difference between #define NUM_P 50 and const int NUM_P = 50 is that C89 does not allow the use of variables as array dimensions, but C99 does; however, C99 is not fully supported by all compilers yet. The fact that gcc accepts your character array with a size of NUM_P is not C89-compliant.
Technically, you could use a flag on the command line to tell gcc that you want to compile in C99:
gcc -std=c99 FILE.c
By the way, when learning C, start at the top of the errors listed and work down from there. In this case, the warnings are not of any concern until you've taken care of the error at the top: "variable-sized object may not be initialized".
Related
Context
In C, I have a function which take an array as a parameter. This parameter is used as an output in this function. The output is always the same size. I would:
make the required size clear for anyone reading the code (it will already be in the function comments, though),
ideally the compilation to output a warning or an error so I can prevent problems at compile-time instead of run-time.
A potential solution
I found here: https://hamberg.no/erlend/posts/2013-02-18-static-array-indices.html something which look like a solution but I am not able to get a warning or an error during the compilation if I try to pass a smaller array than the required size.
Here is my complete program main.c:
void test_array(int arr[static 5]);
int main(void)
{
int array[3] = {'\0'};
test_array(array); // A warning/error should occur here at compilation-time
// telling me my array does not meet the required size.
return 0;
}
void test_array(int arr[static 5])
{
arr[2] = 0x7; // do anything...
}
Contrary to this blog, I use gcc (version 7.4.0) instead of clang with the following command:
gcc -std=c99 -Wall -o main.out main.c
In my code, we can see that the test_array() function needs a 5 elements array. I am passing a 3 elements one. I would expect a message from the compiler about this.
Question
In C, how to force a function parameter being an array to be of a given size? In case it is not, it should be noticeable at compilation-time.
If you pass a pointer to the array instead of a pointer to its first element, you will get an incompatible pointer warning:
void foo(int (*bar)[42])
{}
int main(void)
{
int a[40];
foo(&a); // warning: passing argument 1 of 'foo' from incompatible pointer type [-Werror=incompatible-pointer-types]
// note: expected 'int (*)[42]' but argument is of type 'int (*)[40]'
int b[45];
foo(&b); // warning: passing argument 1 of 'foo' from incompatible pointer type [-Werror=incompatible-pointer-types]
// note: expected 'int (*)[42]' but argument is of type 'int (*)[45]'
}
Compile with -Werror to make it an error.
godbolt
To test that the size of the array (not pointer) being passed is at least 5 elements, a Static_assert can be used, and the necessary _Static_assert can be inserted via a preprocessor macro.
After the declaration of the function, insert:
#define test_array(arr) \
do \
{ \
_Static_assert(sizeof (arr) / sizeof *(arr) >= 5, "Array is too small."); \
test_array(arr); \
} while (0)
(The do … while (0) is a classic way of defining a macro to act syntactically like a statement, so that it can be followed by a ; and flow as expected with if statements and such.)
Before the definition of the function, insert:
#undef test_array
(If any more uses of the function follow, another copy of the #define must be inserted. Alternatively, the function could be defined early in the source file and followed by a #define, eliminating any need for further #undef or #define directives.)
Generally, such code is unlikely to be useful, as programs often pass pointers to the first elements of the arrays (or to elements in the middle of arrays), and it is not possible to test how many elements are at the space a pointer is pointing to. So this is useful only in code where we require an array to be given as the argument. And that requirement is not enforced by this code.
I have this extremely trivial piece of C code:
static int arr[];
int main(void) {
*arr = 4;
return 0;
}
I understand that the first statement is illegal (I've declared a file-scope array with static storage duration and file linkeage but no specified size), but why is it resulting in a linker error? :
/usr/bin/ld: /tmp/cch9lPwA.o: in function `main':
unit.c:(.text+0xd): undefined reference to `arr'
collect2: error: ld returned 1 exit status
Shouldn't the compiler be able to catch this before the linker?
It is also strange to me that, if I omit the static storage class, the compiler simply assumes array is of length 1 and produces no error beyond that:
int arr[];
int main(void) {
*arr = 4;
return 0;
}
Results in:
unit.c:5:5: warning: array 'arr' assumed to have one element
int arr[];
Why does omitting the storage class result in different behavior here and why does the first piece of code produce a linker error? Thanks.
Empty arrays static int arr[]; and zero-length arrays static int arr[0]; were gcc non-standard extensions.
The intention of these extensions were to act as a fix for the old "struct hack". Back in the C90 days, people wrote code such as this:
typedef struct
{
header stuff;
...
int data[1]; // the "struct hack"
} protocol;
where data would then be used as if it had variable size beyond the array depending on what's in the header part. Such code was buggy, wrote data to padding bytes and invoked array out-of-bounds undefined behavior in general.
gcc fixed this problem by adding empty/zero arrays as a compiler extension, making the code behave without bugs, although it was no longer portable.
The C standard committee recognized that this gcc feature was useful, so they added flexible array members to the C language in 1999. Since then, the gcc feature is to be regarded as obsolete, as using the C standard flexible array member is to prefer.
As recognized by the linked gcc documentation:
Declaring zero-length arrays in other contexts, including as interior members of structure objects or as non-member objects, is discouraged.
And this is what your code does.
Note that gcc with no compiler options passed defaults to -std=gnu90 (gcc < 5.0) or -std=gnu11(gcc > 5.0). This gives you all the non-standard extensions enabled, so the program compiles but does not link.
If you want standard compliant behavior, you must compile as
gcc -std=c11 -pedantic-errors
The -pedantic flag disables gcc extensions, and the linker error switches to a compiler error as expected. For an empty array as in your case, you get:
error: array size missing in 'arr'
And for a zero-length array you get:
error: ISO C forbids zero-size array 'arr' [-Wpedantic]
The reason why int arr[] works, is because this is an array declaration of tentative definition with external linkage (see C17 6.9.2). It is valid C and can be regarded as a forward declaration. It means that elsewhere in the code, the compiler (or rather the linker) should expect to find for example int arr[10], which is then referring to the same variable. This way, arr can be used in the code before the size is known. (I wouldn't recommend using this language feature, as it is a form of "spaghetti programming".)
When you use static you block the possibility to have the array size specified elsewhere, by forcing the variable to have internal linkage instead.
Maybe one reason for this behavior is that the compiler issues a warning resulting in a non-accessed static variable and optimizes it away - the linker will complain!
If it is not static, it cannot simply be ignored, because other modules might reference it - so the linker can at least find that symbol arr.
I've noticed in some source code the line:
if(pthread_create((pthread_t[]){}, 0, start_thread, pthread_args)) {
...
It works correctly, but how to understand the first argument?
It seems, that curly braces converts to pthread_t[] type.
P.s. I googled, but didn't find answer, only some guesses (some form of initialization, or legacy feature of c?)
This is a compound literal, with a constraint violation since initializer braces cannot be empty:
(pthread_t[]){}
Using gcc -std=c99 -Wall -Wextra -Wpedantic this produces the warning:
compound_literal_pthread.c:6:36: warning: ISO C forbids empty initializer braces [-Wpedantic]
pthread_t *ptr = (pthread_t []){};
The result seems to be a pointer to pthread_t, though I don't see this behavior documented in the gcc manual. Note that empty braces are allowed as initializers in C++, where they are equivalent to { 0 }. This behavior seems to be supported for C, but undocumented, by gcc. I suspect that is what is happening here, making the above expression equivalent to:
(pthread_t[]){ 0 }
On my system, pthread_t is a typedef for unsigned long, so this expression would create an array of pthread_t containing only a 0 element. This array would decay to a pointer to pthread_t in the function call.
It's a compound literal as mentioned by #some-programmer-dude.
In this specific case it is use to create an array to store the thread_id and discharge it later without the need of creating an extra variable. That is necessary because pthread_create does not accept NULL as argument for thread_id.
You're creating an array with pthread[]. You can pass values in the curly braces if you define a length for the argument.
What you're dealing with there is an array initializer, which happens to be the empty array. You'd usually find it as:
int my_array[] = (int[]){10, 20, 30};
And it would initialize my_array to contain three elements. Here there are no elements, hence the awkward syntax.
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.