Errata

Understanding and Using C Pointers

Errata for Understanding and Using C Pointers

Submit your own errata for this product.

The errata list is a list of errors and their corrections that were found after the product was released. If the error was corrected in a later version or reprint the date of the correction will be displayed in the column titled "Date Corrected".

The following errata were submitted by our customers and approved as valid errors by the author or editor.

Color key: Serious technical mistake Minor technical mistake Language or formatting error Typo Question Note Update

Version Location Description Submitted By Date submitted Date corrected
1
chapter 1, "pointers and memory" section, right before "declaring pointers" subsection

uh, the safari version of this book doesn't have pages (so, sorry, no page number or succinct location).

in the *undefined* section it says "A list of *unspecified* behavior can be found at CERT Secure Coding Appendix CC." (emphasis mine)

it should say, "A list of *undefined* behavior can be found at CERT Secure Coding Appendix CC" (as the CERT Secure Coding Appendix CC is entitled "Undefined Behavior" and "unspecified behavior" was covered in the previous paragraph).

Note from the Author or Editor:
In Chapter 1, Introduction, towards the end of the section titled, "Why You Should Become Proficient with Pointers", the third bullet contains the following sentence:

"A list of unspecified behavior can be found at CERT Secure Coding Appendix CC."

It should read:

"A list of undefined behavior can be found at CERT Secure Coding Appendix CC."

Corey Wright  May 16, 2013  Apr 04, 2014
1
Ch 1 -> "Common Uses of Pointers" -> "Constants and Pointers" -> "Pointers to a Constant"

In discussing pointers to constants (meaning pointers that point to values that cannot be changed [at least by way of the pointer]), it is said:

"The pointer thinks it is pointing to a constant integer; therefore, it does allow the modification of the integer using the pointer."

It should say:

"The pointer thinks it is pointing to a constant integer; therefore, it does *not* allow the modification of the integer using the pointer." (Emphasis mine.)

Just a small typo.

Note from the Author or Editor:
Change:

"The pointer thinks it is pointing to a constant integer; therefore, it does allow the modification of the integer using the pointer."

To read:

"The pointer thinks it is pointing to a constant integer; therefore, it does not allow the modification of the integer using the pointer."

Corey Wright  May 18, 2013  Apr 04, 2014
Printed
Page 2
the "automatic" subsection

The scope of automatic variable is tied to block (any block, not only function block). If there is a subblock inside a function, for a variable declared inside, the statement "Their scope is restricted to the function" is not true.

Note from the Author or Editor:
Page 2
Automatic section

After:

"... the fuction is executing."

Add the following paragraph

"In general, the scope of variables declared within a block statement is restricted to that block. These are often referred to as automatic variables."

Brian Chiu  Jun 19, 2014 
PDF
Page 4
2nd paragraph

The text says "However, in the new C standard, C11, variable size arrays are supported.". While technically speaking this sentence is true, it suggest that variable size arrays are only supported by C11, not earlier language standards. Truth is, this feature was added in much earlier, C99 standard.

Note from the Author or Editor:
Page 4 - Replace the following sentence;

"However, in the new C standard, C11, variable size arrays are supported."

With:

"However, variable size arrays have been supported since C99.".

Krzysztof Adamski  Apr 05, 2014 
Printed
Page 11
2nd paragraph

The following code:

printf("%p\n",*pi); // Displays 5

uses incorrect format specifier (i.e. %p) for displaying a (derefenced) value of int type, which invokes undefined behavior. The correct, UB-free version would be:

printf("%d\n",*pi); // Displays 5

Note from the Author or Editor:
Page 11
2nd paragraph
Change:

printf("%p\n",*pi); // Displays 5

To:

printf("%d\n",*pi); // Displays 5

Grzegorz Szpetkowski  Feb 20, 2015 
Printed
Page 14
second bullet of paragraph 'Pointer to void'

A pointer to void will never be equal to another pointer.

? should be ? :

A pointer to void will never be equal to another pointer to void.

reasoning : a pointer to void can be assigned the value of a non-void pointer

Note from the Author or Editor:
On page 14, second bullet, add the following sentence:

The actual behavior of void pointers is system dependent.

Andre  Aug 02, 2013  Apr 04, 2014
Printed
Page 17
5th line

intptr_t and uintprt_t should be
intptr_t and uintptr_t

Note from the Author or Editor:
On page 17, fifth line, change "uintprt_t" to "uintptr_t".

Anonymous  Jul 05, 2013  Apr 04, 2014
Printed
Page 19
2nd paragraph of Using intptr_t and uintptr_t

I think that that explanation of intptr_t and uintptr_t along with code listings is kind of misleading and misses the point. Consider applying following modifications:

1)

The following illustrates how to use intptr_t:

int num;
intptr_t *pi = #

with:

The following illustrates how to use intptr_t:

int num;
intptr_t pi = (intptr_t)#

You might also tell reader that as by C11 7.20.1.4/p1 the only valid pointer that can be converted to intptr_t (as well as uintptr_t) is void* and since not all implementation shares the same size for all object pointer, thus most portable (always valid) construct would be:

int num;
intptr_t pi = (intptr_t)(void *)#

2)

The error for uintptr_t is irrelevant. The correct way to handle uintptr_t type is analogous to previous case:

uintptr_t pu = (uintptr_t)#

or

uintptr_t pu = (uintptr_t)(void *)#

Additional suggestions:

- information that these types are optional, thus may not always be provided
- information to print values for them (such as: printf("%" PRIxPTR "\n", pi);)

Note from the Author or Editor:
Page 19
Section titled "Using intptr_t and uintptr_t"
First paragraph
Add the following sentence to the end of the first paragraph:

The only valid pointer that can be converted to intptr_t (as well as uintptr_t) is void*.

Replace the text from:

int num;

To the last paragraph of the page (It starts with "These types can be used ...") with:

int num;
intptr_t pi = (intptr_t) (void *) #

We can use uintptr_t in a similar manner as shown here:

uintptr_t pu = (uintptr_t)(void *)#

We cannot use intptr_t or unintptr_t with other data types without casting:

char c;
uintptr_t pc = (uintptr_t)(void *)&c;

Grzegorz Szpetkowski  Feb 21, 2015 
Printed
Page 21
Table 1.5

There is no byte type in C. To represent entity of exactly one byte there is char type or its varations such signed char and unsigned char. All three are guranteed that sizeof(T) == 1 holds.

Note from the Author or Editor:
Page 21
Table 1-5. Data type sizes
Remove the first table entry: "byte 1"

Grzegorz Szpetkowski  Feb 21, 2015 
Page 22
In code sample after figure 1-8

In this section "pointers to short and char", which is just after figure 1-8 (page number of 22 is approximate because I was reading the kindle edition)

printf("Content of ps before: %d\n",ps);
ps = ps + 1;
printf("Content of ps after: %d\n",ps);

printf("Content of pc before: %d\n",pc);
pc = pc + 1;
printf("Content of pc after: %d\n",pc);

The use of the %d format specifier results in negative values on some platforms:
content of ps before: -272632754
content of ps after: -272632752
content of pc before: -272632769
content of pc after: -272632768
It should be replaced with %p specifier as the author uses on many other examples.

Corrected version:

printf("Content of ps before: %p\n",ps);
ps = ps + 1;
printf("Content of ps after: %p\n",ps);

printf("Content of pc before: %p\n",pc);
pc = pc + 1;
printf("Content of pc after: %p\n",pc);

Here's an example of the corrected output on my platform:
content of ps before: 0x7ffeefbff44e
content of ps after: 0x7ffeefbff450
content of pc before: 0x7ffeefbff43f
content of pc after: 0x7ffeefbff440

Note from the Author or Editor:
While the use of the %d specifier can result in a negative value on some platforms, it is not an error. However, to avoid confusion, please add the following statement:

If the results displayed are negative values, use the %p specifier instead.

Before the first sentence on page 23. That sentence starts with: "The ps pointer is ...

Thanks.

Darrell Root  Sep 09, 2021 
PDF
Page 23
middle of the page (a warning example)

The correct option to display that kind of warning message is -Wpointer-arith, not -Wpointerarith, if the compiler is GCC.

Note from the Author or Editor:
On page 23 add a hyphen to:

... [-Wpointerarith]

So it appears as:

... [-Wpointer-arith]

Anonymous  Oct 02, 2013  Apr 04, 2014
PDF
Page 23
Section "Pointers to void and addition"

>Here we will assume the size of a pointer to void is four.
>the resulting address contained in pv will be incremented by four bytes.

Pointer arithmetic will be calcurated based on the size of base type, not pointer type.

In this case
pv + 1 != pv + sizeof(void *)
pv + 1 == pv + sizeof(void)

On the ordinary environment address of pv+1 will be 1 larger than pv.

Note from the Author or Editor:
On page 23, replace the sentence:

However, the resulting address contained in pv will be incremented by four bytes.

To:

However, the resulting address contained in pv will be incremented by one byte. Most compilers will treat the data type void as if it has a size of one byte. Since the pointer points to void, the address is incremented by one.

Most compilers will issue a warning if the sizeof operator is used with the void data type. This is different from the size of a pointer to void which is normally four bytes.

Anonymous  Oct 02, 2013  Apr 04, 2014
Printed
Page 24
diagram

addr 116 should be labeled p1, addr 120 should be
labeled p2

Note from the Author or Editor:
On page 24 change the text in Figure 1-9 from:

p0 116
p0 120

to:

p1 116
p2 120

Adam Zaremba  Jun 03, 2013  Apr 04, 2014
Printed
Page 26
Figure 1-10

The array called "title" in Figure 1-10 should be named "titles".

Richard Reese
Richard Reese
 
Mar 11, 2014  Apr 04, 2014
Page 27
Section "Pointers to a constant"

The section is about pointers to a constant, but the text presents them twice as constant pointers.

1. "Dereferencing a constant pointer is fine if we are simply reading the integer's value"

This statement seems correct for both pointers to constant and constant pointers, but considering the section it is placed, shouldn't it be "Dereferencing a pointer to a constant is fine [...]"?

2. "We cannot dereference a constant pointer to change what the pointer references but we can change the pointer"

This statement seems incorrect for constant pointers, shouldn't it be "We cannot dereference a pointer to a constant to change what the pointer references [...]"?

Thank you!

Note from the Author or Editor:
The reader has some good ideas. On page 27, change the statement at the bottom of the page from:

"Dereferencing a constant pointer is fine ..."

To:

"Dereferencing a pointer to a constant is fine..."

On page 28, at the top of the page change:

"We cannot dereference a constant pointer to change what the pointer references but we can change the pointer."

To:

"We cannot dereference a pointer to a constant to change what the pointer references. But we can change the pointer."

Thanks.

Anonymous  Sep 01, 2021 
Printed
Page 34
5th

The author introduces the malloc function and has given the relevant code for it. However, the given code will generate multiple warnings as the author has not mentioned to include <stdlib.h> before using the malloc function. This file includes the declaration of the function. I think this can be very confusing for a student.

Note from the Author or Editor:
Page 34
Dynamic Memory Allocation Section
Change the first sentence from:

The basic steps used for dynamic memory allocation in C are:

To:

The basic steps used for dynamic memory allocation in C are listed next. The functions are found in the stdlib.h header file.

Adit Gupta  Jan 09, 2015 
Printed
Page 36
the code following 3rd paragraph

the code:
*pc[i] = 0;
should be:
*(pc+i) = '0';
Or
pc[i] = '0';

Note from the Author or Editor:
Page 36
First code sequence
Change the statement:

*pi[i] = 0;

To either:

pi[i] = 0;

Or:

*(pi+1) = 0;

Either will work.

Anonymous  Oct 15, 2014 
Printed
Page 44
4th paragraph in Using the realloc Function

The following sentence:

"If the size is zero and the pointer is not null, then the pointer will be freed."

along with second row in Table 2-2:

"Not null 0 Original block is freed"

is not entirely true in my opinion. The actual behaviour is implementation-defined. As by C11 7.22.3/p1 Memory management functions:

If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the
returned pointer shall not be used to access an object.

and C11 7.22.3.5:

3) (...) If memory for the new object cannot be allocated, the old object is not deallocated and its value is unchanged.

4) The realloc function returns a pointer to the new object (which may have the same value as a pointer to the old object), or a null pointer if the new object could not be allocated.

I made some basic example, that compiles with GCC 4.4.7 on GNU/Linux CentOS 6, that indicates that memory is not freed:

#include <mcheck.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
int a = 5;
int *p, *q;

mtrace();

p = malloc(sizeof(int));
q = &a;

printf("%p\n", (void *) p);
printf("%p\n", (void *) q);

q = realloc(p, 0);

printf("%p\n", (void *) p);
printf("%p\n", (void *) q);

return 0;
}

$ gcc -g check.c
$ export MALLOC_TRACE=report
$ ./a.out
0xd4e460
0x7fff5fa0ecdc
0xd4e460
(nil)
$ mtrace a.out report

Memory not freed:
-----------------
Address Size Caller
0x0000000000d4e460 0x4 at /home/grzegorz/workspace/check.c:12

As you can see the q was set to NULL with realloc call. According to spec this clearly means that realloc failed to allocate block with zero, thus p is not changed. The mtrace tool also indicates that it happened in that way.

I am marking it as question, because I am not sure how to rewrite the sentence. Maybe the simplest way is to put this as "implementation-defined" and that deallocation is not guaranteed. Curiously, man realloc (on my system) says that:

If ptr is NULL, then the call is equivalent to malloc(size), for all values of size; if size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr).

which seems to untrue according to above experiment.

Note from the Author or Editor:
Page 44
Last sentence

"The function's behavior is summarized in Table 2-2."

Add the following statement after the sentence:

"Bear in mind that the actual behavior of the realloc function is implementation dependent. Always verify its behavior before use."

Grzegorz Szpetkowski  Feb 22, 2015 
PDF
Page 46
2nd paragraph from the beginning of the section "The alloca function..."

The paragraph reads:
...In the following
example, an array of char is allocated for use in a function:
void compute(int size) {
char* buffer[size];
...
}

Either the description or the code is wrong. The code defines and allocates an array of pointers to char. The description "an array of chars is allocated" should be coded as: char buffer[size];

Note from the Author or Editor:
On page 46 change the following line of code:

char* buffer[size];

to:

char buffer[size];

Miguel A. Rodriguez-Jodar  May 23, 2013  Apr 04, 2014
PDF
Page 60
First code snippet, the average function.

The variable sum is a automatic variable and is not initialized. Based on the C standard:

If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate.

Even if we may get the correct result on some compilers, we should initialize it. So we should change

int sum;

to

int sum = 0;

Note from the Author or Editor:
On page 60, change:

int sum;

to

int sum = 0;

While the purpose of the example is to show the structure of the stack frame, this correction will ensure the proper execution of the function.

Ivor Horton  Aug 02, 2020 
PDF, Mobi
Page 61
Last paragraph

"Passing a pointer to the object means the object does *not*.... "

(Last sentence)


The page number is from the PDF format of the book.

Note from the Author or Editor:
Change:

"Passing a pointer to the object means the object does have to
be copied, and we can access the object through the pointer."

To:

"Passing a pointer to the object means the object does not have to
be copied, and we can access the object through the pointer."

Amit Saha  May 11, 2013  Apr 04, 2014
Printed
Page 64

sentence should say ?We cannot pass the address of an
integer constant to a pointer to an integer, as this would allow a
constant value to be modified."

Note from the Author or Editor:
On page 64 change:

?We cannot pass the address of an integer constant to a pointer to a constant, as this would allow a constant value to be modified."

To:

"We cannot pass the address of an integer constant to a pointer to an integer, as this would allow a constant value to be modified."

Adam Zaremba  Jun 03, 2013  Apr 04, 2014
Printed
Page 69
example at bottom of the page

the call of allocateArray should be :
allocateArray(vector,5,45) and not allocateArray(&vector,5,45)
because &vector is not a simple pointer but a pointer to a pointer

As a result,iIn Figure 3-8 , the value of the parameter 'arr' in the stack frame in the 'Before malloc' section should be NULL

Note from the Author or Editor:
When we try to execute the example, it will normally fail as we are trying to use vector as an array but its address is NULL. This will result in a segmentation fault type error. In addition, it will generate an warning complaining about incompatible pointer types.

Andre  Aug 05, 2013  Apr 04, 2014
PDF
Page 74
7 lines below Passing Function Pointers

Change

int subtract(int num1, int num2)

to

int sub(int num1, int num2)

Note from the Author or Editor:
On page 74, in the bottom code sequence, change

int subtract(int num1, int num2)

to

int sub(int num1, int num2)

James  Jul 12, 2013  Apr 04, 2014
Printed
Page 75-76
Several places

When the "subtract" function was renamed on page 74, several references to the function were not updated. The word "subtract" needs to be replaced with "sub" in the following locations:

Page 75:
Second paragraph of the section: Returning Function Pointer

"It will return a pointer to either the add or the subtract function"

First code sequence in that section:

" case: '-': retrun subtract;"

Page 76:
Third paragraph, last sentence:

"We will use this definition in conjunction with the add and subtract functions ..."

Fourth paragraph:

"... we will then assign the add and subtract functions ..."

Third code sequence:

" operations['-'] = subtract;"

Richard Reese
Richard Reese
 
Apr 16, 2014 
Printed
Page 76
first paragraph

....If the number of values is less than the size of the array, as in this example, the value is used to initialize every element of the array.

This doesn't appear quite correct to me ;
In my opinion the rule is that all array elements that do not have a corresponding initializer value , are initialized to 0 (or NULL in case of a pointer).
In the given example : operation operations[128] = {NULL} , the result of both interpretations is identical because a NULL value is used as the first initializer

Note from the Author or Editor:
On page 76, first paragraph, change the sentence reading:

If the number of values is less than the size of the array, as in this example, the value is used to initialize every element of the array.

To:

If the number of values is less than the size of the array, as in this example, then the remaining elements of the array are initialized to 0.

Andre  Aug 05, 2013  Apr 04, 2014
PDF
Page 76
5th paragarph code portion of example

Used gnu compiler for the following:

typedef int (*fptrOperation)(int,int);

int (*operations[128])(int, int) = {NULL};

int add(int num1, int num2)
{
return num1 + num2;
}
int subtract(int num1, int num2)
{
return num1 - num2;
}


void initializeOperationsArray() {
operations['+'] = add;
operations['-'] = subtract;
}


int evaluateArray(char opcode, int num1, int num2) {
fptrOperation operation;
operation = operations[opcode];
return operation(num1, num2);
}

int main()
{
initializeOperationsArray();
printf("%d\n",evaluateArray('+', 5, 6));
printf("%d\n",evaluateArray('-', 5, 6));

return EXIT_SUCCESS;
}


Got the following warning:

||=== Build: Debug in StructurePointerToPointer (compiler: GNU GCC Compiler) ===|
C:\Users\Ed\Programs\C_CodeBlocks\StructurePointerToPointer\main.c||In function 'evaluateArray':|
C:\Users\Ed\Programs\C_CodeBlocks\StructurePointerToPointer\main.c|511|warning: array subscript has type 'char' [-Wchar-subscripts]|
||=== Build finished: 0 error(s), 1 warning(s) (0 minute(s), 2 second(s)) ===|


If the following has to be re-cast in order to avoid the warning:
operation = operations[(uint32_t)( opcode) ];


Note from the Author or Editor:
I have not seen that warning before. It may be due to a change in the compiler. As a warning, I do not find it to be significant.

Replacing the the following statement on page 76 of the evaluateArray function:

operation = operations[opcode];

With the following, should eliminate the warning:

operation = operations[(uint32_t)( opcode) ];

Ed Marsh  Sep 28, 2019 
Printed
Page 78
code listing in the middle

This looks like small oversight:

basePointer = (fptrToSingleInt)fptrFirst;

Since type of basePointer is fptrBase, which is type definition for void (*p)(), the correct cast would be:

basePointer = (fptrBase)fptrFirst;

The former cast gives me error with pedantic-errors flag:

$ gcc -std=c99 -pedantic-errors check.c
check.c: In function ‘main’:
check.c:16: error: assignment from incompatible pointer type

and the latter compiles just fine.

Note from the Author or Editor:
Page 78
Last code segement
Change:

basePointer = (fptrToSingleInt)fptrFirst;

To:

basePointer = (fptrBase)fptrFirst;

This will avoid a syntax error when using the -std=c99 and -pedantic-errors compiler flag with the gcc compiler.

Grzegorz Szpetkowski  Mar 03, 2015 
Printed
Page 85
4th paragraph

"The notation vector+i generates machine code that starts at location vector, adds i to the address, and then uses the contents at this address."

"vector+i" should be replaced with *(vector+i) since later the sentence says, "contents at this address".

Note from the Author or Editor:
On page 85 in the second paragraph of the section titled, Differences Between Arrays and Pointers, change both occurrences of:

vector+i

To:

*(vector+i)

Joe Flowers  Jun 07, 2013  Apr 04, 2014
PDF
Page 85
1st paragraph

&vector[2] means go RIGHT 2 positions from vector and return that address instead of LEFT.

(right or left doesn't really matters, the fact is for for accessing vector[2] you use right direction from vector then for &vector[2] it have to be right too).

Note from the Author or Editor:
On page 85
First paragraph
Change the last sentence:

"It can be interpreted as go left two positions ..."

To:

"It can be interpreted as go right two positions ..."


sofiane Akermoun  Oct 06, 2014 
PDF
Page 86
The last paragraph (warning section)

we used *(pv+i) instead of *pv+1.

should be

we used *(pv+i) instead of *pv+i.

Note from the Author or Editor:
On page 86 at the bottom of the page cchange:

we used *(pv+i) instead of *pv+1.

TO:

we used *(pv+i) instead of *pv+i.

The difference is the use of the letter i instead of the number 1.

Anonymous  Jul 22, 2013  Apr 04, 2014
Printed, PDF, ePub
Page 87
Example in middle

The example given may contain undefined behavior due to the line "currentPosition = newBuffer + (currentPosition - buffer);" when the variable buffer is referenced after calling realloc.

This is extensively discussed at http://stackoverflow.com/questions/35047199/array-resizing-and-realloc-function

Note from the Author or Editor:
There is an interesting discussion of this problem found at: http://stackoverflow.com/questions/35047199/array-resizing-and-realloc-function. While not likely to occur in normal situations, the proposed solution as shown below will work:

if(++length >= maximumLength)
{
size_t currentOffset = currentPosition - buffer;

char *newBuffer = realloc(......
// ...

currentPosition = newBuffer + currentOffset;
buffer = newBuffer;
}

Anonymous  Mar 28, 2016 
PDF
Page 89
The last paragraph

"cat." should be " cat"

Note from the Author or Editor:
On page 89, last paragraph, change:

"cat.'

To:

" cat".

Blanks have been inserted in the string to reflect its original condition.

Anonymous  Jul 22, 2013  Apr 04, 2014
Printed
Page 89
First para after code sample

"The first while loop uses the tmp variable"

There is no tmp variable. The first while loop uses a temporary variable called "old".

Note from the Author or Editor:
On page 89, first paragraph after the code, first sentence, change the variable "tmp" to "old".

Leam Hall  Jul 26, 2013  Apr 04, 2014
Printed
Page 89
Last sentence of last paragraph

"The memory in red is the old..."

In the printed book there is no color. Perhaps "The memory the shaded area is the old..."

Note from the Author or Editor:
On page 89, last paragraph, last sentence, change:

The memory in red is the old ...

Should read:

The shaded memory is the old ...

Leam Hall  Jul 26, 2013  Apr 04, 2014
Printed
Page 89
code sample

This is minor nitpick. The name of string variable in main does not match to Figure 4-7, which uses name "word" for it. Thus my suggestion is to replace:

char* buffer = (char*)malloc(strlen(" cat")+1);
strcpy(buffer," cat");
printf("%s\n",trim(buffer));

with:

char* word = (char*)malloc(strlen(" cat")+1);
strcpy(word," cat");
printf("%s\n",trim(word));

or either replace word with buffer in Figure 4-7.

Note from the Author or Editor:
Page 89
Code sequence
Replace the variable, buffer, in the main function with the variable, word, as shown here:

char* word = (char*)malloc(strlen(" cat")+1);
strcpy(word," cat");
printf("%s\n",trim(word));

This will match the variables used in Figure 4-7 on page 90.

Grzegorz Szpetkowski  Mar 04, 2015 
Printed
Page 98
3rd paragraph

The following:

printf("%d ", (arr+i)[j]);

does not evaluate correct offset for arr (as it does not "jump" into next row, but only to next integer element). The proper expression would be:

printf("%d ", (arr+i*cols)[j]);

Note from the Author or Editor:
Page 98
Third code segment
Change:

printf("%d ",(arr+i) [j]);

To:

printf("%d ",(arr+i*cols) [j]);

This will correctly address the correct element of the matrix array.

Grzegorz Szpetkowski  Mar 04, 2015 
PDF
Page 99
After Figure 4-14.

Change:

The expression arr3d[1][0] refers to the second row, first column of the array and is a pointer to a one-dimensional array of size 5.

To:

(...) of size 4.

Because it is the actual size of this one-dimensional array from example.

Note from the Author or Editor:
Actually the size should be 16. It points to a 4 element array of integers occupying total of 16 bytes.

On page 99, second paragrah, last sentece, change:

The expression arr3d[1][0] refers to the second row, first column of the array and is a pointer to a one-dimensional array of size 5.

To:

The expression arr3d[1][0] refers to the second row, first column of the array and is a pointer to a one-dimensional array of size 16.

Pool Wryte  Sep 19, 2014 
Printed
Page 100
Allocating Contiguous Memory

The book describes two methods for allocating two-dimensional arrays contiguously. I believe that since C99 there is third way, which might be considered as "best of two worlds". Namely, we can allocate such two-dimensional array as:

int (*matrix)[columns] = (int (*)[columns]) malloc(rows * columns * sizeof(int));

The whole code sample could be like:

int rows = 2;
int columns = 5;

int (*matrix)[columns] = (int (*)[columns]) malloc(rows * columns * sizeof(int));

for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
printf("%p\n", &matrix[i][j]);
}
}

which gives output as:

0x8f0c008
0x8f0c00c
0x8f0c010
0x8f0c014
0x8f0c018
0x8f0c01c
0x8f0c020
0x8f0c024
0x8f0c028
0x8f0c02c

Assignment operation is also straightforward, as easy as:

matrix[i][j] = i*j;

Note from the Author or Editor:
Page 100
Grzegorz Szpetkowski of the Cracow University of Technology suggested a third way of allocating two-dimensional arrays. I have included it here as I believe it will add to your understanding of C.

"
I believe that since C99 there is third way, which might be considered as "best of two worlds". Namely, we can allocate such two-dimensional array as:

int (*matrix)[columns] = (int (*)[columns]) malloc(rows * columns * sizeof(int));

The whole code sample could be like:

int rows = 2;
int columns = 5;

int (*matrix)[columns] = (int (*)[columns]) malloc(rows * columns * sizeof(int));

for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
printf("%p\n", &matrix[i][j]);
}
}

which gives output as:

0x8f0c008
0x8f0c00c
0x8f0c010
0x8f0c014
0x8f0c018
0x8f0c01c
0x8f0c020
0x8f0c024
0x8f0c028
0x8f0c02c

Assignment operation is also straightforward, as easy as:

matrix[i][j] = i*j
"

Grzegorz Szpetkowski  Mar 08, 2015 
Printed
Page 104
code sample

The code sample uses "layer1", while the output below prints "arr2". Hence my suggestion is to replace all three occurrences of:

printf("layer1[%d][%d]...

with:

printf("arr2[%d][%d]...

Note from the Author or Editor:
Page 104
Code sequence

Change the three occurences of:

printf("layer1[%d][%d]...

with:

printf("arr2[%d][%d]...

Grzegorz Szpetkowski  Mar 08, 2015 
Printed
Page 114
diagram

globalHeader is displayHeader

Note from the Author or Editor:
In Figure 5-5 on page 114 change the stack frame titled "globalHeader" located above "main" with "displayHeader".

Adam Zaremba  Jun 03, 2013  Apr 04, 2014
Printed
Page 119
2nd paragraph

I am not sure if that sentence make sense here:

If we assume the first literal immediately precedes the second literal in memory, the output of this sequence will be as follows.

The placement of string literals has no impact on result. Code sample might be as well as:

char* error = "ERROR: ";
char *warning = "WARNING: ";
char* errorMessage = "Not enough memory";
char* buffer = ...

and the output would be just the same:

ERROR: Not enough memory
ERROR:
Not enough memory

The only requirement is that each literal is properly terminated by NUL and destination argument is large enough to hold an result.

Note from the Author or Editor:
Page 119
First example

For this example, the placement of the string literals has no impact on the results. The example was expanded upon in the next example to illustrate the effect if we are not careful about how we write our code. Potentially, there is a problem which depends on how the memory is allocated.

Grzegorz Szpetkowski  Mar 08, 2015 
Printed
Page 120

strcat mentioned 2 times should read strcpy

Note from the Author or Editor:
On page 120 replace the following text found at the end of the page:

"Always allocate dedicated memory for the concatentation:

strcat(error, errorMessage);"

with:

"Always allocate dedicated memory for the concatentation."



Adam Zaremba  Jun 03, 2013  Apr 04, 2014
Printed
Page 121
last paragraph

...We used parentheses to force the post increment operator to execute first , incrementing the pointer.....

Remark : this isn't necessary because the postfix-operator ++ has a higher precedence than the unary indirection operator *

Note from the Author or Editor:
On page 122, first sentence, change:

Otherwise, the character referenced by string would have been incremented, which is not what is desired:

To:

While the use of parentheses is not required, since the post-increment operator has higher precedence than the dereference operator, it makes the intent clear.

Andre  Aug 07, 2013  Apr 04, 2014
PDF
Page 121
code fragment on the middle of the page.

In the code example

char* path = "C:";
char* currentPath = (char*) malloc(strlen(path)+2);
currentPath = strcat(currentPath,"\\");

memory for "currentPath" is allocated, not initialized.
currentPath does not have anything even NUL terminator.
The result of strcat is unexpected.

This example needs copying path string to the
currentPath before invoking strcat, as follows.

char* path = "C:";
char* currentPath = (char*) malloc(strlen(path)+2);
strcpy(currentPath, path);
currentPath = strcat(currentPath,"\\");

Note from the Author or Editor:
On page 121, change the first code sequence to:

char path[] = "C:";
char* currentPath = (char*) malloc(strlen(path) + 2);
strcpy(currentPath,path);
strcat(currentPath, "\\");

This copies "C:" to currentPath first and then performs concatenation.

Anonymous  Aug 22, 2013  Apr 04, 2014
Printed
Page 122
a paragraph before Figure 5-12

> In the second statement, the
> address-of operator is used explicitly. This is redundant and
> unnecessary. In addition, it will often generate a warning.

The type of &simpleArray is not "char *" but "char **", because address-of operator is applied to char* variable.

Result of passing &simpleArray to stringLength is not "redundant". It will end with imcompatible pointer warning.

Note from the Author or Editor:
Page 122, replace the sentences:

This is redundant and unnecessary. In additional, it will often generate a warning.

With:

While this will work, we are passing a pointer to a pointer to a char instead of a pointer to a char which will generate a warning .



Anonymous  Oct 05, 2013  Apr 04, 2014
Printed
Page 124
Printf call demonstrating the use of the previously defined "format" function

The "char* format" function takes parameter "size_t size" which is used as the "size" argument to "snprintf", which is the size of the buffer the snprintf function writes to.

However the example showing the "format" function's use passes sizeof(buffer) to the function; if the buffer memory has been allocated via malloc, sizeof(buffer) will not be the size of the allocated buffer.

Note from the Author or Editor:
Page 124
The sentence before the second code sequence
Change:

"The following demonstrates the use of the function:"

To:

"The following statement demonstrates the use of the function. It assumes that buffer has been declared as an array. If the memory for buffer has been dynamically allocated, then the size of the memory allocated needs to be passed instead of using the sizeof function."

Michael  Oct 31, 2014 
Printed
Page 127
code listing returnAStaticLiteral

I believe that intent of "static string" was to declare it static array instead of static pointer:

static char bpCenter[] = "Boston Processing Center";
static char dpCenter[] = "Denver Processing Center";
static char apCenter[] = "Atlanta Processing Center";
static char sjpCenter[] = "San Jose Processing Center";

I see no point declaring static pointer with:

static char* bpCenter = "Boston Processing Center";

as string is still the same string literal, like in previous example. Returning an static pointer to an "ordinary" string literal is not the same as returning pointer to static string. The latter example also indicated that original intent was in such way.

Note from the Author or Editor:
Page 127
First code sequence
Replace the four pointer declarations with the following:

static char bpCenter[] = "Boston Processing Center";
static char dpCenter[] = "Denver Processing Center";
static char apCenter[] = "Atlanta Processing Center";
static char sjpCenter[] = "San Jose Processing Center";

The previous sequence will work but using static pointers is not necessary unless we want to return the address of a pointer.

Grzegorz Szpetkowski  Mar 08, 2015 
Printed
Page 131
first paragraph (after the code fragment)

typedef int (ftprOperation)(const char*,const char*)

?? should be ??

typedef int (* ftprOperation)(const char*,const char*)

Note from the Author or Editor:
On page 131, code fragment after first paragraph, change:

typedef int (ftprOperation)(const char*,const char*)

To:

typedef int (*ftprOperation)(const char*,const char*)

Andre  Aug 07, 2013  Apr 04, 2014
Printed
Page 135
Last sentence of first paragraph of new section

"... aligned on an address even divisible by four."
"even" should be "evenly"

Note from the Author or Editor:
Page 135
First paragraph of section
Last sentence.
Change:

"... on an address even divisible by four."

To:

"... on an address evenly divisible by four."

Michael  Nov 01, 2014 
Printed
Page 136
last paragraph

When we declare a variable of this type or dynamically allocate memory for this type, the three pointers will contain garbage.

refinement :
when a declared variable of a structure type has static storage duration (i.e. is declared outside all functions (global variable) or is declared as static within a function), then the structure members are initialized to 0 (NULL for pointers)

Note from the Author or Editor:
On page 136, last paragraph, after the sentence:

When we declare a variable of this type or dynamically allocate memory for this type, the three pointers will contain garbage.

Add:

However, when a declared variable of a structure type has static storage duration (i.e. is declared outside all functions (global variable) or is declared as static within a function), then the structure members are initialized to 0 (NULL for pointers).

Andre  Aug 08, 2013  Apr 04, 2014
Printed
Page 139
diagram

person is ptrPerson

Note from the Author or Editor:
Change the variable "person" in Figure 6-4 on page 139 to "ptrPerson".

Adam Zaremba  Jun 03, 2013  Apr 04, 2014
Printed
Page 142
4th paragraph

The fourth paragraph says; The DISPLAY function pointer designates a function that is passed void (should say void *) and returns void.

Note from the Author or Editor:
Page 142, fourth paragraph. The sentence that reads:

The DISPLAY function pointer designates a function that is passed void and returns void.

It should read:

The DISPLAY function pointer designates a function that is passed a pointer to void and returns void.

Toni Granholm  Jul 30, 2013  Apr 04, 2014
Printed
Page 142
3rd paragraph

Returned value may not be necessarily equal to -1 or 1 as strcmp is defined to return value that is either zero, negative or positive.

The are two remedies, that I would recommend:

1) Reflect negative or positive values in main text (i.e. instead of -1 and 1)
2) Modify compareEmployee to:

int tmp = strcmp(e1->name, e2->name);
return (tmp > 0) - (tmp < 0); // sgn(tmp)

Note from the Author or Editor:
Page 142
Third paragraph
Replace sentences 5 and 6 with:

"A negative return value means the first employee preceeds the second employee alphabetically. A postive return value means the first employee folows the second employee."

Grzegorz Szpetkowski  Mar 10, 2015 
Printed
Page 146
Figure 6-6

In the figure, linkedList variable has address of memory block allocated in the heap.

linkedList is not a pointer. It is declared as a local LinkedList structure in the code fragment in the page 145. linkedList is need to be drawn as structure allocated in the stack frame of main function.


Note from the Author or Editor:
That is correct. The easiest thing to do is to move the contents at address 700 (head, tail, and current pointers) to inside of the linkedList varible in the main stack frame. This replaces the number 700 currently inside of the linkedList variable. Otherwise the figure does not change.

Anonymous  Nov 20, 2013  Apr 04, 2014
Printed
Page 146
1st paragraph

The employees have been added in the opposite order from the previous example using the addTail function.

Previous example was about addHead function, thus I propose to replace with:

The employees have been added in the opposite order from the previous example using the addHead function.

Note from the Author or Editor:
Page 146
Last paragraph, third sentence
Replace the sentence with:

"The employees have been added in the opposite order from the previous example where we used the addHead function. Instead, we used the addTail function. "

Grzegorz Szpetkowski  Mar 11, 2015 
Printed
Page 154
Second to the last paragraph

Change the sentence:

"A common ordering is to add a new node to a tree such that all of the node?s children possess a value less than the parent node and all of the children on the right posses a value greater than the parent node."

To:

"A common ordering is to add a new node to a tree such that all of the node?s children on the left possess a value less than the parent node and all of the children on the right posses a value greater than the parent node."

"on the left" is added.

Richard Reese
Richard Reese
 
Mar 11, 2014  Apr 04, 2014
Printed
Page 155
insertNode code sample

The code, as it stands, will corrupt the root node pointer.
Adding samuel, sally and susan works fine.
Adding sally, samuel and susan does not.

The code sample says
if((*root)->left != NULL) {
*root = (*root)->left;
} else { ...
....
if((*root)->right != NULL) {
*root = (*root)->right;
} else {

It should say
if((*root)->left != NULL) {
root = &(*root)->left;
} else { ...
....
if((*root)->right != NULL) {
root = &(*root)->right;
} else {

Note from the Author or Editor:
On page 155 replace the insertNode function with the following:

void insertNode(TreeNode **realRoot, COMPARE compare, void* data) {
TreeNode *node = (TreeNode*) malloc(sizeof (TreeNode));
node->data = data;
node->left = NULL;
node->right = NULL;

TreeNode *root = *realRoot;

if (root == NULL) {
*realRoot = node;
return;
}

while (1) {
if (compare((root)->data, data) > 0) {
if ((root)->left != NULL) {
root = (root)->left;
} else {
(root)->left = node;
break;
}
} else {
if ((root)->right != NULL) {
root = (root)->right;
} else {
(root)->right = node;
break;
}
}
}
}

The previous version used the root variable when traversing the tree.Since the root variable and the "real root" of the tree passed to this function are the same, as the tree was traversed, the real root was also modified. The only time it should be modified is if the tree is empty.

The new version corrects this problem by adding a realRoot variable that is only modified if the tree is empty.

Toni Granholm  Aug 02, 2013  Apr 04, 2014
Printed
Page 168
6th paragraph

srtlcat
should be
strlcat

Note from the Author or Editor:
On page 168, sixth paragraph, change "srtlcat" to "strlcat".

Anonymous  Jul 03, 2013  Apr 04, 2014
Printed
Page 173
Using Static Analysis Tools 1st paragraph

The book claims that -Wall enables all gcc compiler warnings. This is actually wrong! There are more warnings to enable but all may not be useful. For example adding -Wextra will enable additional warnings that may be of value. For more info see man pages and/or http://stackoverflow.com/questions/11714827/how-to-turn-on-literally-all-of-gccs-warnings

Note from the Author or Editor:
Page 173
Using Static Analysis Tools Section
First paragraph

Change:

"For example, the GCC compiler's -Wall option enables the reporting of all compiler warnings."

To:

"For example, the GCC compiler's -Wall option enables the reporting of many but not all compiler warnings. Check the compiler's documentation for specific compiler options."

Oscar Utbult  May 25, 2014 
Printed
Page 173
Code right before the Using Static Analysis Tools section

Code from the book:

char *name = (char*)malloc(...);
...
memset(name,0,sizeof(name));

The above will only set sizeof(char*) bytes to 0 rather than the actual number of allocated bytes.

Note from the Author or Editor:
Page 173
Second code segment
Change:

char* name = (char*)malloc(...);
...
memset(name,0,sizeof(name));
free(name);

To:

int SIZEOFFIELD = ...;
char* name = (char*)malloc(SIZEOFFIELD);
...
memset(name,0,SIZEOFFIELD);
free(name);

Michael  Nov 02, 2014 
Printed
Page 181
code sample in note

int volatile ptr3 = #

The asterisk is missing from last pointer declaration. Hence, the correct is:

int volatile *ptr3 = #

Note from the Author or Editor:
Page 181
Code in the note section
The pointer declaration is incorrect.
Change:

int volatile ptr3 = &num;

To:

int volatile *ptr3 = &num;

Grzegorz Szpetkowski  Mar 15, 2015 
Printed
Page 187
code sample

It's a small typo. The following statements:

VectorInfo *vectorInfo = Product->info;
int beginningIndex = Product->beginningIndex;

should be written with product in lower-case as:

VectorInfo *vectorInfo = product->info;
int beginningIndex = product->beginningIndex;

Note from the Author or Editor:
Page 187
First code segment
The variable product is capitalized incorrectly. Change:

VectorInfo *vectorInfo = Product->info;
int beginningIndex = Product->beginningIndex;

To:

VectorInfo *vectorInfo = product->info;
int beginningIndex = product->beginningIndex;

Grzegorz Szpetkowski  Mar 16, 2015 
PDF
Page 187
4th line of 2nd paragraph

A typo in the 4th line of the 2nd paragraph on page 187:

‘mutext’ in “While the mutext ..” should be changed to ‘mutex’.

O'Reilly Media
 
Jul 21, 2016 
Printed
Page 188

in the pthread_create function (void *) repeated twice ;
should only show this once

Note from the Author or Editor:
On page 188 replace the following line:

dotProduct, (void *) (void*) (product));

with:

dotProduct, (void*) (product));

Adam Zaremba  Jun 03, 2013  Apr 04, 2014
PDF
Page 188
2nd code block

The line
double sum;

of code at the bottom of page 187 can be eliminated since that variable (i.e. sum) is never used in the program. On the other hand, however, the field 'sum' of the variable vectorInfo is not initialized (to 0). So the final part of the code should be

vectorInfo.vectorA = vectorA;vectorInfo.vectorB = vectorB;vectorInfo.sum = 0; //This line is missing in the original codevectorInfo.length = 4;

O'Reilly Media
 
Jul 21, 2016 
Printed
Page 191-193
code examples

comment on the code on page 191-192 and on the suggested corrections in the 'confirmed errata' list

On my system (Linux, gcc), the suggested corrections in the errata list, cause the following compiler warnings :
- for function 'addNode :
linkmain.c:22:2: warning: passing argument 2 of ?addNode? from incompatible pointer type [enabled by default]
link.h:5:6: note: expected ?void **? but argument is of type ?struct Person *?
- for function 'removeNode' :
linkmain.c:29:9: warning: assignment from incompatible pointer type [enabled by default]

In my opinion the corrections that are needed are :
- in the definition of struct _node : the declaration 'Data* data' should be 'Data data'
- in the function 'removeNode' : the declaration 'Data* data' should be 'Data data'

Indeed, the type 'Data' is a 'pointer to void' , so 'Data *' is a 'pointer to pointer to void' and that is not what we need here.

Nevertheless, on my system (Linux, gcc) ,the code in the book compiles without warnings and executes correctly.
I did some tests and found that the reason is because one can assign the value of a 'pointer to void' to a 'pointer to a pointer to void' and even to a 'pointer to a pointer to a pointer to void' without any warning or execution problem.

This is what happens in 'addNode' in the statement 'node->data = data;' where 'node->data' is of type 'Data *' (= void **) . When 'addNode' is called with a pointer to Person as second argument, the pointer to Person is implicitly converted to a pointer to void ,according to the type of the second formal parameter of addNode , which is of type 'Data' (= void *). The statement 'node->data = data;' then assigns the value to node->data (type void **).

In the function 'removeNode' , the type of 'data' in the return statement is of type 'Data *' ( = void **). However this type is implicitly converted to type 'Data' (= void *), according to the return type in the function header of removeNode.
Thus, in the main program, the result of the statement 'person = removeNode(list);' is that 'person' is implicitly converted to type 'void *' , which is again implicitly converted to type 'Person *' in the call of 'displayPerson' .This makes the program compile and execute correctly.



Note from the Author or Editor:
On page 192 the addNode method's first line should appear as:

void addNode(LinkedList* list, Data data) {

On page 193 the removeNode method's first line should appear as:

Data removeNode(LinkedList* list) {

The previous errata incorrectly changed the code.

Andre  Aug 17, 2013  Apr 04, 2014
Printed
Page 193
demonstation code sample

Both person instances are not freed, thus introducing a memory leak in the application. The suggested modification is to replace:

person = removeNode(list);
displayPerson(*person);

person = removeNode(list);
displayPerson(*person);

with:

person = removeNode(list);
displayPerson(*person);
free(person); // Potential memory leak!

person = removeNode(list);
displayPerson(*person);
free(person);

The reason is that removeNode by design does not free returned object. The comment is needed for exactly them same reason as in removeLinkedListInstance's body - Person struct might have pointer fields, that are allocated on the heap individually.

Note from the Author or Editor:
Page 193
Last code segment

The removeNode function will free up the memory allocated for the Node holding a Person. However, it does not and should not attempt to free up the data it holds. In this case, this is the Person.

It is the responsibility of the user to free up memory for the data. This is best achieved in this example by using the deallocatePerson function developed on page 138 similar to the following:

person = removeNode(list);
displayPerson(*person);
deallocatePerson(person);
free(person);

Grzegorz Szpetkowski  Mar 16, 2015 
PDF
Page 194
next to last paragraph

he next to last paragraph before the section Polymorphism in C on page 194: ‘inability’ in “The user’s inability...” should be changed to ‘ability’.

O'Reilly Media
 
Jul 21, 2016 
Printed
Page 195
1st paragraph

It's a small typo. The

The vFunction structure...

should be:

The vFunctions structure...

Note from the Author or Editor:
Page 195
First paragraph, first sentence
Change:

"The vFunction structure..."

To:

"The vFunctions structure..."

Grzegorz Szpetkowski  Mar 16, 2015 
PDF
Page 197
first code sample, rectangleSetY function

void rectangleSetY(Rectangle *rectangle, int y) {
rectangle->base.y;
}

change to

void rectangleSetY(Rectangle *rectangle, int y) {
rectangle->base.y = y;
}

Note from the Author or Editor:
Page 197
First code segment
Second function

Change:


void rectangleSetY(Rectangle *rectangle, int y) {
rectangle->base.y;
}

To:

void rectangleSetY(Rectangle *rectangle, int y) {
rectangle->base.y = y;
}

Devin  May 20, 2014 
Printed
Page 198

It might not be obvious to this book's typical reader that the polymorphism demonstrated on page 198 is successful only because the memory offset of the functions field is the same for both Shape and Rectangle. Apologies if this fact is mentioned and I have overlooked it.

Note from the Author or Editor:
It was hoped that the Figure 8-5 and the text immediately above it on page 196 would convey the idea that both the Shape and Rectangle structures' vFunctions must have the same offset. The text could be better worded as:

"The memory offset for the Rectangle structure's first field must be the same as the memory offset for the Shape structure's first field."

The word, offset, replaced the word, allocated in two places.

John Firth  Aug 31, 2014