is this legal (and ANSI89 conforming) C?

D

dr.oktopus

Hello,
suppose that I have to create a dummy node of a list data type
that references itself (like in Sedgewick book). Is this:

struct node {
struct node *next;
int c;
} dummy_node = { &dummy_node };

legal? I think no problem should be, but I want a confirm.
 
T

Tom St Denis

Hello,
suppose that I have to create a dummy node of a list data type
that references itself (like in Sedgewick book). Is this:

struct node {
    struct node *next;
    int c;

} dummy_node = { &dummy_node };

legal? I think no problem should be, but I want a confirm.

IIRC it's valid because once you declare the variable you can use it
right away.

Anyone who writes code like that should be shot though. Or at least
mildly beaten with a foam clue bat.

Tom
 
B

BGB

IIRC it's valid because once you declare the variable you can use it
right away.

Anyone who writes code like that should be shot though. Or at least
mildly beaten with a foam clue bat.

IIRC Quake contained some code which looked like that in a few places...


well, along with a lot of:
read a file into a glob of memory;
do some pointer arithmetic (adding file offsets, ...);
cast the pointers to structs.

IMO though, this isn't a very good way to load binary files. my personal
preference is more to have code which reads in/decodes the contents of
the files (reading out individual values, ...), and puts them into newly
allocated memory.


or such...
 
A

Angel

IIRC Quake contained some code which looked like that in a few places...


well, along with a lot of:
read a file into a glob of memory;
do some pointer arithmetic (adding file offsets, ...);
cast the pointers to structs.

That's similar to the code I posted here a couple of weeks ago. It's not
portable due to padding (I used a gcc extension to turn padding off
entirely), endianness and unaligned access.
IMO though, this isn't a very good way to load binary files. my personal
preference is more to have code which reads in/decodes the contents of
the files (reading out individual values, ...), and puts them into newly
allocated memory.

That's how I rewrote it, and I agree, it makes for much cleaner code. It
is more work though. :)
 
T

Tom St Denis

IIRC Quake contained some code which looked like that in a few places...

well, along with a lot of:
read a file into a glob of memory;
do some pointer arithmetic (adding file offsets, ...);
cast the pointers to structs.

IMO though, this isn't a very good way to load binary files. my personal
preference is more to have code which reads in/decodes the contents of
the files (reading out individual values, ...), and puts them into newly
allocated memory.

or such...

ID Software was not known for writing what could be called "portable"
software. At the very least in CLC the idea is code snippets should
be portable.

I think the above code is correct [portable] just not very useful and
as a design decision I'd avoid that sort of declaration/initialization
code. I'm also not a big fan of code like

int a=4,b,c,d=5;
// etc...

I think legibility is increased if declarations are in one block, and
initializers in another...

int a,b,c,d;

a = 4;
d = 5;

// code...

As a general rule aside from going crazy on the prettyness every
minute spent writing proper code is 5 minutes saved on debugging.
Being able to quickly scan code for "is this variable initialized and
if so to what?" is very handy.
 
B

BGB

ID Software was not known for writing what could be called "portable"
software. At the very least in CLC the idea is code snippets should
be portable.

fair enough...

with some modest modifications, I had it working on Windows x64 before.
I later briefly tried building this version on Linux x86-64, but it
tended to crash when doing "new game" (and then managed to effectively
break X11 requiring a hard-reboot), and at the time I didn't really
bother enough with trying to use GDB to make it fully work (one can get
spoiled with things like the Visual Studio debugger and similar...).


and, people have also got it working on a number of exotic
architectures, so its portability probably isn't that terrible.


I have my own 3D engine (vaguely Quake-like, but written clean so as to
hopefully be free of the GPL, and internally working somewhat
differently), which does build and work on Linux x86-64 at present, but
does have a few theoretical technical issues (the theory goes that
calling variable-argument C functions from my custom script-language
"should" be prone to misbehave/blow-up due to how varargs and the AMD64
ABI are handled at present, and I would have to partly rework the
existing function-call mechanism to fix this...). currently, it calls
the functions with garbage in the variable arguments.

I may later integrate "printf" directly into my script-VM (as a builtin
function), since I call it a lot from scripts, and this is the main
function likely to encounter this issue, but Linux is not a highly
important target at the moment, so I may address this when I get to it.

also, lots of work and debugging is needed in general.


whether or not though I can create a complete game though is another
issue, as there is a lot of work needed in areas I am not very good
enough (mostly "content creation" type issues), before one has something
which anywhere compares with anything people would probably willingly
pay money for (and not feel ripped off...).


I think the above code is correct [portable] just not very useful and
as a design decision I'd avoid that sort of declaration/initialization
code.

yes, ok.

I'm also not a big fan of code like

int a=4,b,c,d=5;
// etc...

I think legibility is increased if declarations are in one block, and
initializers in another...

int a,b,c,d;

a = 4;
d = 5;

// code...

agreed, I tend to do similar...

As a general rule aside from going crazy on the prettyness every
minute spent writing proper code is 5 minutes saved on debugging.
Being able to quickly scan code for "is this variable initialized and
if so to what?" is very handy.

I try to make tradeoffs...

prettyness, good style, ... matter some, but ultimately, getting things
written and working is a better strategy.

for example, "perfect" code is of little value if it is never written or
put to use. often, code can be just plain nasty, and the product still
gets written and working (the horrible nastiness just left for whoever
dares look at the source).

but, ideally, a person can still try to make the code look nice where
possible.
 
S

Shao Miller

Hello,
suppose that I have to create a dummy node of a list data type
that references itself (like in Sedgewick book). Is this:

struct node {
struct node *next;
int c;
} dummy_node = {&dummy_node };

legal? I think no problem should be, but I want a confirm.

I don't think it's good. 'dummy_node' does not have 'static' storage
duration. Sorry! :)
 
S

Shao Miller

I don't think it's good. 'dummy_node' does not have 'static' storage
duration. Sorry! :)

As a work-around, perhaps you'd enjoy:

struct node {
struct node * next;
int c;
};

struct node * init_node(struct node * node) {
/* Initialize the node */
node->next = node;
return node;
}

int main(void) {
struct node dummy_node, * dp = init_node(&dummy_node);
/*
* Simply don't use 'dp' and hopefully it's
* optimized away...
*/
return 0;
}
 
E

Eric Sosman

IIRC it's valid because once you declare the variable you can use it
right away.

Yes. The scope of the identifier `node' begins immediately
after its first appearance, so it's in scope when mentioned on
the next line. The scope of the identifier `dummy_node' begins
immediately after its declarator (that is, just before the `='),
so it's in scope when used in the initializer.
Anyone who writes code like that should be shot though. Or at least
mildly beaten with a foam clue bat.

It seems to me a perfectly ordinary and laudable way to intialize
a circular linked list, possibly considered "empty" if `dummy_node'
is regarded as a "list header." (Some compilers will probably warn
about non-initialization of the `c' element, but I think that's not
the thrust of the question.) What alternative construct would you
suggest?
 
S

Shao Miller

Yes. The scope of the identifier `node' begins immediately
after its first appearance, so it's in scope when mentioned on
the next line. The scope of the identifier `dummy_node' begins
immediately after its declarator (that is, just before the `='),
so it's in scope when used in the initializer.

I could be mistaken, but for C89:

- I believe that an initializer for a 'struct', 'union', or array must
be a constant expression.

- I believe that '&dummy_node' is not an address constant because
'dummy_node' is not an object with 'static' storage duration.

"Work-around" code given elsethread.
 
T

Tim Rentsch

dr.oktopus said:
Hello,
suppose that I have to create a dummy node of a list data type
that references itself (like in Sedgewick book). Is this:

struct node {
struct node *next;
int c;
} dummy_node = { &dummy_node };

legal? I think no problem should be, but I want a confirm.

Yes if the definition is given in file scope, or
in block scope in C99; no for block scope in C89.
 
J

James Kuyper

Hello,
suppose that I have to create a dummy node of a list data type
that references itself (like in Sedgewick book). Is this:

struct node {
struct node *next;
int c;
} dummy_node = { &dummy_node };

legal? I think no problem should be, but I want a confirm.

The scope of 'dummy_node' starts at the end of it's declarator
(6.2.1p7). The declarator is the part before the '=' (6.7p1), . It is
therefore perfectly acceptable to use dummy_node within its own
initializer. It's uninitialized, so you can't use it's value; but you
can use it's address, as in your example.

Note: the C standard doesn't use the term "legal" for any purpose
whatsoever. It doesn't prohibit anything; it merely tells you the
consequences if you try to use a conforming implementation of C to
process code that contains certain features. One possible consequence is
that the implementation may be required to issue a diagnostic message.
Another possibility is that it may be permitted to reject your program.
A third possibility is that the C standard may impose no requirements
whatsoever on how your program behaves. None of these possibilities
apply to your code.

The standard does use the word "conforming", but the definition it
contains for "conforming code" is the result of political compromise
that renders it uselessly broad. The only thing which do NOT qualify as
conforming C code are text containing correctly formatted #error
directives that survive conditional inclusion (#if, #ifdef, etc.). The
standard also defines a term "strictly conforming", which is so strictly
defined that almost no useful programs qualify. The code you gave above
could be part of a strictly conforming program.
 
B

Ben Bacarisse

Shao Miller said:
I could be mistaken, but for C89:

- I believe that an initializer for a 'struct', 'union', or array must
be a constant expression.

- I believe that '&dummy_node' is not an address constant because
dummy_node' is not an object with 'static' storage duration.

You don't know it doesn't! The code is, presumably, intended to be at
file scope (if I can mix usages for a moment) so 'dummy_node' will be an
object with static storage duration.
 
S

Shao Miller

You don't know it doesn't! The code is, presumably, intended to be at
file scope (if I can mix usages for a moment) so 'dummy_node' will be an
object with static storage duration.

"Create" and "dummy_node" give me the impression that it is within a
function. As always, I could be mistaken. Perhaps dr.oktopus could
clarify?
 
B

Ben Bacarisse

Shao Miller said:
Shao Miller<[email protected]> writes:
On 6/22/2011 2:35 PM, Tom St Denis wrote:
On Jun 22, 1:26 pm, "dr.oktopus"<[email protected]>
wrote:
Hello,
suppose that I have to create a dummy node of a list data type
that references itself (like in Sedgewick book). Is this:

struct node {
struct node *next;
int c;

} dummy_node = {&dummy_node };

legal? I think no problem should be, but I want a confirm.
I could be mistaken, but for C89:

- I believe that an initializer for a 'struct', 'union', or array must
be a constant expression.

- I believe that '&dummy_node' is not an address constant because
dummy_node' is not an object with 'static' storage duration.

You don't know it [isn't]! The code is, presumably, intended to be at
file scope (if I can mix usages for a moment) so 'dummy_node' will be an
object with static storage duration.

"Create" and "dummy_node" give me the impression that it is within a
function. As always, I could be mistaken. Perhaps dr.oktopus could
clarify?

If, as you were listing your beliefs that led you to your conclusions,
you'd have included this one (that the code is in a function) you could
have avoided unnecessary replies! You were so precise about everything
else, I did not think you were making so significant an assumption.
 
D

dr.oktopus

"Create" and "dummy_node" give me the impression that it is within a
function.  As always, I could be mistaken.  Perhaps dr.oktopus could
clarify?- Nascondi testo citato

No, it is a global variable. I did not include a static specifier
because it could even be used as a structure referenced from various
files.
 
S

Shao Miller

No, it is a global variable. I did not include a static specifier
because it could even be used as a structure referenced from various
files.

My mistake, then!

However, are you referring to using it in a header file without
'static'? If so, why? If multiple translation units include that
header file and are linked together, couldn't you run into trouble? Or
perhaps you '#include' a "defining" header for some translation units
and an "externing" header for others that will be linked with the former
units?
 
D

dr.oktopus

My mistake, then!

However, are you referring to using it in a header file without
'static'?  If so, why?  If multiple translation units include that
header file and are linked together, couldn't you run into trouble?  Or
perhaps you '#include' a "defining" header for some translation units
and an "externing" header for others that will be linked with the former
units?

What I mean is that the use of a static keyword is not necessary to
get
a static storage.
Usually the code I see doesn't put a static specifier in header files,
simply define it, and put a include guard at the start of the header.
 
J

James Kuyper

What I mean is that the use of a static keyword is not necessary to
get
a static storage.

True, a file scope object automatically has static storage. As a result,
when a file scope identifier is defined using the 'static' keyword, it
has an entirely different meaning. It gives the identifier internal linkage.
Usually the code I see doesn't put a static specifier in header files,
simply define it, and put a include guard at the start of the header.

That is a problem waiting to happen. The main purpose of header files is
to make sure that exactly the same code is present in multiple different
translation units. If you #include that header in multiple different
translation units, it will have an external definition in each of those
translation units. There is supposed to be only one external definition
of any particular object or function in an entire program. Therefore, if
you combine two such translation units together into a single program,
the behavior of that program will be undefined.

One thing that happens on many systems, is that the multiple definitions
will be merged, and if they're all identical, the program will appear to
work as expected. If you've done this before without running into
problems, that's probably why.

Include guards prevent the occurrence of multiple
declarations/definitions of the same identifier within a single
translation unit; they don't do anything to prevent occurrence of
multiple external declarations that occur in different translation units.
 

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

No members online now.

Forum statistics

Threads
473,888
Messages
2,569,964
Members
46,294
Latest member
HollieYork

Latest Threads

Top