A question on incomplete definitions

A

ark

Hello group,
Could you help me with this:

static const int x;
............ something .............
static const int x = 17;

It looks perfectly legal to me but MSVC/C++ 6.0 gives, on the first line,
"warning C4132: 'x' : const object should be initialized"
yet generates correct code.

What is correct - the code or the compiler? If the code, is it known what
compilers choke on this and how hard?

Thanks,
Ark
 
M

Malcolm

ark said:
static const int x;
This is a tentative forward declaration of x.
........... something .............
static const int x = 17;
This is the declaration of x

It looks perfectly legal to me but MSVC/C++ 6.0 gives, on the first
line, "warning C4132: 'x' : const object should be initialized"
yet generates correct code.

What is correct - the code or the compiler? If the code, is it known
what compilers choke on this and how hard?
The compiler is allowed to give warnings for code which is correct but looks
suspicious. Here you are using a little-used construct with a const, so it
wonders if you know what you are doing.

I would also give a warning, since I suspect that you don't need a tentative
forward declaration at all of a static const integer in your code. However I
may be wrong.
 
A

Alex

ark said:
Hello group,
Could you help me with this:
static const int x;

You declare and define x, with the implicit value of 0.
However, since the object is const, you won't have a chance
to change its value. AFAIK, this construct is valid, but
is largely meaningless.
........... something .............
static const int x = 17;

You redeclare and redefine x with the value of 17. This
is obviously incorrect because the x identifier already
exists and has storage.
It looks perfectly legal to me but MSVC/C++ 6.0 gives, on the first line,
"warning C4132: 'x' : const object should be initialized"
yet generates correct code.

This is a friendly warning that says that your first line
doesn't make much sense. It is probably not a required
diagnostic.

Alex
 
A

ark

I would also give a warning, since I suspect that you don't need a tentative
forward declaration at all of a static const integer in your code. However I
may be wrong.

Imagine, e.g., a header file that #define's useful thing about x (which is
really likely to be a struct of some sort). Then the header needs a
"tentative forward declaration" of x, and here it is useful IMHO.
Sorry for wrong terminology,
- Ark
 
A

ark

Alex said:
You declare and define x, with the implicit value of 0.
However, since the object is const, you won't have a chance
to change its value. AFAIK, this construct is valid, but
is largely meaningless.


You redeclare and redefine x with the value of 17. This
is obviously incorrect because the x identifier already
exists and has storage.


This is a friendly warning that says that your first line
doesn't make much sense. It is probably not a required
diagnostic.

Alex

I am not sure your analysis is correct; see Malcolm's posting above.
- Ark
 
B

Ben Pfaff

Alan Balmer said:
How does the compiler distinguish this from a normal declaration with
default initialization?

See C99 6.9.2#2:

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.
 
A

ark

See C99 6.9.2#2:

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.

--
"The way I see it, an intelligent person who disagrees with me is
probably the most important person I'll interact with on any given
day."
--Billy Chambless

So... I can do it only in C99, not in classic C (90?) or whatever was
before?
What about K&R?
Thanks for all help,
- Ark
 
B

Ben Pfaff

ark said:
So... I can do it only in C99, not in classic C (90?) or whatever was
before?

There is similar text in C90 if I recall correctly. I generally
quote from C99 because I have an accurate electronic
transcription.
What about K&R? Thanks for all help, - Ark

K&R didn't have the concept of a tentative definition as far as I
know.
 
J

Joe Wright

ark said:
I am not sure your analysis is correct; see Malcolm's posting above.
- Ark
Alex may be more correct that Malcolm. The whole point of the const
qualifier is that its object should not be modified without diagnostic.
You noticed that..

const int x;

yielding a diagnostic because you missed your only 'legal' chance to
define the value of x. It is not clear that the diagnostic is required
but x clearly useless in this case. Further down in your code..

const int x = 17;

redifines x and assigns a value to it. The redifinition is the error
here and requires a diagnostic. Assigning 17 to x here is probably ok.
 
A

Alex

Joe Wright said:
Alex may be more correct that Malcolm. The whole point of the const
qualifier is that its object should not be modified without diagnostic.
You noticed that..
const int x;
yielding a diagnostic because you missed your only 'legal' chance to
define the value of x. It is not clear that the diagnostic is required
but x clearly useless in this case. Further down in your code..
const int x = 17;
redifines x and assigns a value to it. The redifinition is the error
Indeed.

here and requires a diagnostic. Assigning 17 to x here is probably ok.

It can't be okay. See the first sentence in your second
paragraph :)

Alex
 
J

Jack Klein

You declare and define x, with the implicit value of 0.
However, since the object is const, you won't have a chance
to change its value. AFAIK, this construct is valid, but
is largely meaningless.


You redeclare and redefine x with the value of 17. This
is obviously incorrect because the x identifier already
exists and has storage.

I won't quote the standard's text on tentative definitions that means
this is perfectly correct, because Ben already posted it in full.
Make sure you read it. The OP's code is perfectly legal C.
This is a friendly warning that says that your first line
doesn't make much sense. It is probably not a required
diagnostic.

Alex

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++ ftp://snurse-l.org/pub/acllc-c++/faq
 
L

lawrence.jones

ark said:
So... I can do it only in C99, not in classic C (90?) or whatever was
before?

No, the rule has been the same since C89 (so the people who are telling
you that the code is wrong have no excuse for spreading such
misinformation).
What about K&R?

K&R did not provide any mechanism for forward-declaring statics. Many
compilers did, but the exact mechanism varied.

-Larry Jones

What's the matter? Don't you trust your own kid?! -- Calvin
 
J

James Hu

So... I can do it only in C99, not in classic C (90?) or whatever was
before?
What about K&R?

Please make sure you are compiling the code with a C compiler and not
a C++ compiler. C++ has a "one definition" rule that C does not.

-- James
 
K

Kevin Goodsell

ark said:
So... I can do it only in C99, not in classic C (90?) or whatever was
before?

It might be confusing to call C89/90 "Classic C". That term has been
used to refer to a C dialect that came just after K&R C. It's basically
the C that is usually called K&R, but it differs from the language
described in K&R1 in a few ways - it adds structure assignment,
enumerations, and 'void'.

-Kevin
 
K

Kevin Bracey

In message <[email protected]>
Alex said:

No. There is no error. "const int x;" is a tentative definition.

The later "const int x = 17;" is a real definition (because it has an
initialiser) and overrides the tentative definition.

Personally, I think it's a stupid C feature, and my compiler has an option to
disable tentative definitions (because it improves code generation for the
ARM). But the OP's code is totally conforming standard C.

I would advise that he adds an "extern" to the first line to change it into a
declaration, unless he really needs a tentative definition. That'll probably
stop the compiler warning.
 
A

Alan Balmer

See C99 6.9.2#2:
I suppose the reason I never remember this sort of two-step process is
because I'm never had a situation where it was useful. I don't find
the OP's header file example convincing.
 
A

ark

ark said:
Hello group,
Could you help me with this:

static const int x;
........... something .............
static const int x = 17;

It looks perfectly legal to me but MSVC/C++ 6.0 gives, on the first line,
"warning C4132: 'x' : const object should be initialized"
yet generates correct code.

What is correct - the code or the compiler? If the code, is it known what
compilers choke on this and how hard?

Thanks,
Ark
Thanks for the discussion.
A couple of people find this feature useless; IMHO it indicates their bias
toward runtime initialization in complex cases (in which case they can use
C++ as well :).
Those interested in non-trivial compile-time initialization (which reduces
footprint of data and code and, in embedded world, allows to put initialized
stuff in ROM) will probably find this language feature useful.
- Ark
 
A

Alex

Jack Klein said:
comp.lang.c:
I won't quote the standard's text on tentative definitions that means
this is perfectly correct, because Ben already posted it in full.
Make sure you read it. The OP's code is perfectly legal C.

Ugh. My mistake. I have never run into a situation where this
feature would have been useful.

Alex
 
C

Chris Torek

[on forward declarations of variables via tentative definitions]

I suppose the reason I never remember this sort of two-step process is
because I'm never had a situation where it was useful. I don't find
the OP's header file example convincing.

Forward declarations of variables are required for the same
reason forward declarations of functions are required: sometimes
it is impossible to topological-sort-away the dependencies.

Suppose we have function f() that calls function g(), and function
g() that calls function f(). This order:

/* optional: static */
F_TYPE f(args) {
...
g(...);
...
}

/* optional: static */
G_TYPE g(args) {
...
f();
...
}

calls f() from g() with a prototype in place, but calls g() from
f() without one. If we swap the order, putting g()'s definition
first, then f() calls g() with a prototype, but g() calls f()
without one. The solution is to declare at least one of f() and
g() first, then define the two in the remaining topologically-required
order (if we declare both, no order is required).

Just as the function version of the problem shows up in mutually-recursive
functions, the data version shows up in mutually-referential data.
Suppose we have the following data structures:

/* optional: typedef struct S STYPE; */
typedef struct T TTYPE; */

struct S {
struct T *s_tref; /* or TTYPE *s_tref; */
...
};

struct T {
struct S *t_sref; /* etc */
...
};

but now we want to define a static-duration, and optionally
internal-linkage, instance of an S that points to a T, and vice
versa:

/* optional: static */
struct S s_instance = { &t_instance, ... };

/* optional: static */
struct T t_instance = { &s_instance, ... };

As with functions f() and g(), neither order works without a
forward declaration. If s_instance and t_instance are to have
external linkage, we can use the "extern" keyword to "forward
declare" at least one of them:

extern struct T t_instance; /* forward declaration */
struct S s_instance = { &t_instance, ... };
struct T t_instance = { &s_instance, ... };

But if you wish to keep s_instance and t_instance private, with
internal linkage (using the "static" keyword), "extern" is no
longer suitable; now you must use "static":

static struct T t_instance; /* forward decl & tentative def */
static struct S s_instance = { &t_instance, ... };
static struct T t_instance = { &s_instance, ... };

As with functions f() and g(), we can forward-declare both instances
if we like, but at least one -- and in this case at most one (more
complicated multi-way references may require up to N-1 forward
declarations of N objects) -- is required.
 

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