Help!!! What will be the problem in the below program?

S

Santa

Guys:

I am sorry to ask this question. I am trying to print the addresses of
the below two printf's, both are not printing the same, what was the
problem, can somebody correct me. Thanks in advance.
===================

#include <stdio.h>

typedef struct T_USER_CONF {
int x;
int y;
}t_conf;

typedef struct devData {
t_conf tConf[2][12];

}t_dev;


main()
{
t_dev Dev;
int i, j;

printf("Dev 0x%x\n",Dev);

printf("===FIRST PRINT BEGIN ===========\n");
for(i=0; i < 24; i++)
printf(" addr : 0x%x \n",Dev.tConf);
printf("===FIRST PRINT END ===========\n");

printf("===SECOND PRINT BEGIN ===========\n");
for(i=0; i < 2; i++)
for(j=0; j < 12; j++)
printf(" addr : 0x%x \n",Dev.tConf[j]);
printf("===SECOND PRINT END ===========\n");



}
 
E

Emmanuel Delahaye

In said:
I am sorry to ask this question. I am trying to print the addresses of
the below two printf's, both are not printing the same, what was the
problem, can somebody correct me. Thanks in advance.
===================

#include <stdio.h>

typedef struct T_USER_CONF {
int x;
int y;
}t_conf;

typedef struct devData {
t_conf tConf[2][12];

}t_dev;


main()
{
t_dev Dev;
int i, j;

printf("Dev 0x%x\n",Dev);

printf("===FIRST PRINT BEGIN ===========\n");
for(i=0; i < 24; i++)
printf(" addr : 0x%x \n",Dev.tConf);


printf (" addr : %p\n", (void*) Dev.tConf + i);
printf("===FIRST PRINT END ===========\n");

printf("===SECOND PRINT BEGIN ===========\n");
for(i=0; i < 2; i++)
for(j=0; j < 12; j++)
printf(" addr : 0x%x \n",Dev.tConf[j]);


printf (" addr : %p\n", (void*) Dev.tConf + j);
 
M

Martin Ambuhl

(e-mail address removed) (Santa) wrote (29 Jun 2003) in
/ comp.lang.c:
Guys:

I am sorry to ask this question. I am trying to print the addresses of
the below two printf's, both are not printing the same, what was the
problem, can somebody correct me. Thanks in advance.

As nearly as I can make out from your code and question, you need at
least to make the changes below. Note that it makes no sense "to print
the address of the below two printf's"; I have interpreted this as
meaning "to print the addresses of objects in the below printfs." Note
that there a *three* instances in which you do this wrong.

17c17
< main()
---
int main(void)
22c22
< printf("Dev 0x%x\n", Dev);
---
printf("Dev %p\n", (void *) &Dev);
26c26
< printf(" addr : 0x%x \n", Dev.tConf);
---
printf(" addr : %p\n", (void *) Dev.tConf);

32c32
< printf(" addr : 0x%x \n", Dev.tConf[j]);
---
printf(" addr : %p\n", (void *) &Dev.tConf[j]);



===================

#include <stdio.h>

typedef struct T_USER_CONF {
int x;
int y;
}t_conf;

typedef struct devData {
t_conf tConf[2][12];

}t_dev;


main()
{
t_dev Dev;
int i, j;

printf("Dev 0x%x\n",Dev);

printf("===FIRST PRINT BEGIN ===========\n");
for(i=0; i < 24; i++)
printf(" addr : 0x%x \n",Dev.tConf);
printf("===FIRST PRINT END ===========\n");

printf("===SECOND PRINT BEGIN ===========\n");
for(i=0; i < 2; i++)
for(j=0; j < 12; j++)
printf(" addr : 0x%x \n",Dev.tConf[j]);
printf("===SECOND PRINT END ===========\n");



}
 
C

Chris Torek

(This article is cross-postd to comp.lang.c and comp.lang.c++. It
seems to me the original poster should decide which language he is
using and stick with that one. Emmanuel Delahaye apparently saw this
in comp.lang.c, given the quoting below.)

I do not understand this claim -- there is something quite suspect
about the phrase "address of [a specific] printf". (To obtain the
address of the printf() function, simply declare the function, then
write its name without subsequent parentheses. The result has
type "pointer to function (const char *, ...) returning int.") I
will run with E.D.'s interpretation, though.

[edited slightly for readability]
#include <stdio.h>

typedef struct T_USER_CONF {
int x;
int y;
} t_conf;

typedef struct devData {
t_conf tConf[2][12];
} t_dev;

If you are using C++, note that "struct blah" also declares "blah"
as a type-declaring name (with some special contortions for handling
legacy code that, for the most part, you can ignore). In C, you
can express the same thing by writing:

typedef struct blah blah;
struct blah { ... };

and, if you are going to use typedefs at all for struct-names, I
recommend that you do it this way so that you can use the new name
inside the {} pair:

typedef struct list_of_foo list_of_foo;
struct list_of_foo {
list_of_foo *next;
...
};

(I prefer not to use typedef to hide C's "struct" keyword at all,
in general -- but this is purely a style issue. See below.)

"Dev" has type "struct devData" (a.k.a. "t_dev"). It is therefore
a struct type. In C++ this is not as crucial, but in C, it is
often necessary to know that something that has a "struct" type
does indeed have such a type -- for instance, only such things can
have "." member-extractor operators applied. If you have to *know*
it is a struct in order to use it, you might as well use the "struct"
keyword to declare it.

The %x format in printf requires a value of type "unsigned int"
(and possibly also plain and explicitly-signed "int", depending on
whether one interprets a footnote in the C standards as clarifying
words that seem not to be there after all). In any case, Dev has
a struct type, so there is no way this can be correct. The effect
of this call is undefined. (In practice, it does different and
arbitrarily weird things on different implementations.)
printf("===FIRST PRINT BEGIN ===========\n");
for (i = 0; i < 24; i++)
printf(" addr : 0x%x \n", Dev.tConf);


Looking a bit more closely at the type of "Dev", we find that it
is a struct with a single member named "tConf". That member has
array type -- "array 2 of <element-type-1>", in fact -- where the
first element type is in turn another array type. The actual
element type does not matter yet; what matters is that the array
has size 2. That means the only valid subscripts are 0 and 1, yet
"i" will run from 0 through 23 inclusive. Fully twenty-two of the
subscripts will be out of range, hence produce undefined behavior.

Although we are clearly already in trouble, we can take a look at
what the first two calls do, by examining the element-type of
the array in Dev.tConf. The size is 2 -- tConf is an "array 2
of T" for some type T -- and the element-type is "array 12 of
<element-type-2>". In other words, tConf is an array of arrays.

This is how C handles "two-dimensional" arrays, as one-dimensional
arrays whose elements are in turn one-dimensional arrays. Indeed,
one can say (of both C and C++) that the way it handles any arrays
of higher dimensions is that "it doesn't". This is no big deal in
C++, which provides better ways to deal with such things in the
first place, but it is often crucial in C programming.

Just for completeness, <element-type-2> here is "struct T_USER_CONF"
(aka "t_conf"), which is a structure type containing two "int"s
named "x" and "y". So Dev.tConf has type "array 2 of array 12 of
struct T_USER_CONF". Each of these "array N of T" constructs can
fall under what I call "The Rule" about arrays and pointers in C.
The Rule, with capital letters and all, is *the* key to understanding
arrays and pointers, and the odd way in which pointers can simulate
arrays and vice versa. (C++ inherits The Rule from C, but --
because C++ is such a wildly different language, despite the
superficial syntactic similarity -- in C++ it probably does not
need this sort of capitalization. One simply uses the STL, and
mostly ignores the existence of arrays, with their peculiar C-derived
semantics, entirely.)

Here is The Rule about arrays and pointers in C. Note that it
requires careful distinction between "objects" and "values", and
needs one to think about whether something is in "object context"
or "value context".

In a value context, an object of type "array N of T" (for some
suitable integer N and valid element-type T) becomes a value
of type "pointer to T", pointing to the first element of that
array, i.e., the one with subscript 0.

Object and value contexts are actually quite straightforward, as
concepts go. When you write something like:

int i, j;
...
i = j;

how does the compiler know to take the *value* of j and put it into
the *object* named i? The answer is: from context. Here "i" is
on the left side of a simple assignment operator, and "j" is on
the right side, so we mean "the object i" and "the value of the
object j". In other words, "i" has "object context" and "j" has
"value context".

When you place an array object in a value context, the "value" that
a C or C++ compiler produces is the one prescribed by The Rule.
According to The Rule, you drop the integer "N" part and get a
value of type "pointer to T", where T is the element-type of
the array. Hence in:

int *p, a[50];
...
p = a;

one applies The Rule to "array 50 of int" and finds that p is set
to a value of type "pointer to int", pointing to the first element
of the array "a", i.e., &a[0].

Again, note that C (and thus also C++) does not have true
multi-dimensional arrays, but rather arrays whose element type is
in turn another array. Suppose we have an array named "b", of size
5, whose element-type is "array M of T2" (for some suitable integer
M and type T2):

T2 b[5][M];

Now, if we place b in some value context, what value do we get,
according to The Rule?

In a value context,

(yep)

an object of type "array N of T"

(ok; N is 5 and T is "array M of T2")

becomes a value of type "pointer to T" ...

-- which is the answer: the value has type "pointer to T", or
"pointer to array M of T2", and points to the first element of
b, i.e., &b[0]. To write the type "pointer to array M of T2"
in C, we must use the rather peculiar-looking declaration:

T2 (*p)[M];

and we can then write:

p = b;

to set p to &b[0]. This pointer value points to an entire array
-- in this case, the first of N such arrays. This is hard to draw
in text; for a graphical representation of the concept, see
<http://67.40.109.61/torek/c/pa.html>. This pointer is much like
the red-arrow-and-circle in the diagram there.

Why does all this matter? Well, remember that Dev, in the original
code we started with, has type "struct devData", which is a structure
type with one member, "tConf". The member has type "array 2 of
array 12 of struct T_USER_CONF". We thus have one of these "array
N of array M of ..." constructs, which will, under The Rule, produce
values of type "pointer to array M of" whatever. And indeed, this
is precisely what happens next:

printf (" addr : %p\n", (void*) Dev.tConf + i);

Since Dev.tConf is an "array 2 of array 12 of struct T_USER_CONF",
taking its "value" produces a value of type "pointer to array 12
of struct T_USER_CONF". This pointer points to an entire "array
12 of struct T_USER_CONF" -- the first one in Dev.tConf, i.e.,
Dev.tConf[0] -- but it is a pointer value and can be converted
to "void *".

Here things go wrong, though. The expression:

(void *) Dev.tConf + i

parses the same as:

((void *) Dev.tConf) + i

because casts bind more tightly than addition. The result is that
we first convert Dev.tConf's value (as produced by The Rule) to
one of type "void *", then attempt to add an integer value to that.
C and C++ both forbid arithmetic on "void *", so a diagnostic is
required. (Incidentally, note that gcc by default compiles a
different language from C, in which "void *" arithmetic *is* allowed,
and you have to supply special compiler flags to get it to complain
as the C Standards require.)

Clearly E.D. meant:

(void *)(Dev.tConf + i)

which is considerably closer to correct, and even compiles. But
this still suffers from undefined behavior. As you may or may not
recall by now :) the tConf member has type "array 2 of array 12
of ...", but the variable "i" is going to run from 0 through 23
inclusive. The only valid subscripts for tConf are 0 and 1, and
a pointer of type "pointer to array 12 of ..." steps forward by
units of "array-12-of" (as illustrated at the above URL), so when
you add 2, you point just past the end of the object. C does allow
such a pointer as a special concession, but then i goes on to become
3, and C does not define the result of this computation. A very
few (perhaps even "no", today) C compilers will catch the error at
runtime, to prevent you from getting the wrong answer fast. (Most
C compilers will just give you the wrong answer fast though. For
some reason, most people seem to prefer to get wrong answers as
fast as possible, rather than having a slightly slower system catch
such errors.)

If one were to change the loop to read:

for (i = 0; i < 2; i++)
printf(" addr : %p\b", (void *)(Dev.tConf + i));

then this final version would be correct and would do something
reasonably well defined.
printf("===FIRST PRINT END ===========\n");

printf("===SECOND PRINT BEGIN ===========\n");
for (i = 0; i < 2; i++)
for (j = 0; j < 12; j++)
printf(" addr : 0x%x \n", Dev.tConf[j]);


Here the subscripts are valid -- the tConf member (still) has type
"array 2 of array 12 of struct T_USER_DATA", and "i" and "j" are
always in range -- but once again, this passes an entire "struct"
value to printf() when printf() is expecting an unsigned int, so
the effect is undefined. E.D.'s correction again suffers from
misplaced (or missing) parentheses, so that the "void *" binds
to the wrong sub-expression:
printf (" addr : %p\n", (void*) Dev.tConf + j);


but this is easily fixed:

printf(" addr : %p\n", (void *)(Dev.tConf + j));

As always in C, array objects in "value contexts" fall under The
Rule, and since Dev.tConf is an array object, and the "+"
(addition) operator needs values, we must apply The Rule. Since
Dev.tConf has type "array 12 of struct T_USER_DATA", the "value"
of this is a pointer of -- what type? As always, we must discard
the integer N (12) and keep the type T (struct T_USER_DATA). We
get a pointer of type "pointer to struct T_USER_DATA", pointing
to the first element of the array, i.e., &Dev.tConf[0].

Note that we could equally write:

printf(" addr : %p\n", (void *)(&Dev.tConf[0] + j));

Moreover, because pointer addition moves forward by "whole
object" units (as determined by the type of the thing to which
the pointer points), we could also write this as:

printf(" addr : %p\n", (void *)(&Dev.tConf[j]));

and now the final parentheses are redundant (casts bind less
tightly than & and [] operators), so this is just:

printf(" addr : %p\n", (void *)&Dev.tConf[j]);

The cast *is* required in all cases, though, because C's %p format
directive needs a value of type "void *". In C++, there are no
automatic, cast-free conversions to and from "void *", and even in
C, this particular parameter matches up with the ", ..." part of
printf()'s prototype, so the cast is still needed.
 
J

Jack Klein

Guys:

I am sorry to ask this question. I am trying to print the addresses of
the below two printf's, both are not printing the same, what was the
problem, can somebody correct me. Thanks in advance.
===================

Do not multi-post. I already answered this question in
alt.comp.lang.learn.c-c++.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++ ftp://snurse-l.org/pub/acllc-c++/faq
 

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,054
Latest member
TrimKetoBoost

Latest Threads

Top