Explain why this prints the same

E

Eric Lilja

Hello, I'm trying to help someone on a linux-oriented forum. I've taken
his original code and cleaned it up, but I am still wondering about
something. Here's the code:

#include <stdio.h>

int
main(void)
{
int a[][3]={ {1,2,3},{4,5,6,},{7,8,9} };
int *b = (int *)a;
int **c = (int **)a;
int i = 0;
int num_elements = sizeof(a) / sizeof(int);

for(i = 0; i< num_elements; ++i)
printf("*b = %d *c = %d\n", *b++, *c++);

return 0;
}

When compiled I get:
gcc -Wall -W -ansi -pedantic -g -O0 -c -o pointer_wonder.o
pointer_wonder.c
pointer_wonder.c: In function `main':
pointer_wonder.c:13: warning: int format, pointer arg (arg 3)
gcc pointer_wonder.o -o pointer_wonder.exe

The output is:
*b = 1 *c = 1
*b = 2 *c = 2
*b = 3 *c = 3
*b = 4 *c = 4
*b = 5 *c = 5
*b = 6 *c = 6
*b = 7 *c = 7
*b = 8 *c = 8
*b = 9 *c = 9

Why is it same for *b and *c?

/ Eric
 
P

pemo

Eric Lilja said:
Hello, I'm trying to help someone on a linux-oriented forum. I've taken
his original code and cleaned it up, but I am still wondering about
something. Here's the code:

#include <stdio.h>

int
main(void)
{
int a[][3]={ {1,2,3},{4,5,6,},{7,8,9} };
int *b = (int *)a;
int **c = (int **)a;
int i = 0;
int num_elements = sizeof(a) / sizeof(int);

for(i = 0; i< num_elements; ++i)
printf("*b = %d *c = %d\n", *b++, *c++);

return 0;
}

When compiled I get:
gcc -Wall -W -ansi -pedantic -g -O0 -c -o pointer_wonder.o
pointer_wonder.c
pointer_wonder.c: In function `main':
pointer_wonder.c:13: warning: int format, pointer arg (arg 3)
gcc pointer_wonder.o -o pointer_wonder.exe

The output is:
*b = 1 *c = 1
*b = 2 *c = 2
*b = 3 *c = 3
*b = 4 *c = 4
*b = 5 *c = 5
*b = 6 *c = 6
*b = 7 *c = 7
*b = 8 *c = 8
*b = 9 *c = 9

Why is it same for *b and *c?

*b gives you back an int

whereas

*c gives you back an int *

However, the 'values' of each are taken from a.

So, printf would really like a %p (at least) for the third arg.
 
P

pemo

pemo said:
Eric Lilja said:
Hello, I'm trying to help someone on a linux-oriented forum. I've taken
his original code and cleaned it up, but I am still wondering about
something. Here's the code:

#include <stdio.h>

int
main(void)
{
int a[][3]={ {1,2,3},{4,5,6,},{7,8,9} };
int *b = (int *)a;
int **c = (int **)a;
int i = 0;
int num_elements = sizeof(a) / sizeof(int);

for(i = 0; i< num_elements; ++i)
printf("*b = %d *c = %d\n", *b++, *c++);

return 0;
}

When compiled I get:
gcc -Wall -W -ansi -pedantic -g -O0 -c -o pointer_wonder.o
pointer_wonder.c
pointer_wonder.c: In function `main':
pointer_wonder.c:13: warning: int format, pointer arg (arg 3)
gcc pointer_wonder.o -o pointer_wonder.exe

The output is:
*b = 1 *c = 1
*b = 2 *c = 2
*b = 3 *c = 3
*b = 4 *c = 4
*b = 5 *c = 5
*b = 6 *c = 6
*b = 7 *c = 7
*b = 8 *c = 8
*b = 9 *c = 9

Why is it same for *b and *c?

*b gives you back an int

whereas

*c gives you back an int *

However, the 'values' of each are taken from a.

So, printf would really like a %p (at least) for the third arg.

So, [I should have added] *c's value is 1, 2, 3, ... where c is an int *
(obviously, you wouldn't want to deref that!
 
K

Kenneth Brody

pemo said:
Eric Lilja said:
Hello, I'm trying to help someone on a linux-oriented forum. I've taken
his original code and cleaned it up, but I am still wondering about
something. Here's the code:

#include <stdio.h>

int
main(void)
{
int a[][3]={ {1,2,3},{4,5,6,},{7,8,9} };
int *b = (int *)a;
int **c = (int **)a;
int i = 0;
int num_elements = sizeof(a) / sizeof(int);

for(i = 0; i< num_elements; ++i)
printf("*b = %d *c = %d\n", *b++, *c++);

return 0;
}

When compiled I get:
gcc -Wall -W -ansi -pedantic -g -O0 -c -o pointer_wonder.o
pointer_wonder.c
pointer_wonder.c: In function `main':
pointer_wonder.c:13: warning: int format, pointer arg (arg 3)

Note the warning here. ("*c++" is an "int*" type.)
gcc pointer_wonder.o -o pointer_wonder.exe

The output is:
*b = 1 *c = 1 [...]
*b = 9 *c = 9

Why is it same for *b and *c?

*b gives you back an int

whereas

*c gives you back an int *

However, the 'values' of each are taken from a.

And it happens to be that sizeof(int)==sizeof(int*). Try it on a
system where this is not true, such as an old 16-bit DOS compiler
in large model mode. (sizeof int == 2, and sizeof int* is 4.)

In a test here, I get:

*b = 1 *c = 1
*b = 2 *c = 3
*b = 3 *c = 5
*b = 4 *c = 7
*b = 5 *c = 9
*b = 6 *c = 2013
*b = 7 *c = 2013
*b = 8 *c = 9
*b = 9 *c = 2680
So, printf would really like a %p (at least) for the third arg.

Though it would still print (basically) the same thing, as *c is
still really an int being treated as int*.


In any case, this probably falls under the "UB" heading, as you are
taking int's and treating them as int*'s.

(Yes, I know the apostrophes don't really belong in the last sentence,
but it makes it more legible, IMHO.)


--
+-------------------------+--------------------+-----------------------------+
| Kenneth J. Brody | www.hvcomputer.com | |
| kenbrody/at\spamcop.net | www.fptech.com | #include <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------------+
Don't e-mail me at: <mailto:[email protected]>
 
P

pemo

Kenneth Brody said:
pemo said:
Eric Lilja said:
Hello, I'm trying to help someone on a linux-oriented forum. I've taken
his original code and cleaned it up, but I am still wondering about
something. Here's the code:

#include <stdio.h>

int
main(void)
{
int a[][3]={ {1,2,3},{4,5,6,},{7,8,9} };
int *b = (int *)a;
int **c = (int **)a;
int i = 0;
int num_elements = sizeof(a) / sizeof(int);

for(i = 0; i< num_elements; ++i)
printf("*b = %d *c = %d\n", *b++, *c++);

return 0;
}

When compiled I get:
gcc -Wall -W -ansi -pedantic -g -O0 -c -o pointer_wonder.o
pointer_wonder.c
pointer_wonder.c: In function `main':
pointer_wonder.c:13: warning: int format, pointer arg (arg 3)

Note the warning here. ("*c++" is an "int*" type.)
gcc pointer_wonder.o -o pointer_wonder.exe

The output is:
*b = 1 *c = 1 [...]
*b = 9 *c = 9

Why is it same for *b and *c?

*b gives you back an int

whereas

*c gives you back an int *

However, the 'values' of each are taken from a.

And it happens to be that sizeof(int)==sizeof(int*). Try it on a
system where this is not true, such as an old 16-bit DOS compiler
in large model mode. (sizeof int == 2, and sizeof int* is 4.)

In a test here, I get:

*b = 1 *c = 1
*b = 2 *c = 3
*b = 3 *c = 5
*b = 4 *c = 7
*b = 5 *c = 9
*b = 6 *c = 2013
*b = 7 *c = 2013
*b = 8 *c = 9
*b = 9 *c = 2680
So, printf would really like a %p (at least) for the third arg.

Though it would still print (basically) the same thing, as *c is
still really an int being treated as int*.


In any case, this probably falls under the "UB" heading, as you are
taking int's and treating them as int*'s.

(Yes, I know the apostrophes don't really belong in the last sentence,
but it makes it more legible, IMHO.)

That's so cool!

I really should *invest* in an 'old' compiler! And, it'd be so great to
have a 'Take me Back' experience of my own [in an archive.org kinda sense].
Like, if only I'd saved some of programs I 'cut' into 7-hole punched tape (I
never had access to the punched cards!: they were [for eyes only] in the
domain of the machine room ops!)

I used to have my first Ph.D. thesis on one of those removable hard drives -
big clunky things, with equaly big handles!

Ok, I'm 48 - anyone care to beat that?

Old Fart
 
P

pete

Eric said:
Hello, I'm trying to help someone on a linux-oriented forum. I've taken
his original code and cleaned it up, but I am still wondering about
something. Here's the code:

#include <stdio.h>

int
main(void)
{
int a[][3]={ {1,2,3},{4,5,6,},{7,8,9} };
int *b = (int *)a;
int **c = (int **)a;
int i = 0;
int num_elements = sizeof(a) / sizeof(int);

for(i = 0; i< num_elements; ++i)
printf("*b = %d *c = %d\n", *b++, *c++);

return 0;
}

I know this is going to be hard to believe,
but the standard doesn't actually define stepping all
the way through a two dimensional array
with anything other than a pointer to a character type,
even though all the bytes of array object are contiguous.

/* BEGIN new.c */

#include <stdio.h>

int
main(void)
{
char a[][3]={ "123","456","789" };
char *b = a[0];
size_t i = 0;
size_t num_elements = sizeof(a) / sizeof **a;

for(i = 0; i< num_elements; ++i) {
printf("*b = %c\n", *b++);
}
return 0;
}

/* END new.c */
 
D

Dik T. Winter

> Hello, I'm trying to help someone on a linux-oriented forum. I've taken
> his original code and cleaned it up, but I am still wondering about
> something. Here's the code:

Ok, let's see:
> #include <stdio.h>
>
> int
> main(void)
> {
> int a[][3]={ {1,2,3},{4,5,6,},{7,8,9} };

Well, for a a segment of 9 integers is allocated succesively filled with
the integers 1 to 9.
> int *b = (int *)a;

As 'a' in this context is a pointer to an array of three ints, you are
lying to the compiler. The result is (I think) undefined behaviour.
> int **c = (int **)a;

And more so. Here the result is certain to be undefined behaviour.
> int i = 0;
> int num_elements = sizeof(a) / sizeof(int);
>
> for(i = 0; i< num_elements; ++i)
> printf("*b = %d *c = %d\n", *b++, *c++);

And this is the third time you are lying. You are lucky that
sizeof(int) == sizeof(*int) on your system.
>
> return 0;
> } ....
> The output is:
> *b = 1 *c = 1 ....
> *b = 9 *c = 9
>
> Why is it same for *b and *c?

Because on your system sizeof(int) == sizeof(*int) and the compiler
disregards all those lies.
 
P

pete

pete said:
Eric said:
Hello, I'm trying to help someone on a linux-oriented forum. I've taken
his original code and cleaned it up, but I am still wondering about
something. Here's the code:

#include <stdio.h>

int
main(void)
{
int a[][3]={ {1,2,3},{4,5,6,},{7,8,9} };
int *b = (int *)a;
int **c = (int **)a;
int i = 0;
int num_elements = sizeof(a) / sizeof(int);

for(i = 0; i< num_elements; ++i)
printf("*b = %d *c = %d\n", *b++, *c++);

return 0;
}

I know this is going to be hard to believe,
but the standard doesn't actually define stepping all
the way through a two dimensional array
with anything other than a pointer to a character type,
even though all the bytes of array object are contiguous.

Unless you're treating it as a one dimensional array
of an array type.

/* BEGIN new.c */

#include <stdio.h>
#include <string.h>

int main(void)
{
char a[][3]={ "123","456","789" };
char *b = a[0];
char c[3][4] = {0};
char (*d)[3] = a;
char (*e)[4] = c;
size_t i;
size_t num_elements = sizeof(a) / sizeof **a;

for(i = 0; i< num_elements; ++i) {
printf("*b = %c\n", *b++);
}
putchar('\n');
num_elements = sizeof a / sizeof *a;
for(i = 0; i != num_elements; ++i) {
memcpy(e, d, sizeof *d);
printf("*e = %s\n", *e);
++d;
++e;
}
return 0;
}

/* END new.c */
 
O

Old Wolf

Dik said:
Eric Lilja said:
#include <stdio.h>

int main(void) {
int a[][3]={ {1,2,3},{4,5,6,},{7,8,9} };
int *b = (int *)a;

As 'a' in this context is a pointer to an array of three ints, you are
lying to the compiler. The result is (I think) undefined behaviour.

I don't think there is any lying here; you are allowed to cast
one object pointer type to another as long as there are no
alignment issues. The troubles only (potentially) start when
you dereference the pointer.

&a[0] must be correctly aligned for int, because the members
of a[0] are ints, and there cannot be any padding before the
first member of an array.
And more so. Here the result is certain to be undefined behaviour.

Here there could be alignment issues. But if there are not,
then no UB if c is not dereferenced.

(Note: I don't think I worded that exactly right -- the code always
has UB, but on implementations where there were no alignment
problems, the code would behave in a well-defined manner).
And this is the third time you are lying. You are lucky that
sizeof(int) == sizeof(*int) on your system.

Agree (int* you mean, not *int).

IMHO, *b causes undefined behaviour iff i >= 3,
because "a" (ie. &a[0]) points to an array of 3 ints,
and a bounds checking implementation could flag this.

But if b were initialized as: int *b = &a;
then there would be no UB because &a points to
an object that is the size of nine ints.

But some people think that even as written there is
no UB because a[0] is part of the entire object "a",
I don't recall seeing any consensus when this point
has been debated in the past.

*c would cause undefined behaviour in most cases,
because either the bytes pointed to by c form a
trap representation for an int*, or if they form an address
that isn't the address of a valid object.
 
K

Kenneth Brody

pemo wrote:
[...]
main(void)
{
int a[][3]={ {1,2,3},{4,5,6,},{7,8,9} };
int *b = (int *)a;
int **c = (int **)a;
int i = 0;
int num_elements = sizeof(a) / sizeof(int);

for(i = 0; i< num_elements; ++i)
printf("*b = %d *c = %d\n", *b++, *c++);

return 0;
} [...]
*c gives you back an int *

However, the 'values' of each are taken from a.

And it happens to be that sizeof(int)==sizeof(int*). Try it on a
system where this is not true, such as an old 16-bit DOS compiler
in large model mode. (sizeof int == 2, and sizeof int* is 4.)

In a test here, I get:

*b = 1 *c = 1 [...]
*b = 9 *c = 2680
[...]
That's so cool!

I really should *invest* in an 'old' compiler! And, it'd be so great to
have a 'Take me Back' experience of my own [in an archive.org kinda sense].
Like, if only I'd saved some of programs I 'cut' into 7-hole punched tape (I
never had access to the punched cards!: they were [for eyes only] in the
domain of the machine room ops!)

Well, you could probably find a very new compiler where
sizeof(int)!=sizeof(int*). I imagine that some of the 64-bit systems
might qualify.
I used to have my first Ph.D. thesis on one of those removable hard drives -
big clunky things, with equaly big handles!

You mean one of those washing machines that stored data?

That reminds me... My friend never returned my DEC-Tape with the copy
of Star Trek on it that he borrowed (mumble, mumble) years ago.
Ok, I'm 48 - anyone care to beat that?

Well, I'm "only" 44, but I do have 34+ years of programming experience.

--
+-------------------------+--------------------+-----------------------------+
| Kenneth J. Brody | www.hvcomputer.com | |
| kenbrody/at\spamcop.net | www.fptech.com | #include <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------------+
Don't e-mail me at: <mailto:[email protected]>
 
T

tedu

pemo said:
Kenneth Brody said:
And it happens to be that sizeof(int)==sizeof(int*). Try it on a
system where this is not true, such as an old 16-bit DOS compiler
in large model mode. (sizeof int == 2, and sizeof int* is 4.)

I really should *invest* in an 'old' compiler! And, it'd be so great to
have a 'Take me Back' experience of my own [in an archive.org kinda sense].

you could buy a new amd64 machine and experience the same pain today,
only faster. :)
 
P

pemo

You mean one of those washing machines that stored data?

That reminds me... My friend never returned my DEC-Tape with the copy
of Star Trek on it that he borrowed (mumble, mumble) years ago.


Well, I'm "only" 44, but I do have 34+ years of programming experience.

"And you tell that to young. people today and. they won't believe you!"

Yup, it was a bit 'top-loader like' if memory still serves.

You've been programming longer then. I started as an undergrad - back then,
there was no university in the UK that offered *just* a degree in computing,
so I did a mixed degree, Computing and Pure Maths. Note the lack of the
term 'Computer Science' - wasn't really invented yet.

All my programming assignments had to be written out on coding sheets, and
handed into the machine room - where someone entered them, and probably
produced punched cards etc ... If you were unlucky, you'd turn up the next
day to get your printout, and find it said syntax error. Sometimes, it even
told you where! The *big* machine on which this stuff ran used to go on to
'MOP' at 6.00pm everyday. I don't believe I ever knew what MOP stood for,
but I know what it meant - the machine was hired out from 6.00 until
midnight every evening.

We did have one machine that we could program directly. A PDP ? In order to
use that, you had to enter the boot strap routine by flicking switches, and
then load the OS from seven hole punched tape.

Ah, those were the days!
 
P

pemo

tedu said:
pemo said:
Kenneth Brody said:
And it happens to be that sizeof(int)==sizeof(int*). Try it on a
system where this is not true, such as an old 16-bit DOS compiler
in large model mode. (sizeof int == 2, and sizeof int* is 4.)

I really should *invest* in an 'old' compiler! And, it'd be so great to
have a 'Take me Back' experience of my own [in an archive.org kinda
sense].

you could buy a new amd64 machine and experience the same pain today,
only faster. :)

:)
 

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,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top