5.1 Pointers
For a type T, *T is the type ‘‘pointer to T.’’ That is, a variable of type *Tcan hold the address of an object of type T. For example:
char c = ´a´;
char* p = &c;
or graphically:
Unfortunately, pointers to arrays and pointers to functions need a more complicated notation:
int* pi; // pointer to int
char** ppc; // pointer to pointer to char
int* ap[15]; // array of 15 pointers to ints
int (*fp)(char*); // pointer to function taking a char* argument; returns an int
int* f(char*); // function taking a char* argument; returns a pointer to int
See §4.9.1 for an explanation of the declaration syntax and Appendix A for the complete grammar. The fundamental operation on a pointer is dereferencing, that is, referring to the object pointed to by the pointer. This operation is also called indirection. The dereferencing operator is (prefix) unary *. For example:
char c = ´a´;
char* p = &c; // p holds the address of c
char c2 = *p; // c2 == ’a’
The variable pointed to by p is c, and the value stored in c is ´a´, so the value of *p assigned to c2 is ´a´.It is possible to perform some arithmetic operations on pointers to array elements (§5.3). Point to functions can be extremely useful; they are discussed in §7.7.
The implementation of pointers is intended to map directly to the addressing mechanisms of the machine on which the program runs. Most machines can address a byte. Those that can’t tend to have hardware to extract bytes from words. On the other hand, few machines can directly address an individual bit. Consequently, the smallest object that can be independently allocated and pointed to using a built-in pointer type is a char. Note that a bool occupies at least as much space as a char (§4.6). To store smaller values more compactly, you can use logical operations (§6.2.4) or bit fields in structures (§C.8.1).
5.1.1 Zero
Zero (0) is an int. Because of standard conversions (§C.6.2.3), 0 can be used as a constant of any integral (§4.1.1), floating-point, pointer, or pointer-to-member type. The type of zero will be deter-mined by context. Zero will typically (but not necessarily) be represented by the bit pattern all- zeros of the appropriate size.No object is allocated with the address 0. Consequently, 0 acts as a pointer literal, indicating that a pointer doesn’t refer to an object.
In C, it has been popular to define a macro NULL to represent the zero pointer. Because of C++’s tighter type checking, the use of plain 0, rather than any suggested NULL macro, leads to fewer problems. If you feel you must define NULL, use const int NULL = 0;
The const qualifier (§5.4) prevents accidental redefinition of NULL and ensures that NULL can be used where a constant is required.
5.1.2 the type of indicators and definition of indicators
With the type of pointer is that it is the type it points to the type of entity. Such as: a pointer to int type, a point one-dimensional array pointer, a pointer to a pointer type of function, such as.
Pointer variables in the use of a prior statement by a statement of its definition. Statements such as:
Int * p;
Defines a pointer to an integer type of variable p. That p is a variable storing the address of plastic variables. Figure 6.4 that the p and i in memory, the map image to the method of p and i said that the logic of the relationship between. Special note should be: the definition of a pointer variable must use the symbol "*", it indicates that the subsequent pointer variable is variable, but do not think "* p" is a pointer variable, pointer variable is p rather than * p.
In the definition of a pointer variable p, the system for this indicator variable assigned a storage unit (typically 2 bytes), use it to store addresses. But this time the pointer variable does not point to determine the plastic variables, because the indicator variables to determine the address did not enter. Is a pointer variable to point to another variable of plastic, plastic variables must be assigned to the pointer variable address. For example:
Int * p; i = 3;
p = &i;
Above defines a pointer variable p and a plastic variable i, i the initial value of 3. But at this point between p and i have no contact. And then the implementation of the assignment "p = &i;", this time on the point p value of i.
Indicator variables can also be defined as the point real, character, and other types of variables, such as:
float * p;
char * p;
5.2 Arrays
For a type T, T[size] is the type ‘‘array of size elements of type T.’’ The elements are indexed from 0 to size-1. For example:
float v[3]; // an array of three floats: v[0], v[1], v[2]
char* a[32]; // an array of 32 pointers to char: a[0] .. a[31
The number of elements of the array, the array bound, must be a constant expression (§C.5). If you need variable bounds, use a vector (§3.7.1, §16.3). For example:
void f(int i)
{
int v1[i]; // error: array size not a constant expression
vector<int> v2(i); // ok
}
Multidimensional arrays are represented as arrays of arrays. For example:int d2[10][20]; // d2 is an array of 10 arrays of 20 integers Using comma notation as used for array bounds in some other languages gives compile-time errors because comma (,) is a sequencing operator (§6.2.2) and is not allowed in constant expressions
(§C.5). For example, try this: int bad[5,2]; // error: comma not allowed in a constant expression
Multidimensional arrays are described in §C.7. They are best avoided outside low-level code.
5.2.1Array Initializers
An array can be initialized by a list of values. For example:
int v1[] = { 1, 2, 3, 4 };
char v2[] = { ´a´, ´b´, ´c´, 0 };
When an array is declared without a specific size, but with an initializer list, the size is calculated by counting the elements of the initializer list. Consequently, v1 and v2 are of type int[4] and char[4], respectively. If a size is explicitly specified, it is an error to give surplus elements in an initializer list. For example:
char v3[2] = { ´a´, ´b´, 0 }; // error: too many initializers char
v4[3] = { ´a´, ´b´, 0 }; // ok
If the initializer supplies too few elements, 0 is assumed for the remaining array elements. For example:
int v5[8] = { 1, 2, 3, 4 };
is equivalent to
int v5[] = { 1, 2, 3, 4 , 0, 0, 0, 0 };
Note that there is no array assignment to match the initialization:
void f()
{
v4 = { ´c´, ´d´, 0 }; // error: no array assignment
}
When you need such assignments, use a vector (§16.3) or a valarray (§22.4) instead. An array of characters can be conveniently initialized by a string literal (§5.2.2)
5.2.2 String Literals
A string literal is a character sequence enclosed within double quotes:"this is a string" A string literal contains one more character than it appears to have; it is terminated by the null char- acter ´/0´, with the value 0. For example:
sizeof("Bohr")==5
The type of a string literal is ‘‘array of the appropriate number of const characters,’’ so "Bohr" is of type const char[5].
A string literal can be assigned to a char*. This is allowed because in previous definitions of C and C++ , the type of a string literal was char*. Allowing the assignment of a string literal to a char* ensures that millions of lines of C and C++ remain valid. It is, however, an error to try to modify a string literal through such a pointer:
void f()
{
char* p = "Plato";
p[4] = ´e´; // error: assignment to const; result is undefined
}
This kind of error cannot in general be caught until run-time, and implementations differ in their enforcement of this rule. Having string literals constant not only is obvious, but also allows imple- mentations to do significant optimizations in the way string literals are stored and accessed.
If we want a string that we are guaranteed to be able to modify, we must copy the characters into an array:
void f()
{
char p[] = "Zeno"; // p is an array of 5 char
p[0] = ´R´; // ok
}
A string literal is statically allocated so that it is safe to return one from a function. For example:
const char* errormessage(int i)
{
// ...
return "range error";
}
The memory holding range error will not go away after a call of errormessage().Whether two identical character literals are allocated as one is implementation-defined (§C.1).For example:
const char* p = "Heraclitus";
const char* q = "Heraclitus";
void g()
{
if (p == q) cout << "one!/n"; // result is implementation-defined
// ...
}
Note that == compares addresses (pointer values) when applied to pointers, and not the values pointed to.
The empty string is written as a pair of adjacent double quotes, "", (and has the type const char[1]).
The backslash convention for representing nongraphic characters (§C.3.2) can also be used within a string. This makes it possible to represent the double quote (") and the escape character backslash ( /) within a string. The most common such character by far is the newline character,
´/n´. For example:
cout<<"beep at end of message/a/n";
The escape character ´/a´ is the ASCII character BEL (also known as alert), which causes some kind of sound to be emitted.It is not possible to have a ‘‘real’’ newline in a string:"this is not a string but a syntax error"
Long strings can be broken by whitespace to make the program text neater.
The compiler will concatenate adjacent strings, so alpha could equivalently have been initialized by the single string:
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
It is possible to have the null character in a string, but most programs will not suspect that there are characters after it. For example, the string "Jens/000Munk" will be treated as "Jens" by stan- dard library functions such as strcpy() and strlen(); see §20.4.1.
5.3 Pointers into Arrays
In C++, pointers and arrays are closely related. The name of an array can be used as a pointer to its initial element. For example:
int v[] = { 1, 2, 3, 4 };
int* p1 = v; // pointer to initial element (implicit conversion)
int* p2 = &v[0]; // pointer to initial element
int* p3 = &v[4]; // pointer to one beyond last element
or graphically:Taking a pointer to the element one beyond the end of an array is guaranteed to work. This is important for many algorithms (§2.7.2, §18.3). However, since such a pointer does not in fact point to an element of the array, it may not be used for reading or writing. The result of taking the address of the element before the initial element is undefined and should be avoided. On some machine architectures, arrays are often allocated on machine addressing boundaries, so ‘‘one before the initial element’’ simply doesn’t make sense.
The implicit conversion of an array name to a pointer to the initial element of the array is exten- sively used in function calls in C-style code. For example:
extern "C" int strlen(const char*); // from string.h
void f()
{
char v[] = "Annemarie";
char* p = v; // implicit conversion of char[] to char*
strlen(p);
strlen(v); // implicit conversion of char[] to char*
v = p; // error: cannot assign to array
}
The same value is passed to the standard library function strlen() in both calls. The snag is that it is impossible to avoid the implicit conversion. In other words, there is no way of declaring a function so that the array v is copied when the function is called. Fortunately, there is no implicit or explicit conversion from a pointer to an array.
The implicit conversion of the array argument to a pointer means that the size of the array is lost to the called function. However, the called function must somehow determine the size to perform a meaningful operation. Like other C standard library functions taking pointers to characters, strlen() relies on zero to indicate end-of-string; strlen(p) returns the number of characters up to and not including the terminating 0. This is all pretty low-level. The standard library vector (§16.3) and string (Chapter 20) don’t suffer from this problem.
5.3.1 Navigating Arrays
Efficient and elegant access to arrays (and similar data structures) is the key to many algorithms (see §3.8, Chapter 18). Access can be achieved either through a pointer to an array plus an index or through a pointer to an element. For example, traversing a character string using an index,
void fi(char v[])
{
for (int i = 0; v[i]!=0; i++) use(v[i]);
}
is equivalent to a traversal using a pointer:
void fp(char v[])
{
for (char* p = v; *p!=0; p++) use(*p);
}
The prefix * operator dereferences a pointer so that *p is the character pointed to by p,and ++ increments the pointer so that it refers to the next element of the array.
There is no inherent reason why one version should be faster than the other. With modern com- pilers, identical code should be generated for both examples (see §5.9[8]). Programmers can choose between the versions on logical and aesthetic grounds.
The result of applying the arithmetic operators +, -, ++, or -- to pointers depends on the type of the object pointed to. When an arithmetic operator is applied to a pointer p of type T*, p is assumed to point to an element of an array of objects of type T; p+1 points to the next element of that array, and p-1 points to the previous element. This implies that the integer value of p+1 will be sizeof(T) larger than the integer value of p. For example, executing
using a default hexadecimal notation for pointer values. This shows that on my implementation,sizeof(short) is 2 and sizeof(int) is 4.
Subtraction of pointers is defined only when both pointers point to elements of the same array(although the language has no fast way of ensuring that is the case). When subtracting one pointer from another, the result is the number of array elements between the two pointers (an integer). One can add an integer to a pointer or subtract an integer from a pointer; in both cases, the result is a pointer value. If that value does not point to an element of the same array as the original pointer or one beyond, the result of using that value is undefined. For example:
void f()
{
int v1[10];
int v2[10];
int i1 = &v1[5]-&v1[3]; // i1 = 2
int i2 = &v1[5]-&v2[3]; // result undefined
int* p1 = v2+2; // p1 = &v2[2]
int* p2 = v2-2; // *p2 undefined
}
Complicated pointer arithmetic is usually unnecessary and often best avoided. Addition of pointers makes no sense and is not allowed.Arrays are not self-describing because the number of elements of an array is not guaranteed to be stored with the array. This implies that to traverse an array that does not contain a terminator the way character strings do, we must somehow supply the number of elements. For example:
void fp(char v[], unsigned int size)
{
for (int i=0; i<size; i++) use(v[i]);
const int N = 7;
char v2[N];
for (int i=0; i<N; i++) use(v2[i]);
}
Note that most C++ implementations offer no range checking for arrays. This array concept is inherently low-level. A more advanced notion of arrays can be provided through the use of classes; see §3.7.1.
5.4 Constants
C++ offers the concept of a user-defined constant, a const, to express the notion that a value doesn’t change directly. This is useful in several contexts. For example, many objects don’t actually have their values changed after initialization, symbolic constants lead to more maintainable code than do literals embedded directly in code, pointers are often read through but never written through, and most function parameters are read but not written to.The keyword const can be added to the declaration of an object to make the object declared a constant. Because it cannot be assigned to, a constant must be initialized. For example:
const int model = 90; // model is a const const int v[] = { 1, 2, 3, 4 }; // v[i] is a const
const int x; // error: no initializer
Declaring something const ensures that its value will not change within its scope:
void f()
{
model = 200; // error
v[2]++; // error
}
Note that const modifies a type; that is, it restricts the ways in which an object can be used, rather than specifying how the constant is to be allocated. For example:
void g(const X* p)
{
// can’t modify *p here
}
void h()
{
X val; // val can be modified
g(&val);
// ...
}
Depending on how smart it is, a compiler can take advantage of an object being a constant in sev-
eral ways. For example, the initializer for a constant is often (but not always) a constant expression
(§C.5); if it is, it can be evaluated at compile time. Further, if the compiler knows every use of the const, it need not allocate space to hold it. For example:
const int c1 = 1;
const int c2 = 2;
const int c3 = myf(3); // don’t know the value of c3 at compile time
extern const int c4; // don’t know the value of c4 at compile time
const int* p = &c2; // need to allocate space for c2
Given this, the compiler knows the values of c1 and c2 so that they can be used in constant expres- sions. Because the values of c3 and c4 are not known at compile time (using only the information available in this compilation unit; see §9.1), storage must be allocated for c3 and c4. Because the address of c2 is taken (and presumably used somewhere), storage must be allocated for c2. The simple and common case is the one in which the value of the constant is known at compile time and no storage needs to be allocated; c1 is an example of that. The keyword extern indicates that c4 is defined elsewhere (§9.2).
It is typically necessary to allocate store for an array of constants because the compiler cannot,in general, figure out which elements of the array are referred to in expressions. On many machines, however, efficiency improvements can be achieved even in this case by placing arrays of constants in read-only storage.
Common uses for consts are as array bounds and case labels. For example: Enumerators (§4.8) are often an alternative to consts in such cases.The way const can be used with class member functions is discussed in §10.2.6 and §10.2.7. Symbolic constants should be used systematically to avoid ‘‘magic numbers’’ in code. If a numeric constant, such as an array bound, is repeated in code, it becomes hard to revise that code because every occurrence of that constant must be changed to make a correct update. Using a sym- bolic constant instead localizes information. Usually, a numeric constant represents an assumption about the program. For example, 4 may represent the number of bytes in an integer, 128 the num- ber of characters needed to buffer input, and 6.24 the exchange factor between Danish kroner and U.S. dollars. Left as numeric constants in the code, these values are hard for a maintainer to spot and understand. Often, such numeric values go unnoticed and become errors when a program is ported or when some other change violates the assumptions they represent. Representing assump- tions as well-commented symbolic constants minimizes such maintenance problems.
5.4.1 Pointer and the volume indicator
Address storage area as if looking for signs, in the programming language known as the pointer. Can also say that an address point to a program entity storage space. Usually through the variable name or address to visit a program storage means entities referred to as "direct access" approach (in fact, through the variable names is to visit the address to visit).
There is also a "indirect access" approach is to address a variable on another variable. Another unit in the memory to set up a number of variables: pa, pb, pc, pd, pe, pf, respectively, used to store variables a, b, c, d, e, f address. If you want a value, you can first visit the variables pa, to be pa of the value of 1010 (which the value of a variable), and then find it through the address of 1010 point to the value of the memory cell. That the address stored in a variable, and then through the first variable to identify the address of the value (an address), in this address to find the ultimate way to access variables, known as the "indirect access." Need to show that the variables stored address is a special variable, it can only be used to store addresses and can not be used to store other types of data need to be defined specifically.
Need to explain: "pointer" is the word that the image of the guidelines visit the relationship between variables, not in memory as the clock is really like a needle in the mobile. In fact, it only stored the address of a variable only.
5.4.2 Pointers and Constants
When using a pointer, two objects are involved: the pointer itself and the object pointed to. ‘‘Pre- fixing’’ a declaration of a pointer with const makes the object, but not the pointer, a constant. To declare a pointer itself, rather than the object pointed to, to be a constant, we use the declarator operator *const instead of plain *. For example:
void f1(char* p)
{
char s[] = "Gorm";
const char* pc = s; // pointer to constant
pc[3] = ´g´; // error: pc points to constant
pc = p; // ok
char *const cp = s; // constant pointer cp[3] = ´a´; // ok
cp = p; // error: cp is constant
const char *const cpc = s; // const pointer to const
cpc[3] = ´a´; // error: cpc points to constant
cpc = p; // error: cpc is constant
}
The declarator operator that makes a pointer constant is *const. There is no const* declarator operator, so a const appearing before the * is taken to be part of the base type. For example:
char *const cp; const pointer to char char const* pc; // pointer to const char const char* pc2; pointer to const char
Some people find it helpful to read such declarations right-to-left. For example, ‘‘cp is a const pointer to a char’’ and ‘‘pc2 is a pointer to a char const.’’
An object that is a constant when accessed through one pointer may be variable when accessed in other ways. This is particularly useful for function arguments. By declaring a pointer argument
const, the function is prohibited from modifying the object pointed to. For example:char* strcpy(char* p, const char* q); // cannot modify *q
You can assign the address of a variable to a pointer to constant because no harm can come from that. However, the address of a constant cannot be assigned to an unrestricted pointer because this would allow the object’s value to be changed. For example:
void f4()
{
int a = 1;
const int c = 2;
const int* p1 = &c; // ok
const int* p2 = &a; // ok
int* p3 = &c; // error: initialization of int* with const int*
*p3 = 7; // try to change the value of c
}
It is possible to explicitly remove the restrictions on a pointer to const by explicit type conversion(§10.2.7.1 and §15.4.2.1