Array declaration at file scope versus function scope [duplicate] - c

What is the concept when we define a global array with no dimension
This shows output as 16.
#include <stdio.h>
#include <stdlib.h>
int arr[];
int main(int argc, char *argv[])
{
arr[1] = 16;
printf("%d\n",arr[1]);
system("PAUSE");
return 0;
}
And even sizeof(arr) doesn't work. Why?

int arr[]; is a tentative definition there.
Clause 6.9.2, paragraph 2 says:
A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.
and example 2 in paragraph 5 of that clause clarifies:
If at the end of the translation unit containing
int i[];
the array i still has incomplete type, the implicit initializer causes it to have one element, which is set to zero on program startup.
So at the end of the translation unit, your array arr has type int[1]. Before the end, its type is incomplete, hence sizeof doesn't work, since in main, the array type is still incomplete.
Accessing arr[1] invokes undefined behaviour, since arr has only one element.

GCC assumes that arr should only have one element. The fact that you can access other elements than arr[0] without segfaulting is just a coincidence. For instance, on my machine I can access arr[1], arr[10] and arr[100] just fine, but arr[1000] causes a segfault. In general, accessing locations outside array bounds causes undefined behavior.

Related

Why is output of the following program how r u?

The following code is the basic implementation of the if - else conditional statements -
#include <stdio.h>
#include <stdlib.h>
int x;
int main()
{
if(x)
printf("hi");
else
printf("how r u \n");
return 0;
}
6.9.2 External object definitions
Semantics
1 If the declaration of an identifier for an object has file scope and an initializer, the
declaration is an external definition for the identifier.
2 A declaration of an identifier for an object that has file scope without an initializer, and
without a storage-class specifier or with the storage-class specifier static, constitutes a
tentative definition. If a translation unit contains one or more tentative definitions for an
identifier, and the translation unit contains no external definition for that identifier, then
the behavior is exactly as if the translation unit contains a file scope declaration of that
identifier, with the composite type as of the end of the translation unit, with an initializer
equal to 0.
So, Global variables are never left uninitialized, they are always initialized to 0. Hence the output how r u.
According to the C standard, your code does not trigger undefined behaviour:
If an object that has static or thread storage duration is not initialized explicitly, then:
- if it has pointer type, it is initialized to a null pointer;
- if it has arithmetic type, it is initialized to (positive or unsigned) zero;
This is taken from the C11 standard, but C89, and C99 both defined this behaviour, too:
If an object that has static storage duration is not initialized explicitly, it is initialized implicitly as if every member that has arithmetic type were assigned 0 and every member that has pointer type were assigned a null pointer constant.
Because you're declaring x as a global variable, it has static storage duration, and therefore x is guaranteed to be initialized to 0 (it being an int, it obviously has an arithmetic type).
Your main function, therefore reads like this:
int main(void)//use int main(void), not int main()
{
if(0)//x is 0, 0 is false
printf("hi");
else
printf("how r u \n");//because if (x) is false, this is executed
return 0;
}
That's why the output of your program will be "How r u".
//global
int x;
You have declared 'x' as global variable ... Therefore it's default value is 0 .

Casting pointer to memory buffer to pointer to VLA

in C, I believe the following program is valid: casting a pointer to an allocated memory buffer to an array like this:
#include <stdio.h>
#include <stdlib.h>
#define ARRSIZE 4
int *getPointer(int num){
return malloc(sizeof(int) * num);
}
int main(){
int *pointer = getPointer(ARRSIZE);
int (*arrPointer)[ARRSIZE] = (int(*)[ARRSIZE])pointer;
printf("%d\n", sizeof(*arrPointer) / sizeof((*arrPointer)[0]));
return 0;
}
(this outputs 4).
However, is it safe, in C99, to do this using VLAs?
int arrSize = 4;
int *pointer = getPointer(arrSize);
int (*arrPointer)[arrSize] = (int(*)[arrSize])pointer;
printf("%d\n", sizeof(*arrPointer) / sizeof((*arrPointer)[0]));
return 0;
(also outputs 4).
Is this legit, according to the C99 standard?
It'd be quite strange if it is legit, since this would mean that VLAs effectively enable dynamic type creation, for example, types of the kind type(*)[variable].
Yes, this is legit, and yes, the variably-modified type system is extremely useful. You can use natural array syntax to access a contiguous 2-D array both of whose dimensions were not known until runtime.
It could be called syntactic sugar as there's nothing you can do with these types that you couldn't do without them, but it makes for clean code (in my opinion).
I would say it is valid. The Final version of the C99 standard (cited on Wikipedia) says in paragraph 7.5.2 - Array declarators alinea 5 :
If the size is an expression that is not an integer constant expression: ...
each time it is evaluated it shall have a value greater than zero. The size of each instance
of a variable length array type does not change during its lifetime.
It even explicitely says that it can be used in a sizeof provided the size never changes : Where a size
expression is part of the operand of a sizeof operator and changing the value of the
size expression would not affect the result of the operator, it is unspecified whether or not
the size expression is evaluated.
But the standard also says that this is only allowed in a block scope declarator or a function prototype : An ordinary identifier (as defined in 6.2.3) that has a variably modified type shall have
either block scope and no linkage or function prototype scope. If an identifier is declared
to be an object with static storage duration, it shall not have a variable length array type.
And an example later explains that it cannot be used for member fields, even in a block scope :
...
void fvla(int m, int C[m][m]); // valid: VLA with prototype scope
void fvla(int m, int C[m][m]) // valid: adjusted to auto pointer to VLA
{
typedef int VLA[m][m]; // valid: block scope typedef VLA
struct tag {
int (*y)[n]; // invalid: y not ordinary identifier
int z[n]; // invalid: z not ordinary identifier
};
...

What is the meaning of statement below that the declared type shall not be incomplete type

The C standard states:
If the declaration of an identifier for an object is a tentative
definition and has internal linkage, the declared type shall not be an
incomplete type
What does it mean that "the declared type shall not be an incomplete type"?
That means you are not allowed to have:
static int arr[]; // This is illegal as per the quoted standard.
int main(void) {}
The array arris tentatively defined and has an incomplete type (lacks information about the size of the object) and also has internal linkage (static says arr has internal linkage).
Whereas the following (at file scope),
int i; // i is tentatively defined. Valid.
int arr[]; // tentative definition & incomplete type. A further definition
// of arr can appear elsewhere. If not, it's treated like
// int arr[] = {0}; i.e. an array with 1 element.
// Valid.
are valid.

Global integer array with No dimension

What is the concept when we define a global array with no dimension
This shows output as 16.
#include <stdio.h>
#include <stdlib.h>
int arr[];
int main(int argc, char *argv[])
{
arr[1] = 16;
printf("%d\n",arr[1]);
system("PAUSE");
return 0;
}
And even sizeof(arr) doesn't work. Why?
int arr[]; is a tentative definition there.
Clause 6.9.2, paragraph 2 says:
A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.
and example 2 in paragraph 5 of that clause clarifies:
If at the end of the translation unit containing
int i[];
the array i still has incomplete type, the implicit initializer causes it to have one element, which is set to zero on program startup.
So at the end of the translation unit, your array arr has type int[1]. Before the end, its type is incomplete, hence sizeof doesn't work, since in main, the array type is still incomplete.
Accessing arr[1] invokes undefined behaviour, since arr has only one element.
GCC assumes that arr should only have one element. The fact that you can access other elements than arr[0] without segfaulting is just a coincidence. For instance, on my machine I can access arr[1], arr[10] and arr[100] just fine, but arr[1000] causes a segfault. In general, accessing locations outside array bounds causes undefined behavior.

Is Incomplete array type guaranteed to be able to store one element?

Incomplete array types are used in the famous Struct hack and they are allowed since c99 standard. prior to c99 standard these were not allowed. I was looking at the standard and I am unable to conclude:
Are Incomplete array types allowed outside a structure?(All references I found in the standard C99: 6.7.2.1.15 talk about it as the last element in the structure).
So is the following program allowed to compile as per the standard?
int array[];
int main(){return 0;}
Second part of my questions is, If this is allowed is array guaranteed to be able to store atleast one element of they type int.
is the following program allowed to compile as per the standard?
Yes, as per:
(C99, 6.9.2p5) "EXAMPLE 2 If at the end of the translation unit containing
int i[];
the array i still has incomplete type, the implicit initializer
causes it to have one element, which is set to zero on program
startup."
So
int array[];
int main(){return 0;}
is valid and equivalent to:
int array[1];
int main(){return 0;}
Note that it is OK only if array has (like above) external linkage as:
(C99, 6.9.2p3) "If the declaration of an identifier for an object is a tentative definition and has internal linkage, the declared type shall not be an incomplete type."

Resources