Static array initialization in C - c

I am reading the book Let us C by Yashavant Kanetkar.
In the Array of Pointers section there is a section of code which is giving me problems:
int main()
{
static int a[]={0,1,2,3,4}; //-----------(MY PROBLEM)
int *p[]={a,a+1,a+2,a+3,a+4};
printf("%u %u %d\n",p,*p,*(*p));
return 0;
}
What I don't understand is why has the array a have to be initialized as static. I tried initializing it without the static keyword but I got an error saying "illegal". Please help.

C90 (6.5.7) had
All the expressions in an initializer for an object that has static storage duration or in an initializer list for an object that has aggregate or union type shall be constant expressions.
And you are initializing an object that has an aggregate type, so the value must be known at compile time and the address of automatic variables are not in that case.
Note this has changed in C99 (6.7.8/4)
All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals.
The constraint on object with aggregate or union type has been removed and I've not found it placed somewhere else. Your code with static removed should be accepted by a C99 compiler (it is by gcc -std=c99 for instance, which seems to confirm that I've not overlooked a constraint elsewhere).

My guess would be that the contents of an array initialiser have to be a compile-time constant. By using static on a local variable in a function you essentially make that variable global, except with a local scope.

Related

Can I use an integer variable to define an arrays length?

Can I use a variable to define an array's size?
int test = 12;
int testarr[test];
Would this work, I don't want to change the size of the array after initialization. The int test's value isn't known at compile time.
From C99 it is allowed but only for the automatic variables.
this is illegal:
int test = 12;
int testarr[test]; // illegal - static storage variable
int foo(void)
{
int test = 12;
static int testarr[test]; // illegal - static storage variable
}
the only valid form is:
int foo(void)
{
int test = 12;
int testarr[test]; // legal - automatic storage variable
}
can i use an variable to define an arrays size?
This is called Variable Length Arrays (VLA).
Read Modern C then the C11 standard n1570 (and see this reference). VLAs are permitted in §6.7.6; also read the documentation of your C compiler (e.g. GCC).
But you don't want to overflow your call stack, typically limited to a megabyte on laptops (and OS specific).
So you may prefer C dynamic memory allocation with e.g. malloc(3) (and free ...) or calloc.
Beware of memory leaks.
You might be interested by tools such as valgrind. You need to learn to use your debugger (e.g. GDB).
Can I use an integer variable to define an arrays length?
Yes, that is what is called a variable length array and is part of the C standard since C99. Note that an implementation does not need to support it. Therefore you may prefer dynamic memory allocation. Take a look at here:
malloced array VS. variable-length-array
To cite the C standard:
"If the size is not present, the array type is an incomplete type. If the size is * instead of being an expression, the array type is a variable length array type of unspecified size, which can only be used in declarations or type names with function prototype scope;146) such arrays are nonetheless complete types. If the size is an integer constant expression and the element type has a known constant size,the array type is not a variable length array type; otherwise, the array type is a variable length array type. (Variable length arrays are a conditional feature that implementations need not support; see 6.10.8.3.)"
"146) Thus, * can be used only in function declarations that are not definitions (see 6.7.6.3)."
Source: C18, 6.7.6.2/4
Also note:
"Array objects declared with the _Thread_local, static, or extern storage-class specifier cannot have a variable length array (VLA) type."
Source: C18, 6.7.6.2/10
VLAs cannot be used:
at file scope.
when qualified with _Thread_local, static, or extern storage-class specifier.
if they have linkage.
Which means that they can only be used at function-scope and when of storage-class automatic, which is done by default when omitting any explicit specifier.
Feel free to ask for further clarification if you don't understand something.
Without knowing much of the context, wouldn't it be easier to just do this?
#define TEST 12 //to ensure this value will not change at all
int testarr[TEST];
Technically your method should work too but the value of test may change later on depending on the piece of code you written
So, when you try this on C, as in this [https://onlinegdb.com/rJzE8yzC8][1]
It successfully compiles and you can also update the value of the variable int test
However, after the update of test, the size of array does not change since arrays are static-defined.
For guarantee, I suggest you to use either const int or macro variables for doing this.

What is the use of Static in declaring character arrays?

I was doing this program from Hackerrank in C and I solved the program without declaring my character array 'result' as static. It returned an error where my Output didn't match with the Expected Output. But as soon as I inserted the keyword 'static' in the declaration of the array 'result', the code ran successfully.
Here is my code snippet:
char* timeConversion(char* s)
{
int i;
static char result[100]; // this doesn't work if i remove the word 'static'
if (s[0]=='1' && s[1]=='2' && s[8]=='A')
{
result[0]='0';
result[1]='0';
for (i=2;i<8;i++)
result[i]=s[i];
}
else if (s[0]=='1' && s[1]=='2' && s[8]=='P')
for (i=0;i<8;i++)
result[i]=s[i];
else
{
if (s[8]=='A')
for (i=0;i<8;i++)
result[i]=s[i];
if (s[8]=='P')
{
result[0]=s[0]+1;
result[1]=s[1]+2;
for (i=2;i<8;i++)
result[i]=s[i];
}
}
return result;
}
I don't understand that, what is the use of Static here?
Two things:
You are returning the address of a stack-allocated array from your function. If it were not static, you'd just be returning the address of an array that doesn't exist anymore since ordinarily, objects on the stack disappear when they go out of scope (i.e. when the function returns). To return an array from a function that can actually be used by the calling code, it must either be allocated on the heap (i.e. with malloc) or be declared static, since local variables in functions that are declared with the static qualifier persist after the function returns.
If a variable without the static qualifier is declared but not initialized explicitly, it just gets whatever's already in memory at the time.
If a local variable or array is declared with the static qualifier, it is implicitly initialized with zeroes. This means that the variable result can be populated with the parsed date/time value and is guaranteed to be a null-terminated string since all the elements of the array that aren't populated by the code in the function are already zero.
C11 6.2.2 Linkages of identifiers(P3):
If the declaration of a file scope identifier for an object or a
function contains the storage class specifier static, the identifier
has internal linkage.30)
Footnote:
30) A function declaration can contain the storage-class specifier
static only if it is at file scope; see
6.7.1.
It means static variable is declared once and its life time is entire execution of the program. That's why return static array working fine in your code, which makes sense.
If you remove static keyword, then array become a local or auto and scope of local variable exists only within the block or function that it is declared in. in your code, you are trying to return local array, that's invoked undefined behaviour.
GCC Compiler warning :
In function 'timeConversion':
prog.c:30:12: warning: function returns address of local variable [-Wreturn-local-addr]
return result;
^~~~~~
I don't understand that, what is the use of Static here?
A couple of meanings of static relevant to your program, taken from C99 standard (N1256):
6.2.4 Storage durations of objects
An object has a storage duration that determines its lifetime. There are three storage
durations: static, automatic, and allocated. Allocated storage is described in 7.20.3.
The lifetime of an object is the portion of program execution during which storage is
guaranteed to be reserved for it. An object exists, has a constant address,25) and retains
its last-stored value throughout its lifetime.26) If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when
the object it points to reaches the end of its lifetime.
An object whose identifier is declared with external or internal linkage, or with the
storage-class specifier static has static storage duration. Its lifetime is the entire
execution of the program and its stored value is initialized only once, prior to program
startup.
6.7.8 Initialization
....
10. If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. If an object that has static 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;
— if it is an aggregate, every member is initialized (recursively) according to these rules;
— if it is a union, the first named member is initialized (recursively) according to these
rules.
So, in your example, static specifies the lifetime of the result array and also initializes the array as per the rules highlighted above.

keil error #28 expression must have a constant value

I'm using Keil uVision to compile this code for an embedded project.
void doSomething(void)
{
unsigned char a = 0x01;
unsigned char b = 0x02;
typedef struct
{
void *pVoid;
} test_t;
test_t t[] = {{&a}, {&b}};
}
On the last line I receive an error
error: #28: expression must have a constant value
I've read that this is an issue where the compiler does not know the size of the variable. I don't understand what that means.
The variables a and b are of a defined type, so their pointers will always be the same size? Regardless of their type, this is embedded so pointers are all the same size?
It works to make var a and b static, why? This being embedded, I don't want the memory allocated continuously so that is not a solution here.
UPDATE:
I'm using Keil uVision 4.72.10.0 with Armcc v5.03.0.76 - I was able to get Keil to compile this, using the "--c99" flag, as found here.
Your function is perfectly fine in modern C, or even in C99, but C90 had stricter rules for initializers, and your code does not comply with those.
Here is the relevant provision of C90:
All the expressions in an initializer for an object that has static
storage duration or in an initializer list for an object that has
aggregate or union type shall be constant expressions.
(C90 6.5.7/4; emphasis added)
Structure types are aggregate types, so that applies to your code (when interperted according to C90). Where a and b identify function-scope variables, the expressions &a and &b are not constant expressions, so your code does not conform (to C90).
C99 drops the bit about aggregate or union type, and C2011 adds a provision for objects with thread storage duration (new in that version of C) to yield:
All the expressions in an initializer for an object that has static or
thread storage duration shall be constant expressions or string
literals.
That does not apply to your code.
It seems, then, that your compiler is enforcing C90 rules. Perhaps there is an option to select a more recent standard, but if not, then your best alternative is probably to set the structure members' values with assignment statements instead of an initializer:
test_t t[2];
t[0].pVoid = &a;
t[1].pVoid = &b;

Static arrays with variable length

Is the following legal?
const int n=10;
static int array[n];
If, yes, then why and how?
Note that in C language const objects do not qualify as constants. They cannot be used to build constant expressions. In your code sample n is not a constant in the terminology of C language. Expression n is not an integral constant expression in C.
(See "static const" vs "#define" vs "enum" and Why doesn't this C program compile? What is wrong with this? for more detail.)
This immediately means that your declaration of array is an attempt to declare a variable-length array. Variable length arrays are only allowed as automatic (local) objects. Once you declare your array with static storage duration, the size must be an integral constant expression, i.e. a compile-time constant. Your n does not qualify as such. The declaration is not legal.
This is the reason why in C language we predominantly use #define and/or enum to introduce named constants, but not const objects.
const int n=10;
static int array[n];
This code will encounter an error :
storage size of ‘array’ isn’t constant static int array[n];
^
Static memory allocation refers to the process of reserving memory at compile-time before the associated program is executed, unlike dynamic memory allocation or automatic memory allocation where memory is allocated as required at run-time.
const in C donot make that variable available in compile-time.
Statement like this would not generate that error:
static int array[10];
So, the statement that you have written is illegal or it encounter error while compiling the program.
static vars must ve allocated in COMPILE time, and thus their size and initialization value if any MUST be known at compile time. One could argue that using compile time optimizations the n var would/could be replaced with the constant value 10, and thus it might be possible to successfully compile that specific case.

Definition of global variables using a non constant initializer

#include <stdio.h>
int i=10;
int j=i;
int main()
{
printf("%d",j);
}
I get an error stating that initialization element is not a constant? What is the reason behind this?
What is the reason behind this?
C(unlike C++) does not allow initialization of global values with non constant values.
C99 Standard: Section 6.7.8:
All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals.
You could try using:
int i=10;
int j=0;
int main()
{
j=i;//This should be the first statement in the main() and you achieve the same functionality as ur code
return 0;
}
The only true C way is to initialize it at runtime. Although in C++ your code will work fine, without any compilation errors.
The C standard clearly prohibits initialization of global objects with non-constant values. The Section 6.7.8 of the C99 standard says:
All the expressions in an initializer for an object that has static
storage duration shall be constant expressions or string literals.
The definition of an object with static storage duration is in section 6.2.4:
An object whose identifier is declared with external or internal
linkage, or with the storage-class specifier static has static storage
duration. Its lifetime is the entire execution of the program and its
stored value is initialized only once, prior to program startup.
The idea behind this requirement is to have all static storage duration object initialized at compile time. The compiler prepares all static data in pre-initialized form so that it requires no additional initializing code at run time. I.e. when the compiled program is loaded, all such variables begin their lives in already initialized state.
In the first standardized version of C language (C89/90) this requirement also applied to aggregate initializers, even when they were used with local variables.
void foo(void)
{
int a = 5;
struct S { int x, y; } s = { a, a }; /* ERROR: initializer not constant */
}
Apparently the reason for that restriction was that the aggregate initializers were supposed to be built in advance in the pre-initialized data segment, just like global variables.
Use this:-
int i=10,j=1;
int main()
{
printf("%d",j);
}
Though it is a minor change but it will work

Resources