c陷阱与缺陷第三章——Semantic Pitfalls
- 3.1 Pointers and array
- 3.2 Pointers are not arrays
- 3.3 Array declarations as parameters
- 3.5 Null pointers are not null strings
- 3.6 Counting and asymmetric bounds
- Fencepost errors (off-by-one errors)
- Dealing with buffers of various sorts
- Expression --n > 0
- Access an element that doesn't exist
- 3.7 Order of evaluation
3.1 Pointers and array
Array
Two things stand out about C arrays
-
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.
-
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).扫描二维码关注公众号,回复: 9791132 查看本文章
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
[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
are there with
?
The obvious answer is , but it is .
Call the lower bound and upper bound . Thus the number of elements in the sequence is .
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 with , talk instead about values with .
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.