initialization of local static variable

  • Thread starter V.Subramanian, India
  • Start date
V

V.Subramanian, India

consider the following program x.c:
#include <stdlib.h>
#include <stdio.h>

char first[] = "test message";

void display(void)
{
static char* p_first = first + 5;

printf("%s\n", p_first);

char second[] = "new message";
static char* p_second = second + 4;

printf("%s\n", p_second);

return;
}

int main()
{
display();

return EXIT_SUCCESS;
}

When this program is compiled with
gcc (GCC) 3.4.3 20050227 (Red Hat 3.4.3-22.1)
as
gcc -std=c99 -pedantic -Wall -Wextra x.c
it generates the following error:
x.c: In function `display':
x.c:13: error: initializer element is not constant

The compiler accepts the initialization of the local static variable
'p_first' in 'display()' function. However the compiler reports error
for the initializtion of the local static variable 'p_second' in
'display()' function.

In the case of 'p_first', the array name 'first' decays into a pointer
to the first element and so in this case ALSO the memory address
stored in p_first will be known only at run-time(similar to that of
'p_second'). So why doesn't the compiler flag this as an error ?

Please clarify.

Thanks
V.Subramanian
 
B

BartC

char first[] = "test message";

void display(void)
{
static char* p_first = first + 5;

printf("%s\n", p_first);

char second[] = "new message";
static char* p_second = second + 4;

printf("%s\n", p_second);

return;
}
The compiler accepts the initialization of the local static variable
'p_first' in 'display()' function. However the compiler reports error
for the initializtion of the local static variable 'p_second' in
'display()' function.

This is a funny one. My compiler (lccwin32) accepted this code with no
problem.

A couple of others including gcc gave the same error as yours.

There's no reason why that shouldn't work, other than perhaps something in
the language says it shouldn't. (Possibly, to do with the fact that the
declarations of 'second' and 'p_second' may not be in that order; p_second
could be created first.)

But you can get around it by separating the declaration of p_second, and
it's initialisation, into two statements.
 
B

BartC

China Blue Corn Chips said:
V.Subramanian said:
consider the following program x.c:
#include <stdlib.h>
#include <stdio.h>

char first[] = "test message";

The address of first is known when the program is loaded.

The address of first, yes; it's contents are initially set to point to "test
message".
p_first is initialised when the program is loaded with address already
known.

But 'first' refers to it's contents, not it's address. The contents might
have changed (point to another string) by the time display() gets called.
(I'm assuming p_first is re-initialised on each entry to the function.)
 
J

Jens Thoms Toerring

BartC said:
China Blue Corn Chips said:
V.Subramanian said:
consider the following program x.c:
#include <stdlib.h>
#include <stdio.h>

char first[] = "test message";

The address of first is known when the program is loaded.
The address of first, yes; it's contents are initially set to point to "test
message".

I guess that you overlooked that 'first' isn't a pointer but
an array (which would explain most of what you wrote in the
following). Thus memory for it is made available and initia-
lized (to "test message") when the program is loaded. Its
address is, of course, known at that point in time.
But 'first' refers to it's contents, not it's address.

'first + 5' evaluates to the address of (the first element of)
'first, incremented by 5 - there's nothing refering to the con-
tent of 'first'. Since 'first is an array (not a pointer) we've
got the common conversion of an array to a pointer to its first
element when the array is used where a value is required.
The contents might
have changed (point to another string) by the time display() gets called.

The content of the array 'first' could have changed, yes, but it
will still have the same address.
(I'm assuming p_first is re-initialised on each entry to the function.)

No, 'p_first', being a static variable, is initialized exactly
once when the program is loaded - at least that's how I would
tend to interpret 3.1.2.4 in C89:

An object declared with external or internal linkage, or with the
storage-class specifier static has static storage duration. For such
an object, storage is reserved and its stored value is initialized
only once, prior to program startup.

Regards, Jens
 
B

BartC

Jens Thoms Toerring said:
BartC said:
China Blue Corn Chips said:
char first[] = "test message";

The address of first is known when the program is loaded.
The address of first, yes; it's contents are initially set to point to
"test
message".

I guess that you overlooked that 'first' isn't a pointer but
an array (which would explain most of what you wrote in the
following). Thus memory for it is made available and initia-
lized (to "test message") when the program is loaded. Its
address is, of course, known at that point in time.

Oh, OK.
No, 'p_first', being a static variable, is initialized exactly
once when the program is loaded - at least that's how I would
tend to interpret 3.1.2.4 in C89:

I tried this:

int a=1901;

void testfn(void) {
static int b=a;
printf("A, B = %d, %d\n",a,b);
}

- int main(void) {
- testfn();
- a=2901;
- testfn();
- }

[hyphens added to avoid weird formatting issues in Windows Mail]

Which worked as expected, with lccwin32 compiler (b is assigned the value
of a on each entry to the function).

On other, presumably more compliant compilers, this initialisation of b
wasn't allowed. However the behaviour allowed by lccwin32 is more
intuitive and more useful. (There is nothing after all to stop you writing:

static int b; b=a;

which would have the same effect. So why not the convenience of having it
all in one statement?)
 
J

Jens Thoms Toerring

I tried this:
int a=1901;
void testfn(void) {
static int b=a;
printf("A, B = %d, %d\n",a,b);
}
- int main(void) {
- testfn();
- a=2901;
- testfn();
- }
[hyphens added to avoid weird formatting issues in Windows Mail]
Which worked as expected, with lccwin32 compiler (b is assigned the value
of a on each entry to the function).

That, for me, would be absolutely unexpected since then the
'static' qualifier on 'b' would have no effect at all, it
would behave exactly the same as if you would have written

int b = a;
On other, presumably more compliant compilers, this initialisation of b
wasn't allowed.

However the behaviour allowed by lccwin32 is more
intuitive and more useful. (There is nothing after all to stop you writing:
static int b; b=a;
which would have the same effect. So why not the convenience of having it
all in one statement?)

As I said, I would be rather surprised by this behaviou since I'm
used to rely on static variables to be initialized once and then
never again. Thus for me

static int b = a;

(which doesn't work as long a isn't a constant expression) and

static int b;
b = a;

are two rather different things. If 'a' is a constant expression
then 'b' should be set once to it the first time the function is
called and then retain any value it has at the end of the func-
tion.

Consider for example code like this you will find quite often:

void foo( ) {
static int is_uninitialized = 1;
if ( ! is_uninitialized ) {
initialize_something( );
is_uninitialized = 0;
}
...
}

Rather obviously, 'is_uninitialized' is meant to ensure that
initialize_something() is called only once, the first time the
function is called. Having it re-initialized to 1 every time
the function is called would break a lot of existing code...

But then the lccwin32 compiler allows something that isn't
defined by the standard (initialization with a non-constant
expression) and for that situation it could bend the rules
in whatever way it wants - it's on its own to define what
the further consequences are. It's not intuitive to me what
it seems to be doing but then that's of litte importantance;-)

Regards. Jens
 
B

Ben Bacarisse

BartC <[email protected]> wrote:
I tried this:
int a=1901;
void testfn(void) {
static int b=a;
printf("A, B = %d, %d\n",a,b);
}
- int main(void) {
- testfn();
- a=2901;
- testfn();
- }
[hyphens added to avoid weird formatting issues in Windows Mail]
Which worked as expected, with lccwin32 compiler (b is assigned the value
of a on each entry to the function).

That, for me, would be absolutely unexpected since then the
'static' qualifier on 'b' would have no effect at all, it
would behave exactly the same as if you would have written

int b = a;

Not exactly the same. In the case of static b, it's address could be
passed out of the function and would be valid even after the function's
return.

By the way I agree with the "unexpected" part. Despite this being an
extension, I'd expect only one initialisation. By the way, even a
function call is permitted as the initialiser.

<snip>
 
J

jacob navia

Le 07/10/11 21:29, Ben Bacarisse a écrit :
By the way I agree with the "unexpected" part. Despite this being an
extension, I'd expect only one initialisation. By the way, even a
function call is permitted as the initialiser.

Well, why shouldn't be possible?

I don't see any reason. The compiler can do it, the language
looks the same, why shouldn't I do it?

Besides the obvious (the standard doesn't like it) is there
any reason not to do it?

Now, it *could* be said that it would be unexpected because
static initializations are done at load time but obviously
the intention of the programmer here can't be that since
there is no way a function can be called at load time...

Are there any other reasons why it should not work?
 
B

Ben Bacarisse

jacob navia said:
Le 07/10/11 21:29, Ben Bacarisse a écrit :

Well, why shouldn't be possible?

I don't see any reason. The compiler can do it, the language
looks the same, why shouldn't I do it?

Besides the obvious (the standard doesn't like it) is there
any reason not to do it?

Now, it *could* be said that it would be unexpected because
static initializations are done at load time but obviously
the intention of the programmer here can't be that since
there is no way a function can be called at load time...

The programmer may simply expect the initialisation to be done "early"
(as it is in C++). Whenever it get done, I'd expect the function to be
called only once. That, for me, is the "unexpected" part.
Are there any other reasons why it should not work?

No, you define what it means, so it is bound to work. Do you think it
is useful?
 
J

jacob navia

Le 07/10/11 22:16, Ben Bacarisse a écrit :
jacob navia said:
Le 07/10/11 21:29, Ben Bacarisse a écrit :
[snip]


The programmer may simply expect the initialisation to be done "early"
(as it is in C++). Whenever it get done, I'd expect the function to be
called only once. That, for me, is the "unexpected" part.

Yes, I agree that this handling of static is not coherent with the
other handling of statics in other parts of the language.

I see that you have a point here.
No, you define what it means, so it is bound to work. Do you think it
is useful?

I think it makes the language more coherent. See the question of the
original poster. He was surprised that it doesn't work, and
intuitively, you expect it to work.
 
B

BartC

jacob navia said:
Le 07/10/11 21:29, Ben Bacarisse a écrit :

Well, why shouldn't be possible?

I don't see any reason. The compiler can do it, the language
looks the same, why shouldn't I do it?

Actually there seems to be an inconsistency with how lccwin32 handles this:

int a=1901;

void testfn(void) {
static int b=a;
printf("A, B = %d, %d\n",a,b);
}

Here, the initialisation of b seems to be done on each entry to the
function. But here:

int a=1901;

void testfn(void) {
static int b=1234;
printf("A, B = %d, %d\n",a,b);
b=9999;
}

the initialisation of b is only done once; it doesn't get reset to 1234 on
each call.

So there is ambiguity about the construct:

static int b = X;

which may be executed once, or N times, depending on the nature of X (or
more accurately, zero on N times).
 
J

Jens Thoms Toerring

jacob navia said:
Le 07/10/11 22:16, Ben Bacarisse a écrit :
The programmer may simply expect the initialisation to be done "early"
(as it is in C++). Whenever it get done, I'd expect the function to be
called only once. That, for me, is the "unexpected" part.
Yes, I agree that this handling of static is not coherent with the
other handling of statics in other parts of the language.
I see that you have a point here.
I think it makes the language more coherent. See the question of the
original poster. He was surprised that it doesn't work, and
intuitively, you expect it to work.

I think there are different things to consider here. First of all,
not restricting the initialization of a static variable to a con-
stant expression may have some benefits (but not being a compiler
writer and thus not having to ponder all possible consequences I
might overlook a number of hairy corner cases).

The other point is having a static variable initialized more than
once. That's something I find rather disturbing - I learned to
expect a static variable to be initialized once (may it be while
loading the program or when the function it's defined in is called
for the first time seems to me to be less crucial), but having it
change its value afterwards due to the initialization again and
again is something I wouldn't have considered to be a possibility.

As far as I can see there's a double "personality" to a static
variable. One is that it can be returned from a function (the
part of 'static' I hadn't considered but Ben pointed out a few
posts up-thread). The other is the part about "it's initialized
once and then keeps its value across function invocations". That
these two meanings overlap is probably due to the way static va-
riables are typically implemented (i.e. actually somewhere as
some global memory entity, initialized at the momemt they are
set up, just not made visible to the programmer when it's de=
fined within a function).

Your approach seems to concentrate on the "I can return it from
a function", dropping the "it's initialized only once" part of
it - with, as far as I have understood BartC's posts, still ma-
king a difference depending on how it was initialized. I.e. if
it was initialized with a constant expression it behaves as a
"standard" static variable (initialized only once), otherwise it
follows some other rules.

I don't want to argue if this is wrong - you write the compiler
and thus you define what happens - but, at least to me, it does
not make things easier to understand or seems to be more cohe-
rent since I now also have to consider how a static variable is
initialized in order to figure out how it will behave.

Regards, Jens
 
J

jacob navia

Le 08/10/11 00:25, Jens Thoms Toerring a écrit :
I don't want to argue if this is wrong - you write the compiler
and thus you define what happens - but, at least to me, it does
not make things easier to understand or seems to be more cohe-
rent since I now also have to consider how a static variable is
initialized in order to figure out how it will behave.

Obviously if it is initialized with a constant expression the simple
rule applies as before: it is initialized once by the linker before
the program is loaded

If the expression is not constant it will be initialized when the
program executes the initialization statement (it can't be otherwise
since it is NOT a constant expression)

I see this as very easy to understand but I could be wrong since
I program in C since a long time and can't go back to my beginner
years to verify that it looks coherent to a beginner)

Now, I have a problem with the "once" that Ben pointed out. Should
I generate code that initializes that ONCE?

For instance I could generate code that tests a memory location to see
if the variable is initialized or not and skip the initialization
if it is initialized already.

The whole thing comes down to the existence of an initialization
procedures (like the C++ constructors) that would allow us to
write:

double sqrt2_over_2 = sqrt(2)/2;
int main(void)
{
printf("%g\n",sqrt2_over_2);
}
 
I

Ian Collins

Le 08/10/11 00:25, Jens Thoms Toerring a écrit :

Obviously if it is initialized with a constant expression the simple
rule applies as before: it is initialized once by the linker before
the program is loaded

If the expression is not constant it will be initialized when the
program executes the initialization statement (it can't be otherwise
since it is NOT a constant expression)

I see this as very easy to understand but I could be wrong since
I program in C since a long time and can't go back to my beginner
years to verify that it looks coherent to a beginner)

Now, I have a problem with the "once" that Ben pointed out. Should
I generate code that initializes that ONCE?

For instance I could generate code that tests a memory location to see
if the variable is initialized or not and skip the initialization
if it is initialized already.

You probably should do something along those lines, that's how C++ does
it. In C++ a function scope static variable is initialised once the
first time the function is called. You can use a hidden regular file
scope static variable (which will be zero initialised) for the test.
The whole thing comes down to the existence of an initialization
procedures (like the C++ constructors) that would allow us to
write:

double sqrt2_over_2 = sqrt(2)/2;
int main(void)
{
printf("%g\n",sqrt2_over_2);
}

In C++, file scope static variables are initialised before main is
called, so you could create a hidden function called on entry to main to
get the same effect.

If you are adding C++ like extensions, checking and following the C++
rules would be a good idea. You would be gaining the benefit of the
work done for the C++ standard.
 
J

jacob navia

Le 08/10/11 01:25, China Blue Corn Chips a écrit :
What does it mean? Does it mean initalise the static exactly once before the
first dynamic reference?
no

Or does it mean initialise on every call?

Yes, as I have it now.
If the
latter, what does that do to auto-initialising function that require a flag be
initialised once (static bool firstcall = true)?

That is an initialization with a compile time constant and the old
rules apply.
Allowing only initialise at load to a constant simplifies the loader and
provides enough power to implement the other kinds of initialisation.

#define once(statement) do { \
static bool onceflag = true; \
if (onceflag) {onceflag = false; {statement;}} \
} while(0)

This remains valid
#define reinitialiseEachCall(variable,value) variable; variable=(value)

That also :)
static int perfect = 6; /*simple constant initialisation*/
static double pi; once(pi = 2*atan(1));
static int reinitialiseEachCall(b, a);

That will not work at the top level (global)
 
J

jacob navia

Le 08/10/11 00:56, Ian Collins a écrit :
In C++, file scope static variables are initialised before main is
called, so you could create a hidden function called on entry to main to
get the same effect.

If you are adding C++ like extensions, checking and following the C++
rules would be a good idea. You would be gaining the benefit of the work
done for the C++ standard.

Yes, you are right. I think that is a heavy argument in favor of doing
it just once.
 
J

Jens Thoms Toerring

jacob navia said:
Le 08/10/11 00:25, Jens Thoms Toerring a écrit :
Obviously if it is initialized with a constant expression the simple
rule applies as before: it is initialized once by the linker before
the program is loaded
If the expression is not constant it will be initialized when the
program executes the initialization statement (it can't be otherwise
since it is NOT a constant expression)

I could be completely wrong here since I don't have your compiler
installed and thus have to rely on BartC's descriptions. But if
he's correct and I understanding his descriptions correctly a static
variable with a constant expression initializer is initialized only
once, while one with a non-constant expression initializer is set
again each time the function is called. If this is true then I
would think things might become confusing: what actually is a "con-
stant expression" can be a bit difficult to see (I have to admit
that even after a number of years using C I'm still not always
100% certain and may like to rely on the compiler to point out
the errors of my way;-)

Your
double sqrt2_over_2 = sqrt(2)/2;

is a nice example. At the first glance, this is as constant a
value as they get, no doubts about that from a mathematical point
of view. So one would rather likely expect the compiler to "under-
stand" that I mean the quite constant value of 0.70710678... But
then the compiler might not know if I'm going to do something sneaky
like linking against a different libm where the result of sqrt(2)
is something different from 1.41421345... And thus it can't be 100%
sure it's dealing with a constant and will consider it (against
all common sense) to be an initialization with a non-constant
value.

So my point is that while the above looks like a very constant
expression and thus a user may reasonably expect it to be one,
it actually isn't one. And when you make the behaviour of a
static variable depend on what it was initialized with you might
end up with some confusion for the unsuspecting users. Consider
a function like this

double bar( void ) {
static double sqrt2_over_2 = sqrt( 2 ) / 2;
if ( sqrt2_over_2 == sqrt( 2 ) / 2 )
return sqrt2_over_2;
return sqrt2_over_2 = 42.0;
}

What I'm going to get on the first and the second invocation
(please disregard problems with floating point comparisons for
equality for the sake of the example)? Is it an initialization
with a constant value or isn't it? What is an unsuspecting
user going to expect?

Of course, all this is only relevant if the description given
by BartC is correct.
Now, I have a problem with the "once" that Ben pointed out. Should
I generate code that initializes that ONCE?
For instance I could generate code that tests a memory location to see
if the variable is initialized or not and skip the initialization
if it is initialized already.

I'm not sure what you mean with the "once" stuff - but I personally
wouldn't expect to have a static variable having set its value more
than once - ever. I.e. for

long int foo_s( ) {
static long int r = random( );
return r;
}

I would expect to get the same value returned each time while
with

long int foo_a( ) {
long int r = random( );
return r;
}

I'd expect a different value on each invocation.

Regards, Jens
 
B

BartC

Jens Thoms Toerring said:
I could be completely wrong here since I don't have your compiler
installed and thus have to rely on BartC's descriptions. But if
he's correct and I understanding his descriptions correctly a static
variable with a constant expression initializer is initialized only
once, while one with a non-constant expression initializer is set
again each time the function is called. If this is true then I
would think things might become confusing: what actually is a "con-
stant expression" can be a bit difficult to see

It really requires two different symbols:

'=' to mean dynamic assignment
'==' (perhaps) to mean one-time initialisation

The trouble is the current usage in C uses '=' for both meanings. That might
be OK if 'static' only ever means a one-time initialisation. But trying to
extend the way initialisation for statics is done causes some confusion when
just the one symbol is used for both ways.

And having a separate rule for 'constant' and 'non-constant' is not really
workable either for the reasons you've stated. And suppose you *did* want to
initialise multiple times with a constant?
 
R

Richard Damon

Jens Thoms Toerring said:
BartC said:
char first[] = "test message";

The address of first is known when the program is loaded.
The address of first, yes; it's contents are initially set to point to
"test
message".

I guess that you overlooked that 'first' isn't a pointer but
an array (which would explain most of what you wrote in the
following). Thus memory for it is made available and initia-
lized (to "test message") when the program is loaded. Its
address is, of course, known at that point in time.

Oh, OK.
No, 'p_first', being a static variable, is initialized exactly
once when the program is loaded - at least that's how I would
tend to interpret 3.1.2.4 in C89:

I tried this:

int a=1901;

void testfn(void) {
static int b=a;
printf("A, B = %d, %d\n",a,b);
}

- int main(void) {
- testfn();
- a=2901;
- testfn();
- }

[hyphens added to avoid weird formatting issues in Windows Mail]

Which worked as expected, with lccwin32 compiler (b is assigned the
value of a on each entry to the function).

On other, presumably more compliant compilers, this initialisation of b
wasn't allowed. However the behaviour allowed by lccwin32 is more
intuitive and more useful. (There is nothing after all to stop you writing:

static int b; b=a;

which would have the same effect. So why not the convenience of having
it all in one statement?)

I would consider multiple initializations of a static variable to be a
serious error. Note that "static int b=a;" is NOT an assignment
statement but defining an object with initialization. By the standard,
this initialization needs to happen before main starts (from 5.1.2: All
objects with static storage duration shall be initialized (set to their
initial values) before program startup. ) but if you extend the syntax
(and generate the required (warning) diagnostic, then the sensible
option is to initialize only on the first pass.

As you note, change the statement to an assignment statement would allow
you to get the behavior you want.

Note that I believe that gcc, after giving the required warning does
work in the manner I am suggesting.

Where this can be useful would be a function like this:


struct node* func() {

static struct node* list = init_list();

....
}

where the first time func is called, the list is built, and not rebuilt
for subsequent calls. To do this with your syntax becomes a lot more
ugly (needing to create a second static variable for a flag that this is
the first pass through or not, and code to test and set this flag).
Compared to the simple change in code to make the initialization an
assignment, if seems clear which works better.

Also, this conforms to the C++ definition of local static
initialization, The implementation you define is gratuitously
incompatible with how C++ extended the syntax.
 
R

Richard Damon

Jens Thoms Toerring said:
BartC said:
char first[] = "test message";

The address of first is known when the program is loaded.
The address of first, yes; it's contents are initially set to point to
"test
message".

I guess that you overlooked that 'first' isn't a pointer but
an array (which would explain most of what you wrote in the
following). Thus memory for it is made available and initia-
lized (to "test message") when the program is loaded. Its
address is, of course, known at that point in time.

Oh, OK.
No, 'p_first', being a static variable, is initialized exactly
once when the program is loaded - at least that's how I would
tend to interpret 3.1.2.4 in C89:

I tried this:

int a=1901;

void testfn(void) {
static int b=a;
printf("A, B = %d, %d\n",a,b);
}

- int main(void) {
- testfn();
- a=2901;
- testfn();
- }

[hyphens added to avoid weird formatting issues in Windows Mail]

Which worked as expected, with lccwin32 compiler (b is assigned the
value of a on each entry to the function).

On other, presumably more compliant compilers, this initialisation of b
wasn't allowed. However the behaviour allowed by lccwin32 is more
intuitive and more useful. (There is nothing after all to stop you writing:

static int b; b=a;

which would have the same effect. So why not the convenience of having
it all in one statement?)

I would consider multiple initializations of a static variable to be a
serious error. Note that "static int b=a;" is NOT an assignment
statement but defining an object with initialization. By the standard,
this initialization needs to happen before main starts (from 5.1.2: All
objects with static storage duration shall be initialized (set to their
initial values) before program startup. ) but if you extend the syntax
(and generate the required (warning) diagnostic, then the sensible
option is to initialize only on the first pass.

As you note, change the statement to an assignment statement would allow
you to get the behavior you want.

Note that I believe that gcc, after giving the required warning does
work in the manner I am suggesting.

Where this can be useful would be a function like this:


struct node* func() {

static struct node* list = init_list();

....
}

where the first time func is called, the list is built, and not rebuilt
for subsequent calls. To do this with your syntax becomes a lot more
ugly (needing to create a second static variable for a flag that this is
the first pass through or not, and code to test and set this flag).
Compared to the simple change in code to make the initialization an
assignment, if seems clear which works better.

Also, this conforms to the C++ definition of local static
initialization, The implementation you define is gratuitously
incompatible with how C++ extended the syntax.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,571
Members
45,045
Latest member
DRCM

Latest Threads

Top