C Programming Language 2nd Ed, Exercise 1.9 and 1.12, Solution suggestion.

  • Thread starter Christos Kokaliaris
  • Start date
J

Joe Pfeiffer

Cal Dershowitz said:
This is where it stops for me, and I agree.
The () form in a declaration says that the function takes a fixed but
unspecified number and type(s) of arguments. In a definition, it says
that the function has no parameters, but it still provides a declaration
with the above meaning.

The () form is obsolescent, both for declarations and for definitions,
and has been since 1989.

The (void) form specifies that the function has no parameters.

This program:

int main() {
main(42);
}

Wouldn't the new main interpret 42 as argv[0] in this instance?

What "new" main? The only main() in the program is the one right there,
which doesn't have an arrgv parameter.
Am I right that calling main within main is universally shunned?

Pretty close (if I were to say "yes", I'm sure somebody would come up
with some obscure example where it might be marginally useful).
 
K

Keith Thompson

glen herrmannsfeldt said:
As well as I know it, ANSI C allows a different calling convention
for functions that never have variable argument lists, maybe
even STDCALL, but no-one does that. Much better to have the caller
pop the arguments.

(Your use of the word "never" could imply that some functions might
*sometimes* have variable argument lists. A given function either
does or does not have a variable argument list, depending on whether
it's defined with ", ...".)

Yes, the standard permits variadic and non-variadic functions to
have different calling conventions. It doesn't discuss calling
conventions, but it expresses this by saying that a call to a
variadic function without a visible and correct prototype has
undefined behavior. And STDCALL is not mentioned in the standard.

The ", ..." syntax for variadic functions was introduced by the
1989 ANSI standard, and many calling conventions predate that, so
most calling conventions happen to have the property that calling
a variadic function with no visible prototype will happen to work.
A newly defined calling convention could easily break that, but
designers will likely try to avoid breaking existing code -- even
if that code's behavior is undefined
 
K

Keith Thompson

glen herrmannsfeldt said:
(snip, I wrote)



But not yet removed from the standard?

Correct, but notice is given that it could be removed from a future
version of the standard.

[...]
I have seen them, but never found a use for it.

Are you referring to recursive calls to main?

The argument is more general than that. If you use empty parentheses
for parameterless functions other than main, then the issue of recursive
calls to main doesn't matter.

Using () rather than (void) on a *declaration* means that the compiler
needn't (and typically won't) diagnose calls with one or more arguments.
Using () on a *definition* is mostly harmless -- unless calls rely on
the declaration provided by the definition. I suppose you could
consistently use () on definitions on (void) on declarations (while
being very careful to *always* provide a separate declaration), but it's
a lot easier to be consistent.
A few less characters to type. Well, I started C in the K&R days,
though maybe not by much. About 1985 or so.

I consider that to be no advantage at all. In 1985, the void keyword
didn't exist. It's time to start using it.
I suspect my Java programs look more C-like than the other way around.

If you haven't looked at Java lately, look at System.out.format().

Why?
 
S

Stefan Ram

What new main, and what argv[0]? There is no new main; the old main()
simply calls itself. And, clearly, that main does not even consider
anything called "argv[0]".

The word you two are looking for is »incarnation (in computer science)«.
While there is one definition of each function, each call of a function
creates a /new/ so called »incarnation« of the function.
Shunned is slightly too strong. _Slightly_. You'd have to have a very

It's alright in C, but explicitly forbidden in C++. (One more reason
not to claim that any valid C program is a valid C++ program.)
 
K

Keith Thompson

Cal Dershowitz said:
On 6/9/2014 3:14 PM, Keith Thompson wrote: [...]
This program:

int main() {
main(42);
}

Wouldn't the new main interpret 42 as argv[0] in this instance?

As far as the C standard is concerned, the behavior is undefined.

Depending on the calling convention, main(42) *might* do something
equivalent to setting argc (probably not argv[0]) to 42, and probably
putting garbage into the argv pointer -- but since main's definition
doesn't declare argc, it can't refer to it anyway. There is no
argc or argv in this case.

[...]
 
P

Phil Carmody

It goes down fairly well in the IOCCC.
Shunned is slightly too strong. _Slightly_. You'd have to have a very
good reason to use it. It's not _as_ bad as gets(), but certainly worse
than goto. goto can be put to good use in, e.g., a state machine, and
only Lisps advocates and Pascal users would complain,

Lisp had GOTO before C's parents had even met each other.

Phil
 
S

Stefan Ram

Newsgroups: comp.lang.c,comp.lang.lisp
Followup-To: comp.lang.lisp

Phil Carmody said:
Lisp had GOTO before C's parents had even met each other.

Maybe you think of the GO function of LISP 1.5?

It is apparently quite limited:

It can only appear on the top level of a prog or
within a conditional expression on the top level.

It can only jump to a location symbol of this prog,
not another one on a higher or lower level.

Newsgroups: comp.lang.c,comp.lang.lisp
Followup-To: comp.lang.lisp
 
S

Seungbeom Kim

The () form in a declaration says that the function takes a fixed but
unspecified number and type(s) of arguments. In a definition, it says
that the function has no parameters, but it still provides a declaration
with the above meaning.

Can you explain what you mean by the second sentence above?
If it says that the function has no parameters, doesn't it constitute a
declaration that says the same thing? If there exists something that says
a function has no parameters but that is not a declaration, what is it?

The only relevant part I could find in the standard was N1570:6.7.6.3/14:
"An empty list in a function declarator that is part of a definition
of that function specifies that the function has no parameters", but
it doesn't seem to imply that it still provides a declaration that
allows anything but no parameters.
 
T

Tim Rentsch

Seungbeom Kim said:
The () form in a declaration says that the function takes a fixed
but unspecified number and type(s) of arguments. In a definition,
it says that the function has no parameters, but it still provides
a declaration with the above meaning.

Can you explain what you mean by the second sentence above? If it
says that the function has no parameters, doesn't it constitute a
declaration that says the same thing? If there exists something
that says a function has no parameters but that is not a
declaration, what is it? [snip citation 6.7.6.3 p14]

A declaration like this

int f();

declares a function whose type holds no information about either
the number or types of any parameters (fine point: except that
we know the function cannot be variadic, since declarations of
variadic functions always need a prototype).

A function definition like this

int f(){ ... }

defines a function that /in fact/ takes no parameters, but whose
declarator still specifies a type that holds no information about
either the number of types of any parameters.

It may seem strange that even though the function definition
"knows" that the function takes no parameters, it still is the
case that what is declared is a function /whose type/ holds no
information about either the number or types of any parameters,
as clearly the information is available. But that is in fact
what the C standard prescribes.
 
T

Tim Rentsch

Keith Thompson said:
Correct, but notice is given that it could be removed from a future
version of the standard.

I think it's worth pointing out that this language feature (along
with several others) is listed as obsolescent but not currently
listed as deprecated. Being listed as obsolescent means a feature
may be considered for withdrawal in a future revision, but IIUC
normally first goes through a revision cycle (or possibly more
than one, that may depend) where it is not yet removed but just
marked deprecated. In practical terms none of the half dozen
or so language features listed as obsolescent is going to go
away any time soon, and pretty likely not ever.
 
K

Keith Thompson

Seungbeom Kim said:
Can you explain what you mean by the second sentence above?
If it says that the function has no parameters, doesn't it constitute a
declaration that says the same thing? If there exists something that says
a function has no parameters but that is not a declaration, what is it?

The only relevant part I could find in the standard was N1570:6.7.6.3/14:
"An empty list in a function declarator that is part of a definition
of that function specifies that the function has no parameters", but
it doesn't seem to imply that it still provides a declaration that
allows anything but no parameters.

Strange as it seems, the empty parentheses in

void foo() { /* ... */ }

specify that foo has no parameters, but they don't specify that it takes
no arguments. (This strangeness is easily avoided by using prototypes
consistently.)

N1570 6.9.1p7:

The declarator in a function definition specifies the name of the
function being defined and the identifiers of its parameters. If the
declarator includes a parameter type list, the list also specifies
the types of all the parameters; such a declarator also serves as a
function prototype for later calls to the same function in the same
translation unit. If the declarator includes an identifier list,
the types of the parameters shall be declared in a following
declaration list.

The second sentence says that a definition that includes a parameter
type list, such as:

void foo(void) { ... }

provides a prototype, equivalent to:

void foo(void);

which means that, if that definition is visible, any call that passes
one or more arguments is a constraint violation. Without a parameter
type list, no such prototype is provided, and an incorrect call has
undefined behavior (and needn't be diagnosed).
 
K

Kaz Kylheku

Strange as it seems, the empty parentheses in

void foo() { /* ... */ }

specify that foo has no parameters, but they don't specify that it takes
no arguments.

What? No.

It defines a function entity with no parameters, which consequently takes no
arguments.

But the name foo is not *declared* as such: not to the scope within the
braces, nor the subsequent portion of the file scope.

These regions of the program are only informed about the existence of
the name foo which refers to a function.
 
R

Richard Bos

Phil Carmody said:
Lisp had GOTO before C's parents had even met each other.

Yeah, but don't tell a Lisp advocate that. Advocate as opposed to normal
user - you know the kind, claims that there hasn't been a good
programming language since Lisp (even Scheme is a bit dodgy), you can't
do anything in C that you can't do better in Lisp, you can't do tail
recursion in an assembler language like C (don't need to, he should say,
and anyway it's wrong), and anyway GOTO is dirty. Why would you accuse
Lisp of having GOTO? Lisp is the only structured programming language in
the world! It's dirty hacks like C which use GOTO!!!!!1!

Yah. That kind. Don't cross-post...

Richard
 
R

Richard Bos

(e-mail address removed) (Richard Bos) writes:

It's alright in C, but explicitly forbidden in C++. (One more reason
not to claim that any valid C program is a valid C++ program.)

Well. It's _allowed_ in C. It's not exactly popular. I wouldn't shout at
someone who finds a serious use for it as I would at someone using
gets(), but I would blink a bit.

Richard
 
K

Kaz Kylheku

Yeah, but don't tell a Lisp advocate that. Advocate as opposed to normal
user - you know the kind, claims that there hasn't been a good
programming language since Lisp (even Scheme is a bit dodgy), you can't
do anything in C that you can't do better in Lisp, you can't do tail
recursion in an assembler language like C (don't need to, he should say,
and anyway it's wrong), and anyway GOTO is dirty.

Every decent Common Lisp advocate I know (including myself) touts
TAGBODY/PROG and GO as useful features, and in fact the best representation for
certain problems, and a necessary target target language for making certain
kinds of macros as efficient as possible.

Implementations of the ANSI CL LOOP macro usually make use of GO.
Why would you accuse
Lisp of having GOTO? Lisp is the only structured programming language in
the world! It's dirty hacks like C which use GOTO!!!!!1!

Ah, that sounds more like a Scheme advocate than a Lisp advocate.
 
P

Phil Carmody

Yeah, but don't tell a Lisp advocate that. Advocate as opposed to normal
user - you know the kind, claims that there hasn't been a good
programming language since Lisp (even Scheme is a bit dodgy), you can't
do anything in C that you can't do better in Lisp, you can't do tail
recursion in an assembler language like C (don't need to, he should say,
and anyway it's wrong), and anyway GOTO is dirty. Why would you accuse
Lisp of having GOTO? Lisp is the only structured programming language in
the world! It's dirty hacks like C which use GOTO!!!!!1!

Yah. That kind. Don't cross-post...

I think a lot of the funcy language proponents are cut from
the same mould, I've certainly seen some of the above in my
time. At least one of those statements is one I have said
myself, and would again. Then again, I only learnt that lisps
had gotos a decades after I'd last programmed in the language,
having learnt it ('t', a scheme dialect) as part of my
mathematics degree (before I'd learnt C), where predictably
we focussed on the "purer" aspects of language. I had a rose
tinted view of the language family, and was shocked by what
I later learnt.

Phil
 
T

Tim Rentsch

Cal Dershowitz said:
Am I right that calling main within main is universally shunned?

The problem with calling main recursively is not that it's always
wrong but almost never right. There are cases where a recursive
call to main is (IMO) reasonable and appropriate, but these are
rare, so for most developers it's natural to view a recursive
calling of main with suspicion.
 
T

Tim Rentsch

Christos Kokaliaris said:
Recently I decided to study analytically The "C programming
Language 2nd Ed" book and solve every single exercise. After
comparing my own code with the already suggested in the
archives of clc-wiki, I thought of posting my solutions.

Criticism and suggestions on the quality of the solutions I
suggest are much needed and most welcome.

// ex1-9
#include <stdio.h>

/* copy input into output, replacing
one or more blanks by a single blank */
main()
{
int c, nb=0;

while ((c=getchar()) != EOF) {
/* if char is blank increase counter else reset counter.
this notation will be mentioned later in the book
(T) ? A : B; if TRUE then A else B */
(c == ' ') ? (++nb) : (nb = 0);
if (c != ' ' || nb < 2) putchar(c);
}
}

I recommend putting all comments before (or after) a function,
and none in the function body itself. Also, the way the comments
are indented here is rather jarring, but maybe that's an artifact
of how the posting was done.

I agree with other comments given that the setting of 'nb' is
better done with an assignment of a conditional expression.

In the loop, the logic for deciding whether to print is just a
little obscure. It would be good to clarify it.

Someone gave a comment that incrementing the counter might lead
to overflow if there were lots of consecutive spaces. This can
be addressed by having the increment value depend on the value
of the counter.

Putting all these remarks together, this might be written as

/* copy input into output, replacing one or more blanks
* by a single blank
*/

/* We use a variable 'nb' to count how many blanks were last
* seen. The counter is limited to the value 2 to prevent
* overflow. The value of 'nb' is set using a conditional
* expression, which is described later in the book. (etc)
*/

int
main(){
int c, nb = 0;

while( (c = getchar()) != EOF ){
nb = c == ' ' ? nb + (nb<2) : 0;
if( nb == 0 ) putchar( c );
if( nb == 1 ) putchar( ' ' );
}

return 0;
}
//ex1-12
#include <stdio.h>

#define IN 1
#define OUT 0

// print input one word per line
main()
{
int c, state;
// start without a word
state = OUT;

while ((c = getchar()) != EOF) {
// if the char is not blank, tab, newline
if (c != ' ' && c != '\t' && c != '\n') {
// inside a word
state = IN;
putchar(c);
}
// otherwise char is blank, tab, newline, word ended
else if (state == IN) {
state = OUT;
putchar('\n');
}
}
}

What's interesting is that this problem is almost the same as
the first problem, just with a difference of what constitutes
a "blank". Compare the solution shown below to the last one -

int
main(){
int c, nb = 2;

while( (c = getchar()) != EOF ){
nb = c == ' ' || c == '\t' || c == '\n' ? nb + (nb<2) : 0;
if( nb == 0 ) putchar( c );
if( nb == 1 ) putchar( '\n' );
}

if( nb == 0 ) putchar( '\n' );

return 0;
}

To me this way of writing the program is easier to understand
than using the 'state' variable approach.
 
R

Richard Bos

Tim Rentsch said:
I recommend putting all comments before (or after) a function,
and none in the function body itself.

I disagree with this. Comments before the function should document
_what_ the function does (and preferably what its inputs and output
are), while comments inside the function should, where necessary,
clarify _how_ it does this.
Also, the way the comments are indented here is rather jarring,
but maybe that's an artifact of how the posting was done.

That I agree with.

Richard
 
T

Tim Rentsch

I disagree with this. Comments before the function should document
_what_ the function does (and preferably what its inputs and output
are), while comments inside the function should, where necessary,
clarify _how_ it does this.

I agree that differentiating /what/ comments and /how/ comments
is a good idea. That doesn't mean they should be spatially
separated necessarily, only that the two kinds be clearly
distinguished. Putting /what/ comments first and /how/ comments
afterwards is obviously one way of doing that.

Comments (of either kind) inside function bodies, on the other
hand, is a bad idea, a bad habit acquired at an early age where
it is used in examples as a teaching aid. The problem is that
although it might be helpful in teaching how to program, it is
not a good fit for doing actual development. Psychological
experiments in dual-mode cognition (such as code and comments)
show that people prefer to work exclusively in a single mode (ie,
the code mode in this case) once they have reached a certain
level of understanding with how things work. (Unfortunately I
have lost track of where the paper is with a reference for those
results.) Putting comments inside function bodies actually slows
down developers who know what they are doing.

A hybrid scheme I have sometimes used in the past puts comments
inside function bodies only to provide markers to footnotes
given after the function body. For example, a function might
be written like this:

int
main(){
int c, n = 0;

while((c=getchar()) != EOF){
n = c == ' ' ? n+(n<2) : 0; // 1
if(n<2) putchar( c ); // 2
}

return 0;
}
/* Notes:
*
* [1] The variable 'n' holds a count of how many spaces
* have last been seen, limited to 2.
*
* [2] If 'n' is 0, the last character was a non-space and
* should be printed. If 'n' is 1, the last character
* was the first space in this sequence and should be
* printed. If 'n' is greater than 1, that indicates
* a space after the first in a sequence, which should
* not be printed.
*/

In my experience this approach is much better, on those occasions
when comments are called for, than putting comments directly
inside the body of a function.
 

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

Similar Threads


Staff online

Members online

Forum statistics

Threads
473,734
Messages
2,569,441
Members
44,832
Latest member
GlennSmall

Latest Threads

Top