Ambiguous/debatable errata

I

Ioannis Vranos

C90:


From the K&R2 errata site: http://tinyurl.com/3nbvbh

1.

"...
Also, the comparison-routine argument is not treated well. The call
shown on p 119, with an argument

(int (*)(void*,void*))(numeric? numcmp : strcmp)

is not only complicated, but only barely passes muster. Both numcmp and
strcmp take char * arguments, but this expression casts pointers to
these functions to a function pointer that takes void * arguments.

==> The standard does say that void * and char * have the same
representation, so the example will almost certainly work in practice,
and is at least defensible under the standard. There are too many
lessons in these pages".


Does the C90 standard guarantee that "void * and char * have the same
representation"?



2.

"143(§6.5): strdup is not indexed".


What does that mean?



3.

"195(§A4.1) The first few sentences might be reworded a bit to emphasize
that there is a distinction between storage duration and scope, though
both are influenced by explicit and implicit storage-class specifiers".


Can anyone provide a solution for this?



4.

"245(§B1.3, and also at p. 157 §7.4): The scanf functions do not ignore
white space in formats; if white space occurs at a place in the format,
any white space in the corresponding input is skipped".


I am confused on this. When we do scanf("%d%d", &x, &y); when we enter
whitespaces they don't get ignored?



5.

"246(§B1.3): In table B-2, arguments corresponding to o, x, X, u are
unsigned int *".


The table B-2 doesn't mention 'X'. Any ideas?




6. "There is no mention of the offsetof macro in §B".


Can anyone provide a solution for this?
 
I

Ioannis Vranos

Eric said:
Perhaps that's the error.


No, 'o' and 'x' are erroneously mentioned as "int *" instead of
"unsigned int *" in the book.

The question is, under C90 has scanf() any uppercase 'X' format specifier?
 
K

Keith Thompson

Ioannis Vranos said:
C90:
From the K&R2 errata site: http://tinyurl.com/3nbvbh

1.

"...
Also, the comparison-routine argument is not treated well. The call
shown on p 119, with an argument

(int (*)(void*,void*))(numeric? numcmp : strcmp)

is not only complicated, but only barely passes muster. Both numcmp and
strcmp take char * arguments, but this expression casts pointers to
these functions to a function pointer that takes void * arguments.

==> The standard does say that void * and char * have the same
representation, so the example will almost certainly work in practice,
and is at least defensible under the standard. There are too many
lessons in these pages".


Does the C90 standard guarantee that "void * and char * have the same
representation"?

Yes, but it doesn't guarantee that a pointer to a function with void*
parameters is compatible with a pointer to a function with char*
parameters. It would take a perverse implementation for the cast
above to fail, but the standard doesn't guarantee that it will work.

In general, the only guarantees regarding a conversion from one
pointer-to-function type to another ar that it doesn't lose
information and that converting it back to the original type gives you
a usable pointer.
2.

"143(§6.5): strdup is not indexed".


What does that mean?

Presumably it means that strdup is not mentioned in the book's index.
(Of course strdup is non-standard, but I think K&R2 presents it as an
example.)
3.

"195(§A4.1) The first few sentences might be reworded a bit to emphasize
that there is a distinction between storage duration and scope, though
both are influenced by explicit and implicit storage-class specifiers".


Can anyone provide a solution for this?

Re-word the sentences.
4.

"245(§B1.3, and also at p. 157 §7.4): The scanf functions do not ignore
white space in formats; if white space occurs at a place in the format,
any white space in the corresponding input is skipped".

I am confused on this. When we do scanf("%d%d", &x, &y); when we enter
whitespaces they don't get ignored?

You've chosen an example where whitespace doesn't make any difference;
scanf with "%d%d" and with "%d %d" are equivalent. But consider the
"%c" format. " %c" skips whitespace and then reads a single
character; "%c" reads a single character which may be whitespace.
The "%[" format is similar.
5.

"246(§B1.3): In table B-2, arguments corresponding to o, x, X, u are
unsigned int *".


The table B-2 doesn't mention 'X'. Any ideas?

Yes, read the documentation for scanf.
6. "There is no mention of the offsetof macro in §B".

Can anyone provide a solution for this?

A solution would be to mention the offsetof macro. What kind of
solution were you expecting us to be able to provide?

Drafts of the C89 and C99 standards are freely available. The C89/C90
standard itself is admittedly hard to find, but the C99 standard can
be purchased for a reasonable sum (and a lot of things are unchanged
from C90). Since you seem to be primarily concerned with C89/C90 and
C95, I suggest that referring to one of the C89 drafts would answer
many of your questions (you could ask here for definitive information
on what changed between the draft and the published C89/C90 standard).
 
I

Ioannis Vranos

Keith said:
Re-word the sentences.


Which is "the distinction between storage duration and scope"? May you
provide an example?


4.

"245(§B1.3, and also at p. 157 §7.4): The scanf functions do not ignore
white space in formats; if white space occurs at a place in the format,
any white space in the corresponding input is skipped".

I am confused on this. When we do scanf("%d%d", &x, &y); when we enter
whitespaces they don't get ignored?

You've chosen an example where whitespace doesn't make any difference;
scanf with "%d%d" and with "%d %d" are equivalent. But consider the
"%c" format. " %c" skips whitespace and then reads a single
character; "%c" reads a single character which may be whitespace.
The "%[" format is similar.


For the code:

#include <stdio.h>

int main()
{
char c= 0;

printf("%d\n", scanf("\n%c", &c));

printf("%d\n", c);

return 0;
}



When I press enter alone, it does not seem to accept it, while the code


#include <stdio.h>

int main()
{
char c= 0;

printf("%d\n", scanf("%c", &c));

printf("%d\n", c);

return 0;
}

accepts enter.


Any ideas why this happens?

Yes, read the documentation for scanf.


I checked C99, and for fscanf() it doesn't mention "%X" format
specifier, only the "%x".
 
R

Richard Tobin

Ioannis Vranos said:
Which is "the distinction between storage duration and scope"? May you
provide an example?

Storage duration is the lifetime of an object, sometimes referred to as
"extent". Scope is the part of the program where a name is visible.
A declaration introduces both a name and an object; scope is a
property of the name, and duration a property of the object.

If we have

int a;
static int b;
void foo(void)
{
int c;
...
}

The storage duration of a and b is the whole life of the program. The
scope of a is potentially the whole program: any file that has a
declaration "extern int a" can see it. The scope of b on the other
hand is just the containing file.

The duration of c is from the time foo() is entered until it returns.
Its scope is the body of the function. Note that the object can be
referred to outside the body of foo() if, for example, a pointer to it
is passed to another function, provided, even though c is not in
scope there.

Malloc() creates objects without creating names. The duration of a
malloc()ed object is from the time is malloc()ed until it if free()ed
(or realloc()ed).

In some languages there are names whose scope is limited not to a part
of the program text, but to a lifetime. A function may create a
variable x and that variable is visible anywhere in the program until
the function returns. C doesn't have such "dynamically scoped"
variables.

-- Richard
 
P

Philip Potter

Ioannis said:
Which is "the distinction between storage duration and scope"? May you
provide an example?

void f(void) {
static int i=0;
i++;
return;
}

This (somewhat useless) function demonstrates the difference. i is in
scope within f(), and not outside of f(). But i has static storage
duration, meaning it exists from the start to the end of the program
(IOW, by the first statement of main(), i exists and has value 0, and
when main() returns or exit() is called, i exists and its value tells
you the number of times f() has been called (provided this is <= INT_MAX).

The confusion arises because most variables (in practice) have lifetime
and scope which are equal. For example:

static int i;
int j;

void f(void) {
int k;
for (k=0; k<10; k++) {
int l;
l = k + 2;
}
}

each variable here is in scope for its whole lifetime. But in the first
example, i can be out of scope but still exist (e.g. between two calls
to f())

Philip
 
P

Philip Potter

Philip said:
void f(void) {
static int i=0;
i++;
return;
}

This (somewhat useless) function demonstrates the difference. i is in
scope within f(), and not outside of f(). But i has static storage
duration, meaning it exists from the start to the end of the program
(IOW, by the first statement of main(), i exists and has value 0, and
when main() returns or exit() is called, i exists and its value tells
you the number of times f() has been called (provided this is <= INT_MAX).

The confusion arises because most variables (in practice) have lifetime
and scope which are equal. For example:

static int i;
int j;

void f(void) {
int k;
for (k=0; k<10; k++) {
int l;
l = k + 2;
}
}

each variable here is in scope for its whole lifetime. But in the first
example, i can be out of scope but still exist (e.g. between two calls
to f())

Whoops! in the second example, the variable i can be out of scope during
its lifetime - in any code in any other translation unit. Similarly j is
not in scope in any other translation unit which doesn't contain 'extern
int j;' or equivalent. See Richard Tobin's response (which prompted this
correction).
 
K

Keith Thompson

Ioannis Vranos said:
Which is "the distinction between storage duration and scope"? May you
provide an example?

In any draft of the C99 standard, read sections 6.2.1 (Scopes of
identifiers) and 6.2.4 (Storage duration of objects). If you're still
confused, please post again and let us know specifically what you're
confused about.

[...]
For the code:

#include <stdio.h>
int main()
{
char c= 0;
printf("%d\n", scanf("\n%c", &c));
printf("%d\n", c);
return 0;
}

When I press enter alone, it does not seem to accept it, while the code

#include <stdio.h>
int main()
{
char c= 0;
printf("%d\n", scanf("%c", &c));
printf("%d\n", c);
return 0;
}

accepts enter.

Any ideas why this happens?

(I've deleted extraneous blank lines in the above; they just make it
more difficult to read.)

"\n" is whitespace; see C99 7.19.6.2p5.

For the behavior of "%c", see C99 7.19.6.2p12; it reads a single
character, such as the new-line character generated when you press
enter.

The behavior of both programs follows straightforwardly from the
specification I've cited. If you're still confused after reading the
above, please re-post and explain exactly what you're confused about.
I checked C99, and for fscanf() it doesn't mention "%X" format
specifier, only the "%x".

C99 7.19.6.2p14:

The conversion specifiers A, E, F, G, and X are also valid and
behave the same as, respectively, a, e, f, g, and x.

For fprintf, the uppercase versions of these formats cause any printed
letters (hex digits, the 'x' in the 0x hexadecimal prefix, the 'e' for
the exponent in decimal floating-point, the 'p' for the exponent in
hexadecimal floating-point) to be printed in uppercase. For fscanf,
these letters are accepted in either uppercase or lowercase, so
there's no semantic difference between, say, "%x and "%X", but they're
allowed for consistency.
 
L

lawrence.jones

Ioannis Vranos said:
I checked C99, and for fscanf() it doesn't mention "%X" format
specifier, only the "%x".

7.19.6.2p14:

The conversion specifiers A, E, F, G, and X are also valid and
behave the same as, respectively, a, e, f, g, and x.

-Larry Jones

I've changed my mind, Hobbes. People are scum. -- Calvin
 
I

Ioannis Vranos

Keith said:
C99 7.19.6.2p14:

The conversion specifiers A, E, F, G, and X are also valid and
behave the same as, respectively, a, e, f, g, and x.

OK, saw it.

For fprintf, the uppercase versions of these formats cause any printed
letters (hex digits, the 'x' in the 0x hexadecimal prefix, the 'e' for
the exponent in decimal floating-point, the 'p' for the exponent in
hexadecimal floating-point) to be printed in uppercase. For fscanf,
these letters are accepted in either uppercase or lowercase, so
there's no semantic difference between, say, "%x and "%X", but they're
allowed for consistency.


OK, thanks.
 
I

Ioannis Vranos

Keith said:
In any draft of the C99 standard, read sections 6.2.1 (Scopes of
identifiers) and 6.2.4 (Storage duration of objects). If you're still
confused, please post again and let us know specifically what you're
confused about.

[...]
For the code:

#include <stdio.h>
int main()
{
char c= 0;
printf("%d\n", scanf("\n%c", &c));
printf("%d\n", c);
return 0;
}

When I press enter alone, it does not seem to accept it, while the code

#include <stdio.h>
int main()
{
char c= 0;
printf("%d\n", scanf("%c", &c));
printf("%d\n", c);
return 0;
}

accepts enter.

Any ideas why this happens?

(I've deleted extraneous blank lines in the above; they just make it
more difficult to read.)

"\n" is whitespace; see C99 7.19.6.2p5.


OK, I am puzzled because the errata mentions:

"245(§B1.3, and also at p. 157 §7.4): The scanf functions do not ignore
white space in formats; if white space occurs at a place in the format,
any white space in the corresponding input is skipped".


and the original text mentions:

"The format string may contain:

* Blanks or tabs, which are ignored".


So the correction should be:

"The format string may contain:

* If blanks or tabs occur at a place in the format, any blanks or tabs
in the corresponding input are skipped respectively".


or


"The format string may contain:

* If white space occurs at a place in the format, any white space in the
corresponding input is skipped". ?


The errata sentence itself: "if white space occurs at a place in the
format, *any* white space in the corresponding input is skipped" is
puzzling.


So can we have scanf("\t%c", &x); and we input a space ' ' instead of a
tab, scanf() ignores the space although we used a '\t' in the format
specifier?
 
K

Keith Thompson

Ioannis Vranos said:
Keith Thompson wrote: [...]
OK, I am puzzled because the errata mentions:

"245(§B1.3, and also at p. 157 §7.4): The scanf functions do not ignore
white space in formats; if white space occurs at a place in the format,
any white space in the corresponding input is skipped".

and the original text mentions:

"The format string may contain:

* Blanks or tabs, which are ignored".


So the correction should be:

"The format string may contain:

* If blanks or tabs occur at a place in the format, any blanks or tabs
in the corresponding input are skipped respectively".


or


"The format string may contain:

* If white space occurs at a place in the format, any white space in the
corresponding input is skipped". ?

Yes, unless it occurs within another directive; for example,
whitespace can appear within a "%[...]" directive.
The errata sentence itself: "if white space occurs at a place in the
format, *any* white space in the corresponding input is skipped" is
puzzling.


So can we have scanf("\t%c", &x); and we input a space ' ' instead of a
tab, scanf() ignores the space although we used a '\t' in the format
specifier?

The standard says:

A directive composed of white-space character(s) is executed by
reading input up to the first non-white-space character (which
remains unread), or until no more characters can be read.

White-space characters are space, tab, new-line, vertical-tab, and
formfeed. So a directive composed of just those characters causes
consecutive white-space characters (zero or more of them) to be read
and discarded. No distinction is made between different whte-space
characters, either in the format string or in the input.

You seem to be trying to construct corrected wording for K&R. Out of
curiosity, why? Isn't sufficient to understand how fscanf works?
 
I

Ioannis Vranos

Keith said:
You seem to be trying to construct corrected wording for K&R. Out of
curiosity, why? Isn't sufficient to understand how fscanf works?


I have recently bought the English version because I wanted to have an
accurate reference with all errata entered (given that it has a C89
reference).

In the greek translation I had, the page numbering was different enough
to make it very difficult to enter the errata.
 
I

Ioannis Vranos

Keith said:
Yes, but it doesn't guarantee that a pointer to a function with void*
parameters is compatible with a pointer to a function with char*
parameters. It would take a perverse implementation for the cast
above to fail, but the standard doesn't guarantee that it will work.

In general, the only guarantees regarding a conversion from one
pointer-to-function type to another ar that it doesn't lose
information and that converting it back to the original type gives you
a usable pointer.


The complete errata from http://tinyurl.com/3nbvbh :


"119-121(§5.11): The qsort discussion needs recasting in several ways.
First, qsort is a standard routine in ANSI/ISO C, so the rendition here
should be given a different name, especially because the arguments to
standard qsort are a bit different: the standard accepts a base pointer
and a count, while this example uses a base pointer and two offsets".


Because of this I renamed the "qsort" references, declarations and
definitions as "sort".



"Also, the comparison-routine argument is not treated well. The call
shown on p 119, with an argument

(int (*)(void*,void*))(numeric? numcmp : strcmp)

is not only complicated, but only barely passes muster. Both numcmp and
strcmp take char * arguments, but this expression casts pointers to
these functions to a function pointer that takes void * arguments. The
standard does say that void * and char * have the same representation,
so the example will almost certainly work in practice, and is at least
defensible under the standard. There are too many lessons in these pages."


So, what can be a reasonable fix for this mistake in K&R2? On page 119,
changing the function signature of sort (renamed sort from qsort as
mentioned above) taking as an argument "int (*comp)(void *, void *)" to
take as an argument int (*comp)(char *, char *) above the main()
definition, and remove the cast in main()?


I should expect Kernighan & Ritchie to provide more complete errata for
K&R2, than these.
 
I

Ioannis Vranos

Eric said:
"Solution?" Are you asking us to rewrite K&R, or what?


Well, if we all provided solutions for the ambiguous errata of K&R2 and
posted them here and sending them to K&R errata site, and/or perhaps
added to clc FAQ, I think would be useful to all.

Myself am going to post here and send to that K&R errata site all the
solutions I implemented for the ambiguous errata I have solved or others
contributed solutions, with their names, when finished.

The current solution I propose for this ambiguous errata is the addition
of a sentence:

"There is a distinction between storage duration and scope, though both
are influenced by explicit and implicit storage-class specifiers", right
after the sentence of A4.1, "Several keywords, together with the context
of an object's declaration, specify its storage class".


What does everyone think of this?
 
I

Ioannis Vranos

For the errata:

"Index: stddef.h is listed but not summarized in the text; It needs a
section in Appendix B".


I propose the following:

"B12. Common standard type definitions: <stddef.h>

The standard header <stddef.h> defines the following types and macros:

The types are:

ptrdiff_t
A signed integral type of the result of subtracting two pointers.

wchar_t
An integral type whose range of values can represent distinct
wide-character codes for all members of the largest extended character
set specified among the supported locales; the null character has the
code value 0 and each member of the basic character set has a code value
equal to its value when used as the lone character in an integer
character constant.

size_t
An unsigned integral type of the result of the sizeof operator.


The macros are:

NULL
NULL expands to an implementation-defined null pointer constant.

offsetof(type, member-designator)
offsetof expands to an integral constant expression of type size_t,
the value of which is the offset in bytes to the structure member
(member-designator), from the beginning of its structure (type)".


Any feedback is welcome.
 
I

Ioannis Vranos

A minor modification:


For the errata:

"Index: stddef.h is listed but not summarized in the text; It needs a
section in Appendix B".


I propose the following:

"B12. Common standard type definitions: <stddef.h>

==> The header <stddef.h> defines the following types and macros:

The types are:

ptrdiff_t
A signed integral type of the result of subtracting two pointers.

wchar_t
An integral type whose range of values can represent distinct
wide-character codes for all members of the largest extended character
set specified among the supported locales; the null character has the
code value 0 and each member of the basic character set has a code value
equal to its value when used as the lone character in an integer
character constant.

size_t
An unsigned integral type of the result of the sizeof operator.


The macros are:

NULL
NULL expands to an implementation-defined null pointer constant.

offsetof(type, member-designator)
offsetof expands to an integral constant expression of type size_t,
the value of which is the offset in bytes to the structure member
(member-designator), from the beginning of its structure (type)".


Any feedback is welcome.
 

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,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top