Help wanted on some source codes

B

Becker

1.===========================================
/* a.c */
int x;
int y;

void main()
{
f();
printf("%x %x\n", x, y);
}

/* b.c */
double x;

void f()
{
x = -0.0;
}


2.===========================================
/* a.c */
int x = 1;
int y;

void main()
{
f();
printf("%x %x\n", x, y);
}

/* b.c */
double x;

void f()
{
x = -0.0;
}

3.===========================================
/* a.c */
int x;
int y = 1;

void main()
{
f();
printf("%x %x\n", x, y);
}

/* b.c */
double x;

void f()
{
x = -0.0;
}

4.===========================================
/* a.c */
int x = 1;
int y = 1;

void main()
{
f();
printf("%x %x\n", x, y);
}

/* b.c */
double x;

void f()
{
x = -0.0;
}

5.===========================================
/* a.c */
int x = 1;
int y = 1;

void main()
{
f();
printf("%x %x\n", x, y);
}

/* b.c */
double x;

void f()
{
x = -0;
}


Remark=======================================
The above programs from 1 to 5 are all constituted
of two separate files, a.c & b.c, and can be
compiled under gcc.
e.g.
/* Linux */
$ gcc -o test a.c b.c
$ ./test
/* Windows */
C:\gcc -o test.exe a.c b.c
C:\test


Question=======================================
I'm wondering about the strange results generated
by the programs. Please help me. I want to understand
why the results are like that.
By the way, the source codes above are in old K&R style
and will be warned by the gcc, you can change them to
new ISO/ANSI style :)

Thanks in advance & best regards!

Becker
30-Nov-2005
 
W

Walter Roberson

1.===========================================
/* a.c */
int x;
int y;

void main()
{
f();
printf("%x %x\n", x, y);
}

%x expects an unsigned int, not an int.
/* b.c */
double x;

void f()
{
x = -0.0;
}

You never initialize y at all, and you initialize x to the wrong
type. double might or might not be the same size as int; it is not
uncommon for it to be longer, but that is not certain. If it is longer
then you -might- end up overwritting y, but the relative order of x and
y are not fixed by the standard, and double might be larger than two
ints together so you might be scribbling on a random portion of memory.

If you happen to be using IEEE754 floating point, then -0.0 is
considered to be different than 0.0 . Even on systems that it is not,
the floating point representation of 0 is not certain to be all binary
0's (though -some- floating point standards require that all-binary 0's
must be treated as floating point 0.)

Your programs 2 thru 4 are just variations on exactly the same themes
with various different values initializing the memory that might or might
not be scribbled over.

5.===========================================
void f()
{
x = -0;
}

-0 is an integer constant, and it is the same as 0 on all but the
rather-uncommon signed-magnitude machines (which are allowed for by
the standard.) -0 as an integer is thus the same as 0 as an integer.
That integer is then cast to the double that you have declared x to
be in this file. On some systems that is different than the floating
point -0.0 .
 
O

Old Wolf

Becker said:
1.===========================================
/* a.c */
int x;
int y;

void main()
{
f();
printf("%x %x\n", x, y);
}

/* b.c */
double x;

void f()
{
x = -0.0;
}

All of your programs have undefined behaviour because there
are two variables called 'x' with external linkage.

Also, "%x" is only to be used for unsigned ints, and
main should return an int (allowing "void main" is compiler-
specific).

For an understanding of why GCC gives the results you see,
please ask your question on a GCC newsgroup.
 
S

slebetman

Old said:
All of your programs have undefined behaviour because there
are two variables called 'x' with external linkage.

The programs are rubbish of course. But wouldn't the file scope of x
mean that function f() is actually refering to the double x instead of
the int x?
 
J

Jack Klein

1.===========================================
/* a.c */
int x;
int y;

void main()
{
f();
printf("%x %x\n", x, y);
}

/* b.c */
double x;

void f()
{
x = -0.0;
}

[snip several other examples]

Each of your examples defines main() with a return type of void, and
also has main() call a function without a declaration in scope. There
is no version of the C standard where a program performing both of
these actions is legal.

If you are using gcc as a standard compiler conforming to the C
standard prior to 1999, 'void main()' invokes undefined behavior
immediately. Friends don't let friends void main(). If you are using
gcc as a standard compiler conforming to a 1999 or later version of
the C standard, it is a constraint violation to call a function
without a declaration in scope.

So you are not invoking gcc to conform to any version of the C
standard, which is just as well, because your program is not actually
valid C.

But as to the question you asked, even if you fixed these things:

Each of your examples defines 'x' with external linkage in two
different source files. It doesn't even make any difference whether
'x' has the same type or different types in the two definitions, you
have broken a rule and have undefined behavior.

Once you have undefined behavior, it's "game over, thanks for playing,
Carol will give you some lovely parting gifts". The C language
neither knows or cares what happens next, there is no right or wrong.
It could cause the CD drive tray to jump out and hit you in the nose.
Even if the computer does not have a CD drive.

The C standard says this in "6.9 External definitions" paragraph 5:

"An external definition is an external declaration that is also a
definition of a function (other than an inline definition) or an
object. If an identifier declared with external linkage is used in an
expression (other than as part of the operand of a sizeof operator
whose result is an integer constant), somewhere in the entire program
there shall be exactly one external definition for the identifier;
otherwise, there shall be no more than one."
 
J

Jack Klein

The programs are rubbish of course. But wouldn't the file scope of x
mean that function f() is actually refering to the double x instead of
the int x?

This is part of what I posted in a reply to the OP, but I'll repeat it
here for your benefit. The issue here is NOT file scope, but most
specifically IS external linkage:

The C standard says this in "6.9 External definitions" paragraph 5:

"An external definition is an external declaration that is also a
definition of a function (other than an inline definition) or an
object. If an identifier declared with external linkage is used in an
expression (other than as part of the operand of a sizeof operator
whose result is an integer constant), somewhere in the entire program
there shall be exactly one external definition for the identifier;
otherwise, there shall be no more than one."

Violating a 'shall' outside of a constraint section is just plain old
ordinary undefined behavior. You have left planet C behind and
entered the Twilight Zone, and there is no right or wrong.
 
S

Skarmander

The programs are rubbish of course. But wouldn't the file scope of x
mean that function f() is actually refering to the double x instead of
the int x?
Nope, because technically, there's no difference between the two.

First of all, this program is in violation of:

6.9-5 "If an identifier declared with external linkage is used in an
expression [..], somewhere in the entire program there shall be exactly one
external definition for the identifier; [..]"

But suppose one of these was an "extern" declaration instead, so there was
only one definition. Then we get:

6.2.2-2 "In the set of translation units and libraries that constitutes an
entire program, each declaration of a particular identifier with external
linkage denotes the same object or function."

So the "int x" and "double x" must be the same object. But of course that's
not possible, and we are violating:

6.2.7-2 "All declarations that refer to the same object or function shall
have compatible type; otherwise, the behavior is undefined."

Needless to say, int and double are not compatible types.

Now, in *practice*, a compiler will indeed treat the 'x' in a.c and the 'x'
in b.c as different objects within their respective scopes, compile the
units as such, and then the linker will either notice a conflict (best
case), create two separate objects (suboptimal but not completely awful
case), or not notice anything at all and happily merge the storage for these
incompatible objects (worst but unfortunately also most likely case).

Try this: assign some nontrivial value to 'x' in 'f', try to compile and
link the program, then run it if this works. You should see the value of the
double (partially) reinterpreted as an int. Say hi to the nasal demons for
me when you do this.

This notwithstanding, a platform would actually be allowed to format your
harddisk immediately after parsing both a.c and b.c, without paying any
consideration to scope. The behavior is undefined; scope is irrelevant.

S.
 
S

slebetman

Jack said:
This is part of what I posted in a reply to the OP, but I'll repeat it
here for your benefit. The issue here is NOT file scope, but most
specifically IS external linkage:

The C standard says this in "6.9 External definitions" paragraph 5:

"An external definition is an external declaration that is also a
definition of a function (other than an inline definition) or an
object. If an identifier declared with external linkage is used in an
expression (other than as part of the operand of a sizeof operator
whose result is an integer constant), somewhere in the entire program
there shall be exactly one external definition for the identifier;
otherwise, there shall be no more than one."

Violating a 'shall' outside of a constraint section is just plain old
ordinary undefined behavior. You have left planet C behind and
entered the Twilight Zone, and there is no right or wrong.

Yes I understand that. But I didn't think all globals are external
linkage by default. I thought you need to use the 'extern' keyword for
that. So, without extern globals with the same name have undefined
behavior? I'm guessing that globals declared with the 'static' keyword
can have the same name.
 
R

Richard Heathfield

(e-mail address removed) said:
But I didn't think all globals are external
linkage by default. I thought you need to use the 'extern' keyword for
that.

No. When you do this:

extern int foo;

you are saying "dear compiler, I promise to you that there is an object
called foo, which is an int, but its storage is already reserved elsewhere
so you don't need to reserve any; just trust me on this". Each translation
unit needing to read or write foo (EXCEPT ONE) will need to have such a
line. Typically, such lines are put into a header.

In exactly one place in your program (at file scope), you must keep your
promise:

int foo;

The translation unit that has this line need not have an extern int foo; as
well (although it will do no harm if it does).
 
S

slebetman

Richard said:
(e-mail address removed) said:


No. When you do this:

extern int foo;

you are saying "dear compiler, I promise to you that there is an object
called foo, which is an int, but its storage is already reserved elsewhere
so you don't need to reserve any; just trust me on this". Each translation
unit needing to read or write foo (EXCEPT ONE) will need to have such a
line. Typically, such lines are put into a header.

In exactly one place in your program (at file scope), you must keep your
promise:

int foo;

The translation unit that has this line need not have an extern int foo; as
well (although it will do no harm if it does).

Ah yes, quite right. Extern merely means "it's somewhere else" not
"please export this". Thank you for reminding me.
 
B

Becker

Thanks all :)
Yes, I understand now. The codes are full of undifined behaviors.
And If I want to know why the results are like that, I should
get to know how gcc treats those "undifined behaviors".

PS: I get those programs from a forum :p
 
F

Flash Gordon

The programs are rubbish of course. But wouldn't the file scope of x
mean that function f() is actually refering to the double x instead of
the int x?

No, both variable x have external linkage therefore the behaviour is
undefined and on some systems, with some options, won't even link. If it
does link the compiler is allowed to put both items in the same
location, make that location the size of the smaller (probably int) and
send a letter to you mother telling her you are dead. Alternatively the
compiler/linker could place the two variables in separate locations and
send a letter to the accounts department of the company you work for
telling them to stop your salary. Anything it does is valid.
 
B

Becker

Flash said:
No, both variable x have external linkage therefore the behaviour is
undefined and on some systems, with some options, won't even link. If it
does link the compiler is allowed to put both items in the same
location, make that location the size of the smaller (probably int) and
send a letter to you mother telling her you are dead. Alternatively the
compiler/linker could place the two variables in separate locations and
send a letter to the accounts department of the company you work for
telling them to stop your salary. Anything it does is valid.

hehe, I got it.
 

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,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top