printf() error with long double and null pointer.

P

pete

Jordan Abel wrote:
Casts are not, as far as I can tell, part of constant-expression.

Why not?

The only thing that every expression has, is a type.
If the type of a constant expression comes from a cast,
then the cast sure seems like part of the expression to me.
 
J

Jordan Abel

Why not?

The only thing that every expression has, is a type.
If the type of a constant expression comes from a cast,
then the cast sure seems like part of the expression to me.

but constant-expression is the argument to #if - and casts can't be
evaluated by the preprocessor
 
B

Ben Pfaff

Jordan Abel said:
but constant-expression is the argument to #if - and casts can't be
evaluated by the preprocessor

There seems to be some confusion here between what the
context-free grammar for the constant-expression nonterminal can
produce and what is actually allowed in a constant expression.
The CFG for constant-expression is very liberal:

1 constant-expression:
conditional-expression

However, the constraints on constant-expression are more
restrictive than the CFG.
 
P

pete

Jordan said:
but constant-expression is the argument to #if - and casts can't be
evaluated by the preprocessor

That's a different issue that has to do with
the illiteracy of the preproccessor.

The result of the sizeof operator is a constant expression.
You can declare an array with it in C89.
char array[sizeof(int)];
But you can't use sizeof with #if either.

N869
6.6 Constant expressions
[#8]
Cast operators
in an arithmetic constant expression shall only convert
arithmetic types to arithmetic types, except as part of an
operand to the sizeof operator.
 
M

Micah Cowan

Ben Pfaff said:
There seems to be some confusion here between what the
context-free grammar for the constant-expression nonterminal can
produce and what is actually allowed in a constant expression.
The CFG for constant-expression is very liberal:

1 constant-expression:
conditional-expression

However, the constraints on constant-expression are more
restrictive than the CFG.

And yet still allow casts (even for "integer constant expressions",
which are the most constrained, and are in fact what we're talking about).

As to casts being evaluated by the preprocessor; that is a
_special_exception_ for the preprocessor.

-Micah
 
S

stathis gotsis

Micah Cowan said:
NULL may even be #defined as 0.

The difference is, that, assuming a 16-bit integer type with no
padding bits,

I think you do not need the assumption of no padding bits. The Standard
quotes:
"For any integer type, the object representation where all the bits
are zero shall be a representation of the value zero in that type."
There is no distinction between padding and value bits.
int foo = 0;

is guaranteed to give foo an all-zeroed-bits representation, whereas

void *bar = 0;

is not. Again, assuming the same conditions above about integer types,
you could do:

int *baz = calloc(sizeof *baz);

and *baz would be guaranteed to have the value zero (but don't use
code like the above, because in general you /can't/ make these
assumptions about padding bits, etc).

That same assumption is redundant here as well. Of course, i agree with all
the rest.
 
J

Jordan Abel

I think you do not need the assumption of no padding bits. The Standard
quotes:
"For any integer type, the object representation where all the bits
are zero shall be a representation of the value zero in that type."
There is no distinction between padding and value bits.

That holds in one direction, but it could be that assigning zero to the
variable will result in padding bits set to 1.
 
A

Andrey Tarasevich

fieldfallow said:
...
I thought null pointers had a value of zero. Why is gcc's executable
printing ffffffffh? Also the value of 'ld' seems to be wrong.
...

No. A null pointer has value correctly referred to as... well, 'null
pointer value' (NPV). This value can have any physical representation
chosen by implementation: it could be all zeros, it could be 0xFFFFFFFF,
it could be 0xBAADFOOD or anything else. It could also be type-specific,
i.e. different pointer types can use different physical representations
of their NPV.

What is normally meant by "null pointers had a value of zero" is that
constant integral value '0', when used in pointer context, is
automatically interpreted as NPV of the corresponding type (i.e. it is
implicitly replaced with the correct physical representation of the NPV
by the compiler).

For example, if in some implementation value of 0xBAADFOOD is used as
physical representation of NPV or type 'int*' and value 0xFFFFFFFF is
used as physical representation of NPV or type 'double*', the C code

int* pi = 0;
double* pd = 0;

will be translated into a sequence of operations that physically
initialize 'pi' with value 0xBAADFOOD and 'pd' with value of 0xFFFFFFFF.
 
S

stathis gotsis

Jordan Abel said:
That holds in one direction, but it could be that assigning zero to the
variable will result in padding bits set to 1.

I missed that one. What i said applies in this situation:

int *baz = calloc(sizeof *baz);

where *baz = 0.
 
M

Micah Cowan

stathis gotsis said:
I think you do not need the assumption of no padding bits. The Standard
quotes:
"For any integer type, the object representation where all the bits
are zero shall be a representation of the value zero in that type."
There is no distinction between padding and value bits.

Mm, quite. I'd forgotten about TC2.
 
K

Keith Thompson

Andrey Tarasevich said:
No. A null pointer has value correctly referred to as... well, 'null
pointer value' (NPV). This value can have any physical representation
chosen by implementation: it could be all zeros, it could be 0xFFFFFFFF,
it could be 0xBAADFOOD or anything else. It could also be type-specific,
i.e. different pointer types can use different physical representations
of their NPV.

Right, but as a practical matter, most implementations do happen to
use all-bits-zero as the representation of a null pointer, and the
vast majority of implementations that relatively inexperienced
programmers are likely to run into do so. (If you're using an
implementation that uses a different representation for null pointers,
chances are it's an embedded system and you already know the details
of how it works.)

If you print a null pointer using printf's "%p" format, and the output
looks like a number other than 0, it's *possible* that null pointers
have a representation other than all-bits-zero, but it's more likely
that something else has gone wrong. (And in fact it turned out that
way in this case.)

The first thing I'd try in that case is a minimal program like this:

#include <stdio.h>
int main(void)
{
printf("NULL = %p\n", (void*)NULL);
return 0;
}
 
F

fieldfallow

Andrey said:
No. A null pointer has value correctly referred to as... well, 'null
pointer value' (NPV). This value can have any physical representation
chosen by implementation: it could be all zeros, it could be 0xFFFFFFFF,
it could be 0xBAADFOOD or anything else. It could also be type-specific,
i.e. different pointer types can use different physical representations
of their NPV.

What is normally meant by "null pointers had a value of zero" is that
constant integral value '0', when used in pointer context, is
automatically interpreted as NPV of the corresponding type (i.e. it is
implicitly replaced with the correct physical representation of the NPV
by the compiler).

For example, if in some implementation value of 0xBAADFOOD is used as
physical representation of NPV or type 'int*' and value 0xFFFFFFFF is
used as physical representation of NPV or type 'double*', the C code

int* pi = 0;
double* pd = 0;

will be translated into a sequence of operations that physically
initialize 'pi' with value 0xBAADFOOD and 'pd' with value of 0xFFFFFFFF.


Thanks Andrey! Your explanation is cleared up my confusion regarding the
issue.
 
F

fieldfallow

Keith said:
If you print a null pointer using printf's "%p" format, and the output
looks like a number other than 0, it's *possible* that null pointers
have a representation other than all-bits-zero, but it's more likely
that something else has gone wrong. (And in fact it turned out that
way in this case.)

The first thing I'd try in that case is a minimal program like this:

#include <stdio.h>
int main(void)
{
printf("NULL = %p\n", (void*)NULL);
return 0;
}

Here would printf("NULL = %p\n", (void*)0); also be equivalent?
 
C

CBFalconer

stathis said:
.... snip ...

I missed that one. What i said applies in this situation:

int *baz = calloc(sizeof *baz);

The only thing that applies in that situation is a diagnostic,
along the lines of "missing parameter".

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
More details at: <http://cfaj.freeshell.org/google/>
Also see <http://www.safalra.com/special/googlegroupsreply/>
 
S

stathis gotsis

CBFalconer said:
The only thing that applies in that situation is a diagnostic,
along the lines of "missing parameter".

Yes you are right, i cut-pasted the previous poster's code. He rather meant:
int *baz = calloc(1,sizeof *baz);
 
J

Jack Klein

Hello all,

Before stating my question, I should mention that I'm fairly new to C.

Now, I attempted a small demo that prints out the values of C's numeric
types, both uninitialised and after assigning them their maximum defined
values. However, the output of printf() for the long double 'ld' and the
pointer of type void 'v_p', after initialisation don't seem to be right.

The compiler used was gcc (mingw) with '-Wall', '-std=c99' and
'-pedantic' switches. No warnings were emitted. Incidentally the MS
Visual C++ 2003 compiler's output seems okay. I give them both below:

gcc's output:
ld == -1.#QNAN0
or -1.#QNAN
v_p == FFFFFFFF

msvc's output:
ld == 179769313486231570000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000.000000
or 1.79769e+308
v_p == 00000000

I thought null pointers had a value of zero. Why is gcc's executable
printing ffffffffh? Also the value of 'ld' seems to be wrong.

The code for the demo is:
#include <stdio.h>
#include <limits.h>
#include <float.h>

int main(void) {
char c;
unsigned char uc;
short s;
unsigned short us;
int i;
unsigned int ui;
long l;
unsigned long ul;
float f;
double d;
long double ld;
void *v_p;

printf("c == %c\n\tor %d\nuc == %c\n\tor %u\ns == %hd\nus == %hu\n"
"i == %d\nui == %u\nl == %ld\nul == %lu\nf == %f\n\tor %g\n"
"d == %lf\n\tor %g\nld == %Lf\n\tor %Lg\nv_p == %p\n",
c,c,uc,uc,s,us,i,ui,l,ul,f,f,d,d,ld,ld,v_p);

puts("Initialising them with their maximum allowed values...");
c = CHAR_MAX;
uc = UCHAR_MAX;
s = SHRT_MAX;
us = USHRT_MAX;
i = INT_MAX;
ui = UINT_MAX;
l = LONG_MAX;
ul = ULONG_MAX;
f = FLT_MAX;
d = DBL_MAX;
ld = LDBL_MAX;
puts("Initialising v_p with NULL...");
v_p = NULL;

printf("c == %c\n\tor %d\nuc == %c\n\tor %u\ns == %hd\nus == %hu\n"
"i == %d\nui == %u\nl == %ld\nul == %lu\nf == %f\n\tor %g\n"
"d == %lf\n\tor %g\nld == %Lf\n\tor %Lg\nv_p == %p\n",
c,c,uc,uc,s,us,i,ui,l,ul,f,f,d,d,ld,ld,v_p);
return 0;
}

Where is the mistake?

The mistake is caused by a marketing decision made by Microsoft, and
allowed by the C standard.

Mingw is like a typical gcc implementation for most platforms. That
is, it contains a compiler and headers but no libraries. It uses the
standard libraries that already exist on the host system where it is
installed. On 32-bit Windows, that means Microsoft's C library, so
your printf() call call's Microsoft's printf() functions.

Microsoft made a marketing decision not to support the 80-bit extended
precision type of the Intel FPU, so they implement both double and
long double as the same type, a 64-bit floating point object. See
http://support.microsoft.com/default.aspx?scid=kb;en-us;129209 for
Microsoft's explanation of this sorry situation.

gcc, on the other hand, uses the Intel 80-bit extended precision type
for long double.

So you are calling a printf() function that expects one floating point
with a "%Lf" conversion specifier, but you are calling it with a
different type of floating point object, and one that is a different
size.

A mis-match like this in a call to printf() causes undefined behavior.
 
J

Jordan Abel

The mistake is caused by a marketing decision made by Microsoft, and
allowed by the C standard.

Mingw is like a typical gcc implementation for most platforms. That
is, it contains a compiler and headers but no libraries. It uses the
standard libraries that already exist on the host system where it is
installed. On 32-bit Windows, that means Microsoft's C library, so
your printf() call call's Microsoft's printf() functions.

Microsoft made a marketing decision not to support the 80-bit extended
precision type of the Intel FPU, so they implement both double and
long double as the same type, a 64-bit floating point object. See
http://support.microsoft.com/default.aspx?scid=kb;en-us;129209 for
Microsoft's explanation of this sorry situation.

gcc, on the other hand, uses the Intel 80-bit extended precision type
for long double.

There should be a way to specify the lack of it with an __attribute__,
then.

say, int printf(char *, ...) __attribute__((ldbl64));

Just because one is microsoft and the other is gnu doesn't mean that
mingw isn't "at fault" for not properly supporting the calling
conventions used by the vendor library.
So you are calling a printf() function that expects one floating point
with a "%Lf" conversion specifier, but you are calling it with a
different type of floating point object, and one that is a different
size.

A mis-match like this in a call to printf() causes undefined behavior.

Well, only in that attempting to mix code between different
implementations _always_ causes undefined behavior. The simple fact is
that here is a place that gcc and the microsoft libraries fail to
provide a single coherent implementation.
 
K

Keith Thompson

Jack Klein said:
The mistake is caused by a marketing decision made by Microsoft, and
allowed by the C standard.

Mingw is like a typical gcc implementation for most platforms. That
is, it contains a compiler and headers but no libraries. It uses the
standard libraries that already exist on the host system where it is
installed. On 32-bit Windows, that means Microsoft's C library, so
your printf() call call's Microsoft's printf() functions.

Microsoft made a marketing decision not to support the 80-bit extended
precision type of the Intel FPU, so they implement both double and
long double as the same type, a 64-bit floating point object. See
http://support.microsoft.com/default.aspx?scid=kb;en-us;129209 for
Microsoft's explanation of this sorry situation.

gcc, on the other hand, uses the Intel 80-bit extended precision type
for long double.

So you are calling a printf() function that expects one floating point
with a "%Lf" conversion specifier, but you are calling it with a
different type of floating point object, and one that is a different
size.

A mis-match like this in a call to printf() causes undefined behavior.

No, a mis-match like this means that the implementation isn't
conforming.

The C standard requires the implementation as a whole to conform to
its requirements, even if different parts of the implementation (the
compiler and the library) are provided by different parties. If the
compiler and library are visibly incompatible, then the implementation
is as non-conforming as if it yielded 3 for (1+1).
 
F

Flash Gordon

fieldfallow said:
Does even printing out the values cause undefined behaviour? Is any
other operation legal, or must I assign them values before I do anything
with them?

Evaluating them without even printing them invokes undefined behaviour. I.e.
int main(void)
{
int a;
a; /* Undefined behaviour */
return 0;
}
Thanks for your answers. The C book I currently have is not very clear
on this point.

I recommend you get a copy of K&R2 and also read the comp.lang.c FAQ at
http://c-faq.com/ (the Bibliography tells you what K&R2 is and
references lots of other good books)
 

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,955
Messages
2,570,117
Members
46,705
Latest member
v_darius

Latest Threads

Top