Is this code valid

A

Arin Chaudhuri

Hi,
Is the code below that gives the bytes that make up a double valid?
/******/
#include <stdio.h>

int
main(void)
{
double w=0.1;
unsigned char *p;
unsigned char c;
size_t i;
p=(unsigned char *)&w;
for(i=0;i < sizeof w;i++,p++)
{
c=*p;
printf("%X ",(unsigned) c);
}
getchar();
return 0;
}
/****/

My questions are:
1. Is it permissible to cast a pointer to a double to a pointer to an
unsigned char and extract the values byte by byte as above?

2. At the end of the loop p points to a possibly invalid location. I
know that one can point to one past the end of an array, but here this
doesn't hold. Does this lead to undefined behavior?
 
M

Martin Dickopp

Hi,
Is the code below that gives the bytes that make up a double valid?
/******/
#include <stdio.h>

int
main(void)
{
double w=0.1;
unsigned char *p;
unsigned char c;
size_t i;
p=(unsigned char *)&w;
for(i=0;i < sizeof w;i++,p++)
{
c=*p;
printf("%X ",(unsigned) c);
}
getchar();
return 0;
}
/****/

My questions are:
1. Is it permissible to cast a pointer to a double to a pointer to an
unsigned char and extract the values byte by byte as above?

Yes. This is know as accessing the "representation" of an object.
2. At the end of the loop p points to a possibly invalid location. I
know that one can point to one past the end of an array, but here this
doesn't hold. Does this lead to undefined behavior?

No. For the purpose of arithmentic operations on pointers, a pointer to
an object which is not array element behaves like a pointer to an array
of size one. After the loop, `p' points to `&w + 1', i.e. one past what
would be the last element of `w' if it were an array. That's allowed.

Martin
 
L

Leor Zolman

No. For the purpose of arithmentic operations on pointers, a pointer to
an object which is not array element behaves like a pointer to an array
of size one. After the loop, `p' points to `&w + 1', i.e. one past what
would be the last element of `w' if it were an array. That's allowed.

I understand what you mean, and the OP probably does too, but it seems to
me that it would help this explanation a little to explicitly say "pointer
to an array of char of size one" rather than simply "pointer to an array of
size one". Because, as per conventional pointer arithmetic rules, your
later example expression "&w + 1" would mean something totally different
(than what I think you intended) if based on w's actual type.
-leor
 
B

Barry Schwarz

I understand what you mean, and the OP probably does too, but it seems to
me that it would help this explanation a little to explicitly say "pointer
to an array of char of size one" rather than simply "pointer to an array of
size one". Because, as per conventional pointer arithmetic rules, your
later example expression "&w + 1" would mean something totally different
(than what I think you intended) if based on w's actual type.

But in this case, p points to an array of 1 double and at the end of
the loop points one byte past the last byte in the double.


<<Remove the del for email>>
 
L

Leor Zolman

But in this case, p points to an array of 1 double and at the end of
the loop points one byte past the last byte in the double.

Arghhh. For some bizarre reason, the picture in my head of &w had the
pointer pointing to the end of w instead of the beginning. That was new.
Thanks,
-leor
 
M

Mike Wahler

Leor Zolman said:
Arghhh. For some bizarre reason, the picture in my head of &w had the
pointer pointing to the end of w instead of the beginning. That was new.

No, 'new' would be C++.

Sorry, couldn't resist. :)

-Mike
 
E

E. Robert Tisdale

Arin said:
Is the code below that gives the bytes that make up a double valid?

/******/
#include <stdio.h>
#include said:
int
main(int argc, char* argv[]) {
const double w = 0.1;
const unsigned char* p = (unsigned char*)(&w);
for (size_t i = 0; i < sizeof w; ++i) {
fprintf(stdout, "%X ", (unsigned int)(p));
}
getchar();
return EXIT_SUCCESS;
}
/****/

My questions are:

1. Is it permissible to cast a pointer to a double
to a pointer to an unsigned char
and extract the values byte by byte as above?

That's the way that I'd do it.
2. At the end of the loop p points to a possibly invalid location.
I know that one can point to one past the end of an array,
but here this doesn't hold. Does this lead to undefined behavior?

No.

Notice that I've trimmed your little program
and made it a little easier to read and understand.
> gcc -Wall -std=c99 -pedantic -o main main.c
> ./main
9A 99 99 99 99 99 B9 3F
 
V

Vittal

I think the code is valid but it is not portable. It's a typical case
of byte swapping and you see different results on Solaris and Linux
boxes.

Here are my results on solaris and linux:

solaris: 3F B9 99 99 99 99 99 9A
linux: 9A 99 99 99 99 99 B9 3F

if you are running your code on a single platform and not worrying
about taking it to other platforms then you can carry on with you
code.

hope this helps you
-vittal
 
M

Martin Dickopp

Leor Zolman said:
I understand what you mean, and the OP probably does too, but it seems to
me that it would help this explanation a little to explicitly say "pointer
to an array of char of size one" rather than simply "pointer to an array of
size one".

That's /not/ what I mean. If that's what you understood, my explanation
wasn't good enough.
Because, as per conventional pointer arithmetic rules, your later
example expression "&w + 1" would mean something totally different
(than what I think you intended) if based on w's actual type.

This is the actual standard text in 6.5.6#7:

| For the purposes of these operators, a pointer to an object that is
| not an element of an array behaves the same as a pointer to the first
| element of an array of length one with the type of the object as its
| element type.

`w' has type `double'. Once a pointer has been set to `&w', arithmetic
operations (only those allowed for pointers, of course) can used to make
the pointer point to any address between `&w' and `&w + 1' (inclusively),
which are more than two addresses unless `sizeof (double) == 1'.

`p' was a pointer to `unsigned char', but that doesn't matter, because
it's not the type of the pointer, but the type of the underlying object
that is used to determine the valid address range.

Clarification for the OP: I assume you know the following fact, but
just in case you don't, I'll mention it anyway. Arithmetic on pointers
works in units of the size of the type pointed to, i.e. if `w' has type
`double', `&w + 1' points to the same address as `(unsigned char *)&w +
sizeof (double)'.

Martin
 
D

Dan Pop

In said:
Is the code below that gives the bytes that make up a double valid?
/******/
#include <stdio.h>

int
main(void)
{
double w=0.1;
unsigned char *p;
unsigned char c;
size_t i;
p=(unsigned char *)&w;
for(i=0;i < sizeof w;i++,p++)
{
c=*p;
printf("%X ",(unsigned) c);
}
getchar();
return 0;
}
/****/

Not only is it valid, but it looks as if it were written by someone
reading c.l.c on a regular basis:

1. c is correctly declared as unsigned char and p as pointer to unsigned
char.

2. i is declared as size_t, to keep even the most anal crititcs happy.

3. c is cast to unsigned in the printf call, to match the type expected
by %X.

The only missing bit is a newline to stdout before returning from main().
No, the getchar() call is not an appropriate replacement for that.

OTOH, the code contains a lot of redundancy, as if it were written by
someone still unfamiliar with writing C code. If I were to write it
myself, it would look like this:

#include <stdio.h>

int main(void)
{
double w = 0.1;
unsigned char *p = (unsigned char *)&w;
size_t i;

for (i = 0; i < sizeof w; i++) printf("%02X ", (unsigned)p);
printf("\n");
return 0;
}

Dan
 
D

Dan Pop

In said:
I think the code is valid but it is not portable.

Any program displaying the representation of a value is, *by definition*,
not strictly conforming. Whether it is portable or not depends on your
definition of "portability". According to the most common definition
"it does the job it was supposed to, on any conforming implementation",
it *is* portable (modulo the missing newline bit) as it does exactly what
it is supposed to: display the representation of a double value. It is
the representation of a double value that is not portable, but this is not
the program's fault.

Dan
 
G

Guest

Dan said:
The only missing bit is a newline to stdout before returning from main().

Eh????
The standard require this?
Can you cite verb and phrase please?

Reading myself the standard I found something like:

If the main function returns to its original caller,
or if the exit function is called, all open files are
closed (hence all output streams are flushed) before
program termination.

So I think there is no reason
to write "\n" before returning from main()..

- Dario
 
L

Leor Zolman

Eh????
The standard require this?
Can you cite verb and phrase please?

Reading myself the standard I found something like:

If the main function returns to its original caller,
or if the exit function is called, all open files are
closed (hence all output streams are flushed) before
program termination.

So I think there is no reason
to write "\n" before returning from main()..

- Dario

You may want to go read the "Program output" thread that began on 5/24.
This isn't as simple an issue as it might seem.
-leor
 
M

Martin Dickopp

Dario (drinking coffee in the office…) said:
Eh????
The standard require this?
Can you cite verb and phrase please?

This has just been discussed in great detail in another thread in this
newsgroup. 7.19.2#2:

| A text stream is an ordered sequence of characters composed into
| lines, each line consisting of zero or more characters plus a
| terminating new-line character. Whether the last line requires a
| terminating new-line character is implementation-defined.
Reading myself the standard I found something like:

If the main function returns to its original caller,
or if the exit function is called, all open files are
closed (hence all output streams are flushed) before
program termination.

There is no doubt that the stdout stream is closed (and therefore
flushed), however this:
[...] there is no reason to write "\n" before returning from main()..

does not follow from the fact that stdout is flushed.

Martin
 
G

Guest

So, if I understood correctly the problem:
-1- The standard does not require a final "\n".
-2- A specific implementation may (or may not) require a final "\n".
-2.a- If a specific implementation require a final "\n" _and_
I miss the final "\n" then everything may happen.
But in any case the file is correctly closed and flushed
(because I leave the main function).
So everything may happen but _only_ _after_ my program
is terminated.

Is this right?

- Dario
 
M

Martin Dickopp

Dario (drinking coffee in the office…) said:
So, if I understood correctly the problem:
-1- The standard does not require a final "\n".

A program is only strictly conforming if it writes a terminating
new-line character to every text output stream it has written to.
If it doesn't, it relies on implementation-defined behavior.
-2- A specific implementation may (or may not) require a final "\n".
Yes.

-2.a- If a specific implementation require a final "\n" _and_
I miss the final "\n" then everything may happen.
Yes.

But in any case the file is correctly closed and flushed
(because I leave the main function).

No. "Everything may happen" means that, well, /everything/ may
happen. :) /Not/ flushing and closing the stream is certainly a
behavior contained in "everything".

My previous post might have been misleading. The guarantee that the
stdout stream is flushed and closed is only valid if no undefined
behavior is invoked. As soon as undefined behavior is invoked, there
are no more guarantees at all.
So everything may happen but _only_ _after_ my program
is terminated.

The behavior becomes undefined when the stream is closed, which is
before the program terminates.

Martin
 
J

j0mbolar

Hi,
Is the code below that gives the bytes that make up a double valid?
/******/
#include <stdio.h>

int
main(void)
{
double w=0.1;
unsigned char *p;
unsigned char c;
size_t i;
p=(unsigned char *)&w;
for(i=0;i < sizeof w;i++,p++)
{
c=*p;
printf("%X ",(unsigned) c);
}
getchar();
return 0;
}
/****/

My questions are:
1. Is it permissible to cast a pointer to a double to a pointer to an
unsigned char and extract the values byte by byte as above?

I don't believe a pointer to double will be correctly
aligned for a pointer to unsigned char.
2. At the end of the loop p points to a possibly invalid location. I
know that one can point to one past the end of an array, but here this
doesn't hold. Does this lead to undefined behavior?

no. what would lead to undefined behavior is if you used
it in an l-value expression.
 
M

Martin Dickopp

I don't believe a pointer to double will be correctly aligned
for a pointer to unsigned char.

Since `unsigned char' has a size of one byte, a pointer to `unsigned
char' cannot have any other alignment requirement than one byte.

Martin
 
A

Arin Chaudhuri

Vittal said:
I think the code is valid but it is not portable. It's a typical case
of byte swapping and you see different results on Solaris and Linux
boxes.

I am sorry I don't understand you at all. If the representation of
double is different, the answers should be different. Is writing a
portable program(as you mean it) possible?
 
E

Eric Sosman

j0mbolar said:
I don't believe a pointer to double will be correctly
aligned for a pointer to unsigned char.

The alignment requirement for any type T must be a
divisor of sizeof(T). (Proof: In `T array[2];' both
`array[0]' and `array[1]' must be correctly aligned.)
Since `sizeof(unsigned char)' is divisible only by one
and since one is a divisor of every type's size, every
type is suitably aligned for `unsigned char'.
no. what would lead to undefined behavior is if you used
it in an l-value expression.

Not clear what you mean by "it." It's certainly all
right to use `p' itself as an lvalue: Apply `--' to it,
for example, and it once again points to a known-to-exist
byte. What you mustn't do is `*p' or equivalent while
`p' points past the end of the object. It doesn't matter
whether this `*p' is the l.h.s. of an assignment, or just
a read-only operand elsewhere in an expression; it's
U.B. as soon as you evaluate `*p'.
 

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
474,143
Messages
2,570,821
Members
47,367
Latest member
mahdiharooniir

Latest Threads

Top