c陷阱与缺陷第三章——Semantic Pitfalls

3.1 Pointers and array

Array

Two things stand out about C arrays

  1. C has only one-dimensional arrays, and the size of an array must be fixed as a constant at compilation time.

    However, an element of an array may be an object of any type, including another array; this makes it possible to simulate multi-dimensional arrays.

  2. Only two things can be done to an array: determine its size and obtain a pointer to element 0 of the array.

    All other array operations are actually done with pointers, even if they are written with what look like subscripts.

    That is, every subscript operation is equivalent to a pointer operation, so it is possible to define the behavior of subscripts entirely in terms of the behavior of pointers.

a[i] == i[a]

见:c11 6.5.2.1 array subscripting p1 p2

Array subscripting

Constrains

[1] One of the expressions shall have type “pointer to complete object type”, the other expression shall have integer type, and the result has type “type”.

Semantics

[2] A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object.
 
The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).

Since a + i and i + a mean the same things, a[i] and i[a] also mean the same things.

#include<stdio.h>
int main(void)
{
	char a[5] = "abcd";
	printf("a[1] = %c, 1[a] = %c\n", a[1], 1[a]);
	return 0;
}

output:

a[1] = b, 1[a] = b

3.2 Pointers are not arrays

char *r;
char *s = "hello";
strcpy(r, s);

The above method doesn’t work because that r doesn’t point anywhere.

char r[100];
char *s = "hello";
strcpy(r,s);

This now works.

3.3 Array declarations as parameters

There is no way to pass an array to a function directly.

Using an array name as an argument immediately converts it to a pointer to the initial element of the array.

C automatically converts an array parameter declaration to the corresponding pointer declaration. In other words, writing

int strlen(char s[])
{
	/* stuff */
}

precisely equivalent to

int strlen(char *s)
{
	/*stuff*/
}

3.5 Null pointers are not null strings

Casting an integer to a pointer

The result of converting an integer to a pointer is implementation dependent, with one important exception.

That exception is the constant 0, which is guaranteed to be converted to a pointer that is unequal to any valid pointer.

For documentation, this value is often given symbolically:

#define NULL 0

but the effect is the same.

The important thing to remember about 0 when used as a pointer is that it must never be dereferenced.

In other words, when you have assigned 0 to a pointer variable, you must not ask what is in the memory it addresses.

It is valid to write:

if (p == (char *)0) ...

but it is not valid to write:

if (strcmp(p, (char *)0) == 0) ...

because strcmp always looks at the memory addresses by its arguments.

If p is a null pointer, even the effect of

printf("%s",p);

is undefined.

c11 description

c11 6.3.2.3 p3~p6

[3]An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.
 
If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

So, 0 is a null pointer constant, and the behavior (char *)0 converts a null pointer constant to a pointer type, thus the resulting pointer is a null pointer.

[4] Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null pointers shall compare equal.

[5] An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.

[6] Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.

Dereferencing a pointer

Pointer Rules
What does “dereferencing” a pointer mean?

Null pointer

Null Pointers
Section 1. Null Pointers

3.6 Counting and asymmetric bounds

Fencepost errors (off-by-one errors)

How many fence posts 10 feet apart do you need to support 100 feet of fence?

The obvious answer is to divide 100 by 10 to get 10, but of course this is wrong: the right answer is 11.

Another example:
How many integers x x are there with 16 x 37 16 \leq x \leq 37 ?

The obvious answer is 37 16 = 21 37 - 16 = 21 , but it is 22 22 .

Call the lower bound l l and upper bound h h . Thus the number of elements in the sequence is h l + 1 h - l + 1 .

Way to avoid this error: Express a range by the firtst element of range and the first element beyond it.

Hence, instead of talking about values of x x with 16 x 37 16 \leq x \leq 37 , talk instead about values with 16 x < 38 16 \leq x \lt 38 .

Using the inclusive lower bound and exclusive upper bound.

Dealing with buffers of various sorts

Considering a function whose job is to collect input of irregular length into blocks of N characters and write out a buffer-load when it becomes full.

The declaration for the buffer might look something like this:

#define N 1024
static char buffer[N];
static char *bufptr;

It may be tempting to establish that bufptr always points at the last occupied character in the buffer, but our preference for asymmetric bounds causes us to make bufptr represent the first free character in the buffer.

With that convention, we put a character c into the buffer by writing

*bufptr++ = c;

We initially say the buffer is empty by writing

bufptr = &buffer[0];
/*or
bufptr = buffer
*/

The number if characters int the buffer at any time is just bufptr - buffer.

So we can test if the buffer is full by if (bufptr - buffer) equals to N

Expression --n > 0

Iterating the expression --n >= 0 is one way of doing something n times.

Access an element that doesn’t exist

ANSI explicitly permits this usage: the address of the nonexistent element just past the end of an array may be taken and used for assignment and comparison purpose. Of course it is illegal to refer to that element.

3.7 Order of evaluation

Only the four C operators &&, ||, ? :, and , specify an order of evaluation. && and || evaluate the left operand first, and the right operand only if necessary. The , operator evaluate its left operand and discards its value, then evaluate its right operand.

发布了120 篇原创文章 · 获赞 2 · 访问量 5804

猜你喜欢

转载自blog.csdn.net/Lee567/article/details/103221880