P 155 k&r section 7.3

M

mdh

mdh said:




They are saying different things, but both are correct....



What Keith is telling you .... is how the C compiler
understands the (char *fmt, ...) stuff. It sees char *, and thinks "Aha, a
pointer to char", sees fmt, and thinks "okay, I know its name now", sees
..., and thinks "whoa, variadic function, okay, no problem, I remember how
to do those"). It hasn't a clue what you're trying to do...


Well, in that respect, the compiler and I share something in
common!!!! :) Seriously, you have very accurately articulated what I
have ignored up to now.

What Barry is telling you is *why* bwk chose to use a char * for the first
argument - that is, he's explaining the purpose of fmt within the context
of this particular example function.

Yep..got it.
So I assume the initialization not only

No. You hand va_start the name of the last named parameter, the one that
occurs just before the ... bit, and it uses that information to get ready
to find (at RUNtime) the first unnamed argument value that has been passed
to this particular function.

Ok...more clarity...thank you.
When you call va_arg(), you are telling the compiler that (a) you know
there's another argument to collect, and (b) you know its type.
snip


. Keith is referring
to fmt itself, which is of type char *, ...... The types of
subsequent parameters are unknown, so you have to work them out yourself.



Forget it. That isn't completeness. That's complete nonsense.


Richard, included trying to respond to the request "Now, show us your
code (the one with the first parameter declared as an int) so we can
explain what you are doing wrong, even if it is only misinterpreting
what you see in the debugger"

Richard and Keith...thanks so much for sticking with me. You have now,
hopefully, (until the next section at least) brought me one step
further away from being "undefined"! :)
 
M

mdh

I'd leave it out too.  A function that took a variable number of
arguments that all had to be of the same type would still be variadic
and one that always took the same number would not be variadic even if
they could be of different types in different calls (that would be
polymorphic).  In other words, the types of the arguments have nothing
to do with the function being (or not being) variadic.

Of course in C a variadic function can take arguments of any type as
well as of any number (provided then required arguments are given).

Thanks Ben.
 
B

Barry Schwarz

#include <stdio.h>
#include <stdarg.h>

int main (int argc, const char * argv[]) {


/*void minprintf(char *fmt, ...);*/
void minprintf(int fmt, ...);

minprintf("This is a test:\n%d:\n%s:\n%f:\n", 27, "More test",
-90.786);

/*warning: passing argument 1 of minprintf of incompatible pointer
type ( int fmt) */

And for some reason you think this can be ignored with impunity?
return 0;
}

void minprintf(int fmt, ...)
/*void minprintf(char *fmt, ...)*/{

unrelated commentary snipped
va_list ap;
ditto

int ival;
double dval;
char *p, *sval;


va_start(ap, fmt);

/* So, here is the source of my confusion, and clearly the lack of
explaining it to the clc. I think this has been answered, so please
bear with me, but ap is now initialized to point to the first
unnamed argument. As Keith said "If the first argument were an int

No, it points to the argument named format which should be the last
named argument.
rather than a char*, then minprintf wouldn't have any way of knowing
what argument values to grab." So I assume the initialization not only
points ap to the first argument, but it "types" the pointer as well,
and this information is conveyed by the expression "fmt". And,
rhetorically, I know the first argument is of type character, because,
""The *only* thing the "char *fmt, ..." part of the declaration tells
the compiler is that the first argument is of type char*" ( Keith).

For completeness,
debugger output (value of fmt) with declaration 'int fmt' =
1416128883 ( what I see in debugger as I step through the code)

Since you have already invoked undefined behavior, the debugger output
is irrelevant.
debugger output (value of fmt) with declaration char *fmt = "This is
a test:\n%d:\n%s:\n%f:\n" ( as above)
debugger output (value of *ap) ( int fmt) = 0 '\0'
debugger output (value of *ap) ( char *fmt) = 0 '\0'

On my system, va_list is conditionally defined as either a typedef for
a struct or a typedef for a char*. If it is the same on yours, the
attempting to display *ap in the debugger may not be meaningful.
*/


for ( p=fmt; *p; p++){

Didn't your compiler generate the mandatory diagnostic here also? Why
did you ignore it?
/*So, the first argument, in my case "This is a test:\n%d:\n%s:\n%f:
\n" is now assigned to the char *p */

if ( *p != '%'){
putchar(*p);
continue;
}
switch (*++p) {
case 'd':
ival=va_arg(ap, int);

/* this part I get, I hope :). Each call of va_arg returns 1 argument

Since va_arg is not a function, you are not calling anything. Look up
your varargs.h and see the actual code that will be executed.
 
M

mdh

And for some reason you think this can be ignored with impunity?


Barry...come on! Of course I don't think this can be ignored with
impunity. I think I can explain your incredulousness. You are
**rightly** concerned with writing correct code. But you **also**
understand **why** it is correct. Now imagine if I just corrected
something without **understanding** why it is correct. The code would
be correct, but I would be back next week making the same
error...well, that's the way I work. I obviously asked in such a way
that it was not obvious what I was trying to understand...but
eventually, thanks to you and others who stuck with me...I understand
the concept. In **trying** to understand the concept, I often find it
helpful to understand **why** something is wrong. Does that make
sense?



Let me just say that despite what you may think, I pay really close
attention to what you, and the others have to say. My only error is
perhaps to be persistent until I **get** it. If this comes across as
ignoring the obvious, or not giving heed to what you say, that is not
my intention.
 
B

Barry Schwarz

Barry...come on! Of course I don't think this can be ignored with
impunity. I think I can explain your incredulousness. You are

But you executed code that did not compile correctly. Then you asked
why it appeared to work anyway and why your debugger showed something
you didn't expect. In this situation, the only way to get a
reasonable answer to those questions (ignoring whether the questions
are worth asking) is in a group that discusses your system. We can
tell you the behavior is undefined but we cannot tell you why your
debugger showed something (anything?) or why the code seemed to do
what you expected.
**rightly** concerned with writing correct code. But you **also**
understand **why** it is correct. Now imagine if I just corrected
something without **understanding** why it is correct. The code would

You mean that at this stage you still don't understand that the
arguments you pass to a function must match the parameters the
function expects? Ignore the why. Treat it as a language
restriction, no different than defining variables before using them or
only coding executable statements inside a function.
be correct, but I would be back next week making the same
error...well, that's the way I work. I obviously asked in such a way
that it was not obvious what I was trying to understand...but
eventually, thanks to you and others who stuck with me...I understand
the concept. In **trying** to understand the concept, I often find it
helpful to understand **why** something is wrong. Does that make
sense?

There are way too many wrong things to worry about **why** each is
wrong. You cannot add two pointers, you cannot multiply a pointer by
an integer, you cannot convert a pointer to a different type without
taking alignment into consideration, you can pass NULL to free but not
to getc, you can initialize an automatic variable with the result of a
function call but not a static variable, and on and on.
 
R

Richard

Barry Schwarz said:
But you executed code that did not compile correctly. Then you asked
why it appeared to work anyway and why your debugger showed something
you didn't expect. In this situation, the only way to get a
reasonable answer to those questions (ignoring whether the questions
are worth asking) is in a group that discusses your system. We can
tell you the behavior is undefined but we cannot tell you why your
debugger showed something (anything?) or why the code seemed to do
what you expected.

Nonsense. Someone will be able to. Possibly there are more people here
using his platform because they are interested in C than populate the
"for that platform group".

So less of the "we".

The reason was, in fact, explained to him in this very thread.

And such practical examples, when contrasted with the mythical systems
we keep hearing of here, make great strides in convincing people WHY
"standard C" is better. It's just a pity too many of you run off and
hide behind the "we" and "off topic" rather than compare and contrast
REAL systems in the Standard C arena. Now *that* would be helpful to
people. Real people. With real compilers and platforms. And real
deadlines. And real issues.
 
M

mdh

mdh said:



In this case, it's more like:

Me: "Drive on the left."
You: "Okay, I'll try driving on the right and see what happens."
Me: "Your funeral, not mine."
But you don't actually need to know that to write C programs, and in fact
it's better (from a portability perspective) if you *don't* know, or if
you can at least pretend you don't know - because it might work
differently on a different machine.


Richard...thanks for being so patient. I took a good look at a lot of
the discussions over the years in the clc...as I had plenty of time
last night! One thing I realized was that "undefined behavior" has
come up ad nauseum and, understandably, the gusto to explain each time
that once ub occurs...all discussion ends. So I can see how some of my
questions must frustrate sometimes, esp falling into the trap of
trying to discern why something is not working by looking at a result
after ub has occurred. Having said that, it does lead me to ask you
this.
If one sees a diagnostic ( and I specifically mean a warning) , should
I, as you newbie ( not you as the expert) assume that anything after
this is ub ( as far as I can see, a message does not actually use the
term 'ub'), and as such, my questions ought to be directed, if any to
the cause of the diagnostic, as opposed to the result. I think this is
the error I made in this line of questioning, esp when asked to show
code.
 
M

mdh

**understanding** why it is correct. The code would

You mean that at this stage you still don't understand that the
arguments you pass to a function must match the parameters the
function expects?


Barry, I appreciate all the help you have given. As to the specific
question above, I have to defend myself. I **thought** that the
arguments and parameters **did** match, as I conveniently ignored the
warning...which should be read as not quite understanding the
importance and gravity of ignoring a diagnostic. Hopefully, I will not
make that mistake again. However, if I ever do, I am sure it will be
pointed out again, with even greater gusto !! :) ( see my answer
that I have just given to Richard, before I saw yours).
 
R

Richard

mdh said:
Barry, I appreciate all the help you have given. As to the specific
question above, I have to defend myself. I **thought** that the
arguments and parameters **did** match, as I conveniently ignored the
warning...which should be read as not quite understanding the
importance and gravity of ignoring a diagnostic.

Do you ignore road signs too?
 
N

Nick Keighley

On Sep 7, 10:57 am, Richard Heathfield <[email protected]> wrote:

Richard...thanks for being so patient. I took a good look at a lot of
the discussions over the years in the clc...as I had plenty of time
last night! One thing I realized was that "undefined behavior" has
come up ad nauseum and, understandably, the gusto to explain each time
that once ub occurs...all discussion ends. So I can see how some of my
questions must frustrate sometimes, esp falling into the trap of
trying to discern why something is not working by looking at a result
after ub has occurred. Having said that, it does lead me to ask you
this.
If one sees a diagnostic ( and I specifically mean a warning) , should
I, as you newbie ( not you as the expert) assume that anything after
this is ub ( as far as I can see, a message does not actually use the
term 'ub'),

sadly no :-(
implementations are permitted to output diagnostics
for anything they feel the whim to do so.

For instance "unused variable" is poor style but harmless
(unless you meant to use the variable!)

Some compilers complain if you don't use the return value
of a function. Again mostly harmless.

I'm afraid you just have to learn. Parameter mismatch
is one you can't usually ignore.

As a rule of thumb strive for a "clean compile", no errors
or warnings. Sadly this isn't always possible.

Try to understand what the compiler is trying to say and
then decide if you can live with it.
 
J

jameskuyper

mdh wrote:
....
If one sees a diagnostic ( and I specifically mean a warning) , should
I, as you newbie ( not you as the expert) assume that anything after
this is ub ( as far as I can see, a message does not actually use the
term 'ub'), and as such, my questions ought to be directed, if any to
the cause of the diagnostic, as opposed to the result. I think this is
the error I made in this line of questioning, esp when asked to show
code.

It's a good rule of thumb that you should set your compiler to it's
most sensitive warning level, and write your code so that it doesn't
generate any warnings. Like most rules of thumb, you need to be
careful about applying this; some compilers will warn about a great
many things that they don't need to warn about - you should either
turn off those warnings, or ignore them. More importantly, unless you
understand why a warning message was issued, you can't be sure of the
correct way to fix it; some ways of turning off warnings don't do
anything about the actual problem; some ways even make it worse.

Unfortunately, as a newbie you have no easy way to distinguish between
legitimate warnings and unnecessary ones, between the right way to fix
something and the wrong way to fix it.. The hard way, which i strongly
recommend, is that you should always figure out why the warning is
being produced, before you make any decision about how to fix the
problem it's warning you about.
 
M

mdh

mdh wrote:

...



Unfortunately, as a newbie you have no easy way to distinguish between
legitimate warnings and unnecessary ones,...... The hard way, which i strongly
recommend, is that you should always figure out why the warning is
being produced, before you make any decision about how to fix the
problem it's warning you about.


Well, Nick and James...that is the **strong** take home message from
this discussion for me. I think it will at the very least, serve to
focus the inquiry if I do ask the clc. Thank you both.
 
J

jameskuyper

mdh said:
Barry, I appreciate all the help you have given. As to the specific
question above, I have to defend myself. I **thought** that the
arguments and parameters **did** match

I don't want you to feel as though we're harrassing you, but that's
the key part I've never understood about this thread, and it seems
that most of the other people are feeling similarly frustrated about
it. How did you reach the incorrect conclusion that they did match?
People make mistakes all the time, I'm certainly not an exception, but
I don't understand the nature of this particular mistake.

The first time we saw your actual code was in your message with
Date: Sat, 6 Sep 2008 18:16:07 -0700 (PDT)
In that message, your very own code showed a correct prototype for
minprintf() commented out, in which the first parameter was declared
as 'char *'. The very next line shows an incorrect prototype, not
commented out, which declared the first parameter as an 'int'. This
was followed by an actual call to the function with a first argument
of type char*. Finally, you include a comment that indicates that the
compiler gave you a warning indicating that the function call involved
an argument of a type that was incompatible with the type of the
corresponding parameter in your function prototype.

How could you modify the prototype without being aware of the fact
that your change would break your code? How could you see a compiler's
warning message that explained what the problem was, add a comment to
your code about that message, and yet remain unaware of the problem?
The only way this makes sense to me is if you thought that 'int' was
a synonym for 'char*', and I can't figure out what might have given
you that idea.
 
M

mdh

I don't want you to feel as though we're harrassing you,


No...I have followed other threads and I do **not** feel harrassed.
I am at times equally frustrated at not being able to clearly
articulate my questions well.




but that's
the key part I've never understood about this thread,


An honest question and I will attempt to answer it.

May I start with an analogy ( true, by the way). In 1992 12 of us
sailed from St Augustine in Florida to Lisbon. One of the crew was
Michael Smith. A year later, in 1993 he would get the Noble Prize in
Chemistry for his work in "oligonucleotide-based, site-directed
mutagenesis and its development for protein studies". As a graduate
he wanted to understand the configurations of protein. As we sat
around the breakfast table in the middle of the ocean, he would
explain how it worked. This is the analogy he ( or his s.o..can't
remember which) gave. Imagine that you have an engine. You have no
manual, but you develop a tool to open the engine, take something out,
and put it back together again. Then you fire up the engine and see
what happens. ( Instead of looking at engines, he was looking at the
way proteins folded). If you do this enough times, taking out
different bits each time, eventually you might be able to draw a
conclusion as to what the piece you removed did. That is often the way
I have approached things in my life....maybe not driving around blind
corners, but certainly in thought experiments etc.
Now...through the course of this thread, I realize that this cannot be
applied directly to C, because of one **small** issue, calle undefined
behavior :) Whereas one might get a **predictably** **different**
response by pulling out bits and pieces from DNA or an engine,
deliberately doing so in C is different.

So, at the beginning of the thread, this is what I thought.

So, let me preface this by saying

****What I am about to write is wrong thinking***

If I do not understand something, (in C) by **deliberately** creating
an error, I can try and figure out, by looking at the result , how the
**original** expression/function/whatever works

****End of wrong thinking*****


*****Correct thinking*****

If one sees a diagnostic, it **could** mean ub, but in any case, **GO
NO FURTHER** until it is understood. The end result **should** be the
same ie in terms of understanding the issue, but it sure as heck won't
frustrate the clc the way this thread has done!!!!! :)

**** End of hopefully correct thinking****

it. How did you reach the incorrect conclusion that they did match?
People make mistakes all the time, I'm certainly not an exception, but
I don't understand the nature of this particular mistake.


I hope the above explanation sheds some light on it. ( and see below)

The first time we saw your actual code was in your message with
Date: Sat, 6 Sep 2008 18:16:07 -0700 (PDT)
In that message, your very own code showed a correct prototype for
minprintf() commented out, in which the first parameter was declared
as 'char *'. The very next line shows an incorrect prototype,


James, originally I was going to just post the above and not answer
the specifics...but clearly you have taken the trouble to look back
through the thread, so the very least I can do is go back and show
what I was trying to ask, the relevant replies and hopefully clarify
this for you.
How could you modify the prototype without being aware of the fact
that your change would break your code? How could you see a compiler's
warning message that explained what the problem was, add a comment to
your code about that message, and yet remain unaware of the problem?
The only way this makes sense to me is if you thought that 'int' was
a synonym for 'char*', and I can't figure out what might have given
you that idea.

So, I hope this will answer some of your queries.



It all started off with a question about Macros.

Sept 5, 4:15pm (mdh) ( all times PDT)The section is titled Variable-length Argument lists.
May I ask a question about the use of "macros".
Could anyone help me understand the significance of using macros vs
functions, as I think it is more significant than I realize.
<<<<<




5:20pm (KT) (Amongst others)The reason va_start, va_arg, va_end, and va_copy are defined as
macros
is that they *can't* be defined as functions.
For example, va_arg takes two arguments, an expression of type
va_list
(I'm actually not sure it can be an arbitrary expression) and a type
name.
<<<<<<<



Then I asked about clarification of the expression va_list ap.

6:43 pm (mdh)This declares ap a pointer of type va_list? And the reason ap is a
pointer is simply because that is the way it is defined??
<<<<<




and again KT answered at 7:21pmThe standard merely says that it's "an object type suitable for
holding information needed
by the macros va_start, va_arg, va_end, and va_copy"
<<<<<<<<


There was some discussion as to why I thought what I thought, but that
was sorted out. The one thing that I could not put into context was
what the role of the "char *fmt" in the decalration "(char *fmt, ...)
was. I knew it was obviously important, in va_start.

Also at 6:43pm (mdh)
va_start(ap, fmt);
So, from what you say, if I understand this, the type here (fmt) is
"pointer to char" and the expression 'ap' is initialized or "returned"
as a pointer to char (to the first argument)?
<<<<<<<<<


And again, KT answered in the same reply at 7:21pm

va_start doesn't return anything. It initializes ap, an object of
type va_list. (That's part of the reason it's a macro; a function
can't modify an argument.) The va_list object exists to allow access
to the variadic arguments; you can think of it as a kind of abstract
index into the argument list.
The second argument to va_start is the name of the parameter just
before the ", ...".

<<<<<<<<<<<<<



After getting some sleep, I still could not understand the exact role
of "char *fmt", so the next day posted this...and this is where it all
started going downhill.

Sept 6, 11:46 am (mdh)

May I pursue another issue a little further.

void minprintf(char *fmt, ...);
or
void minprintf(int fmt, ...);

The only difference is ( maybe the **only** will raise some chuckles)
that char *fmt points to all the arguments in my debugger, but int
returns an integer( surprise, surprise!). For fear of flogging a dead
horse, is that the difference?

<<<<<<<<<<



Clearly...in hindsight, this was ***WRONG THINKING*** !!!!!...but
that is where this came from. In addition, the use of the term
debugger muddied the water even further.

I think the clc gave me a little to much credit in thinking I
understood the gravity and significance of ub. At this point, a bunch
of different questions were posed, and I tried to be diligent in
answering them as best I could, including a plea for actual code.

BS at 12:09 pm
Show your code for a minprintf with an int as its
first parameter and a sample calling statement.

<<<<<<<<<




HvD at 1:10pm

Then, please post a minimal complete program that includes the
declaration
void minprintf(int fmt, ...);, defines that function exactly the way you
have defined it now, and calls it.

<<<<<<<<<<<


Again, I tried to hone in on the one thing that was not making sense
to me.

mdh at 12:30pm
Are you saying that the role of the declaration "char *fmt" is to
tell the compiler that each argument needs to be extracted as a char?
<<<<<<<<<<<





RH ( who has been one of the stalwarts, in helping me navigate C)
quickly cut to the chase

RH at 1.23pm

f I change the declaration /and/ the definition, I get a different
diagnostic message:
foo.c:36: warning: passing arg 1 of `minprintf' makes integer from
pointer
without a cast
Can you convince me that these diagnostic messages have no
significance?

<<<<<<<<<<<<

Again...**WRONG THINKING** on my part.


KT answered ( my 12:30pm query) at 4.02pm

The *only* thing the "char *fmt, ..." part of the declaration tells
the compiler is that the first argument is of type char*, and there
will be zero or more arguments of unspecified type(s) following that.


........





For example, if a call
looks like this:
minprintf("%s = %d\n", "foobar", 42);
then minprintf itself needs to make the following calls to fetch the
parameter values:
va_list ap;
va_start(ap, fmt);
va_arg(ap, char*);
va_arg(ap, int);
va_end(ap);

<<<<<<<<<<


That should have been the end of it, but there now seemed to be a
concurrent thread that had honed in on the **WRONG THINKING** part of
my replies ie trying to explain how diagnostics had been ignored, and
meaning given to ub.

At 6:03pm ( mdh) I posted the code I think you are referring to


void minprintf(int fmt, ...)
/*void minprintf(char *fmt, ...)*/{

.....etc etc

<<<<<<<<<<

So James, I hope you understand now where that craziness came from. A
combination of **WRONG THINKING** and trying to diligently respond to
people trying to help me.

At some point, RH said.

RH at 6:50pm
/* So, here is the source of my confusion,

You hand va_start the name of the last named parameter,
When you call va_arg(), you are telling the compiler that (a) you
know
there's another argument to collect, and (b) you know its type.

and

Keith is referring
to fmt itself, which is of type char *, not type char.

For completeness,
debugger output (value of fmt) with declaration 'int fmt' =

Forget it. That isn't completeness. That's complete nonsense.

<<<<<<<<<<<<<<<<


??*** :) I won't make the "for completeness" mistake again!!!!




Hopefully, I have not lost BS forever!
BS at 11:20 am ( today)

But you executed code that did not compile correctly. Then you asked
why it appeared to work anyway and why your debugger showed something
you didn't expect.

<<<<<<<<<

I hope my earlier explanation covers this.


So James, I hope this does justice to all the time you have taken to
help. As I look through this thread, it's confusing to me too, at
times!!. Perhaps, it would have been more appropriate to start another
thread once the issues of the macros had been settled, as it **might**
have made it cleaner.


So, to all of the people who answered this thread, I am sorry if this
lead to frustration. One of the great joys is being part of the clc
and interacting with such amazing programmers. You have no idea
( well, maybe you do) how stimulating this is.
So thank you and ....
This is a learning curve to me too.
 
M

mdh

mdh said:
<snip>

As a graduate, he would have been about 20 years old - about 1952 or so -
and that would have been about the only way to work stuff out about
protein-folding, and it would have taken a LONG time. Nowadays, people can
build on the knowledge he and others gained,


Richard...thank you. I am agreeing with you. The reason I wrote that
long response was that James, who like you has always been responsive
to my questions, had taken the trouble to go back and try and figure
out what I had been mis-understanding. The very least I could do was
try and give him an answer showing the evolution of my **new**
understanding.



(What I had not quite connected was the **gravity** and
**significance** of diagnostics and **potential** ub. I was not trying
to ignore the "manual", but merely trying to understand it. :)
Richard...just think how boring it would be if everyone thought
exactly the same way!!!! :) :)
 
C

CBFalconer

Richard said:
.... snip ...

C is a known language designed by human beings, not a natural
phenomenon into which we need to do research. The rules are
clearly understood by a great many people (and less clearly
understood by a great many more, largely because they use
something similar to the technique you suggest of ignoring the
manual and the experts in favour of random experimentation).

Not to mention the erroneious information they 'learn' because of
the use of an erroneous compilation system.
 

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,773
Messages
2,569,594
Members
45,125
Latest member
VinayKumar Nevatia_
Top