I would like to know what the legal way of defining a constant struct that has a pointer as one of it's elements.
Looking at this post (Initializer element is not constant in C) I can see the following:
6.6 Constant expressions
(7) More latitude is permitted for constant expressions in initializers. Such a constant expression shall be, or evaluate to, one of the following:
— an arithmetic constant expression,
— a null pointer constant,
— an address constant, or
— an address constant for an object type plus or minus an integer constant expression.
(8) An arithmetic constant expression shall have an arithmetic type and shall only have operands that are integer constants, floating constants, enumeration constants, character constants, and sizeof expressions. Cast operators in an arithmetic constant expression shall only convert arithmetic types to arithmetic types, except as part of an operand to a sizeof operator whose result is an integer constant.
My question is if the following is well defined according to the (intersection of the C89 and C99) standard.
The contents of test.h:
#ifndef TEST_H
#define TEST_H
typedef struct _poly {
unsigned long int degree;
signed long int *coeffs;
} poly;
extern const poly my_poly;
extern void print_poly(poly p);
#endif
Contents of test.c:
#include <stdio.h>
#include "test.h"
static signed long int coeffs[3] = {1L, 2L, 3L};
const poly my_poly = {2UL, coeffs};
void print_poly(poly p)
{
/* Irrelevant mumbo-jumbo goes here. */
}
Contents of main.c:
#include "test.h"
int main(void)
{
print_poly(my_poly);
return 0;
}
Compiling on Debian 11 with gcc (and -Wall -Wextra -Wpedantic -std=c89 enabled), clang (with -Weverything -std=c89 enabled), tcc (with -Wall -std=c89 enabled), and pcc (with -std=c89 enabled) produces no warnings and no errors and runs as expected. Is the code:
static signed long int coeffs[3] = {1L, 2L, 3L};
const poly my_poly = {2UL, coeffs};
the correct way to initialize a constant struct that has a pointer as one of its member so that it is a compiler-time constant? It seems to follow the rule that it be an address constant, but I'm not sure.
As the question notes, an initializer may be an address constant.
C 2018 6.6 9 says “An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator; it shall be created explicitly using the unary & operator or an integer constant cast to pointer type, or implicitly by the use of an expression of array or function type…”
In const poly my_poly = {2UL, coeffs};, coeffs is an array of static storage duration, and it is implicitly converted to a pointer to its first element (per C 2018 6.3.2.1 3). Therefore, the result of the conversion, effectively &coeffs[0], is an address constant and may be used as an initializer.
Related
this the snip of the warning i was getting when i tried to compile the program.I am just getting started with pointers and this following program is being flagged by compiler for some reason I am not able comprehend. the code is as follows:
#include <stdio.h>
int dec = 0;
int *d;
d = &dec;
int main() {
return 0;
}
there is no error when I am stuffing these declarations in to main's body. the version of gcc I am using is gcc version 12.2.0(downloaded using MSYS2) and code editor MS visual code.can anybody post an explanation for this?
as i have stated above i have randomly started typing a program to get familiar with pointers, i expected there to be no variation in the treatment of pointers regardless of where they are being declared and intialised.
You're attempting to perform an assignment outside of a function, which is not allowed. What you can do is initialize:
int *d = &dec;
You may use only declarations in file scopes.
In the provided program you are using an assignment statement
d = &dec;
in the file scope. So the compiler issues an error.
Instead you could write for example
#include <stdio.h>
int dec = 0;
int *d = &dec;
int main( void ) {
return 0;
}
As the variable dec has static storage duration then the expression &dec is an address constant and my be used as an initializer for the variable d that also has static storage duration.
From 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.
and (6.6 Constant expressions)
7 More latitude is permitted for constant expressions in initializers.
Such a constant expression shall be, or evaluate to, one of the
following:
— an arithmetic constant expression,
— a null pointer constant,
— an address constant, or
— an address constant for a complete object type plus or minus an
integer constant expression.
and
9 An address constant is a null pointer, a pointer to an lvalue
designating an object of static storage duration, or a pointer to a
function designator; it shall be created explicitly using the unary
& operator or an integer constant cast to pointer type, or
implicitly by the use of an expression of array or function type. The
array-subscript [] and member-access . and -> operators, the address &
and indirection * unary operators, and pointer casts may be used in
the creation of an address constant, but the value of an object shall
not be accessed by use of these operators.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I am trying to allocate memory for my structure pointer which I am declaring globally. But I am getting this error --> error: initializer element is not constant
typedef struct A {
uint32_t arr[30][4096];
uint32_t var1;
uint8_t var2;
bool var3;
}B;
B *x = (B*)malloc(sizeof(B));
Can anyone explain me where I am doing it wrong.
Also, instead than dynamic memory allocation, is there a direct way to allocate memory to structure pointers?
Thanks in advance.
You cannot use malloc in global scope like declaring a variable.
You must use constant to init a variable in global scope.
You can use it (it is a function call) inside a function
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct {
uint32_t arr[30][4096];
uint32_t var1;
uint8_t var2;
bool var3;
}A;
A *x;
int main()
{
x = malloc(sizeof(A));
if (x != NULL)
{
free(x);
}
return 0;
}
Form the c99 standard.
6.7.8 Initialization
All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string
literals.
The constants are defined as follows:
6.4.4 Constants
Syntax
constant:
integer-constant (e.g. 4, 42L)
floating-constant (e.g. 0.345, .7)
enumeration-constant (stuff in enums)
character-constant (e.g. 'c', '\0')
The standard defines constant expressions as follows:
6.6 Constant expressions
(7) More latitude is permitted for constant expressions in initializers. Such a constant expression shall be, or evaluate to, one
of the following:
— an arithmetic constant expression,
— a null pointer constant,
— an address constant, or
— an address constant for an object type plus or minus an integer constant expression.
(8) An arithmetic constant expression shall have an arithmetic type and shall
only have operands that are integer constants, floating constants,
enumeration constants, character constants, and sizeof expressions.
Cast operators in an arithmetic constant expression shall only convert
arithmetic types to arithmetic types, except as part of an operand to
a sizeof operator whose result is an integer constant.
C doesn't allow you to have generic statements or expressions in the global scope (outside of functions), not even for initializers. Only compile-time constants are allowed as initializers in the global scope.
If you want to initialize the pointer then you need to do it as an assignment inside a function, perhaps first thing in the main function:
// Definition of variable
B *x;
int main(void)
{
// Initialization of variable through assignment
x = malloc(sizeof *x);
...
}
(This question was inspired by investigating an earlier question)
I have a code sample that initializes two global static variables: one is a pointer to extern variable, another is an expressioon computed from that pointer:
#include <stdint.h>
#define UNCACHE_MASK 0xABCDEF12UL // Value of the mask to apply
extern int memory_area;
const void * virtual_address = &memory_area;
const uintptr_t int_address = ((uintptr_t)&memory_area) | UNCACHE_MASK;
When I compile I get the following:
$ gcc -c test.c
test.c:6:1: error: initializer element is not computable at load time
const uintptr_t int_address = ((uintptr_t)&memory_area) | UNCACHE_MASK;
^
I am not much of an expert in C, but it seems that if &memory area is good for initializing virtual_address, it should also be good for initializing int_address.
What am I missing?
(gcc version 4.8.2, Cygwin on Win 7)
The formal definition of constant expressions of C language allow converting integer values to pointer types (to form address constants), but not the other way around (to form arithmetic constant expressions). It explicitly states that "cast operators in an arithmetic constant expression shall only convert arithmetic types to arithmetic types". For this reason the (uintptr_t) &memory_area bit violates the requirements imposed on arithmetic constant expressions. The expression is formally not a constant expression and therefore cannot be used in initializer for an object with static storage duration.
So, in short, &memory_area is an address constant, but (uintptr_t) &memory_area is not an arithmetic constant expression.
But it is indeed strange to see that GCC does not allow it as an extension.
this is my code. I wrote this part outside my main
typedef struct { int x; } foo;
const int bar = 2;
foo myFoo = { (int) bar };
However this returns:
common.c:6: error: initializer element is not constant
common.c:7: error: (near initialization for ‘myFoo.x’)
If I copy and paste the code into the main it will work.
Can someone give me an explanation please?
you can do using enum like:
typedef struct { int x; } foo;
enum {bar = 2};
foo myFoo = { bar };
or
using #define bar 2 like
#define bar 2
typedef struct { int x; } foo;
foo myFoo = { bar };
But both are known at compilation time: why not?
typedef struct { int x; } foo;
foo myFoo = { 2 };
const int bar = 2
C 2011 6.6 6:
An integer constant expression shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, _Alignof expressions, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof or _Alignof operator.
C 2011 6.7.9 4:
All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals.
When you define the object inside the body of main, it has automatic storage duration (exists only as long as main is executing). When you define the object at global scope, outside of any function, it has static storage duration (exists for the life of the program).
Automatic objects can have non-constants in them because they are initialized when the program is running. So the program can execute computations or fetch values from other objects to calculate the value. The initial values of static objects should be available before the program is executed so that they can be built into the program image. While it is obvious to a human that the initial value of myFoo in your example can be figured out at compile time, this requires additional work in the compiler that was not judged worthwhile to require in the C standard.
This question already has answers here:
Error "initializer element is not constant" when trying to initialize variable with const
(8 answers)
Closed 2 years ago.
I'm coming from javascript/php/python and probably I'm missing something, here is the code:
const int a = 50;
const int c = 100;
const int d = 100;
int endX = c + a;
int endY = d;
int startX, startY, b;
I get
ex1.4.c:6: error: initializer element is not constant
ex1.4.c:7: error: initializer element is not constant
Does someone have an explanation?
Unfortunately, in C const variables are not really const.
Below are the extracts from the c99 standard.
6.7.8 Initialization
All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string
literals.
The constants are defined as follows:
6.4.4 Constants
Syntax
constant:
integer-constant (e.g. 4, 42L)
floating-constant (e.g. 0.345, .7)
enumeration-constant (stuff in enums)
character-constant (e.g. 'c', '\0')
The standard defines constant expressions as follows:
6.6 Constant expressions
(7) More latitude is permitted for constant expressions in initializers. Such a constant expression shall be, or evaluate to, one
of the following:
— an arithmetic constant expression,
— a null pointer constant,
— an address constant, or
— an address constant for an object type plus or minus an integer constant expression.
(8) An arithmetic constant expression shall have an arithmetic type and shall
only have operands that are integer constants, floating constants,
enumeration constants, character constants, and sizeof expressions.
Cast operators in an arithmetic constant expression shall only convert
arithmetic types to arithmetic types, except as part of an operand to
a sizeof operator whose result is an integer constant.
Thus, c and a are not constant expressions and cannot be used as initializers in your case.
const expressions must be a compile time constant in C unlike in C++ therefore c+a can't be used as a constant. The usual way to handle this problem in C is to use the preprocessor instead:
#define A 50
#define C 100
#define D 100
int endX = C + A;
int endY = D;
int startX, startY, b;
If you are declaring endX as a global variable the error makes sense.
The reason is that global variables are initialized in compiling time, and you are trying to initialize endX as an operation that must be done in execution time.
Yeah, you can't initialize something to a variable. The compiler does the initialization and at compile time it doesn't know the value of c+a;
The int x = 1; type initialization is fine, the compiler just puts a 1 at the address of x in the object code.
To initialize something to c+a, you want to do it at runtime, in the startup code in c or constructor in C++.
In C programming langages, objects with static storage duration must be initialized with constant expressions (or aggregate containing constant expressions). If endX has static storage duration, its initializer (c+a) is not a constant expression (i.e. the expression cannot be evaluated during translation phase).