!!!Chapter 4 Arrays and Pointers

4.1 Arrays

Modern C++ programs should almost always use vectors and iterators in preference to the low-level arrays and pointers.

Arrays are data structures that are similar to library vectors but are built into the language. 

Arrays have significant drawbacks:

1. They are fixed size, you cannot use push_back to automatically add elements

2. There is no size operation to keep track of the size of an array.

An array is a compound type that consists of a type specifier, an identifier, and a dimension.The dimension must be a constant expression:

const buf_size = 512;
int staff_size = 24;
const unsigned sz = get_size();        //const value not known until run time
int a[buf_size];                       //ok
int b[staff_size];                     //error: staff_size is nonconst variable
int c[sz];                             //error: size not known until run time
int c[buf_size + 1];                   //ok

4.1.1 Defining and Initializing Arrays

Array are initialized by the following rule:

a. If we don't provide element initialize:

      1. Elements of an array of built-in type defined outside the body of a function are initialized to zero.

      2. Elements of an array of built-in type defined inside the body of a function are uninitialized.

      3. If the array holds elements of class type, the elements are initialized by default constructor. If there is no default constructor, then the elements must be explicitly initialized.

b. If we explicitly initialized array:

      1. If dimension size is greater than listed elements, the initializers are used for the first elements, other elements are same as condition a. 

      2. The number of elements provided cannot exceed the size!

An explicitly initialized array need not specify a dimension value:

int ia[] = {0,1,2};   //an array of dimension 3

We need to pay attention to null when initialize character arrays:

char ca1 = { 'c', '+'};           //dimension is 2, no null
char ca2 =  {'c', '+', '\n'};     //dimension is 3, explicit null
char ca3 = {"c+"};                //dimension is 3, automatic null
char ca4[2]={"c+"};               //error, "c+" are three elements

There is no array copy or assignment

int ia[] = {1, 2};
int ia2[](ia);            //error: cannot initialize one array with another
int ia3[2];
ia3 = ia;                 //error: cannot assign one array to another

When we subscript an array, the right type to use for the index is size_t:

const size_t array_size = 10;
int ia[array_size];

4.2 Introducing Pointers

4.2.2 Defining and Initializing Pointers

A pointer points at an object. When we dereference a pointer, we obtain the object to which the pointer points.

Specifically, a pointer holds the address of another object:

string s("hello world");
string *sp = &s;              //sp holds the address of s

The * indicates that sp is a pointer; the & operator is the address-of operator. address-of operator can only be applied to lvalue.

There are two different pointer declaration styles:

string *ps1;        //ps1 is a pointer
string* ps2;        //ps2 is a pointer
string* ps3, ps4;   //ps3 is a pointer, ps4 is string
string* ps5, *ps6;  //ps5 is a pointer, ps6 is also a pointer

A valid pointer has three possible values (An uninitialized pointer is invalid!):

1. hold the address of a specific object

2. point one past the end of an object

3. it can be zero, which points to no object.

int ival = 1024;
int *p1 = 0;           //pi initialized to address no object
int *p2 = &ival;       //p2 initialized to address of ival
int *p3;               //p3 is uninitialized, it's dangerous

A Void type pointer can be used to hold an address of any object, but there are only a limited number of actions we can perform on a void* pointer.

Difference between pointers and references:

1. reference always refers to an object: it is an error to define a reference without initializing it.

2. The assignment behavior is different: Assigning to a reference changes the object to which the reference is bound; it does not rebind the reference to another object. Once initialized, a reference always refers to the same underlying object.

Pointers to pointers:

int ival = 1024;
int *p1 = &ival;
int **p2 = &p1;      //p2 points to a pointer to int
int *p3 = *p2;       //dereference p2 yield the object to which p2 points
int p4 = **p2;       //dereference twice to access ival

4.2.4 Using Pointers to Access Array Elements

When we use the name of an array in an expression, the name is automatically converted into a pointer to the first element of the array:

int ia[] = {0, 2, 3, 4, 5};
int *ip1 = ia;          //ip1 points to ia[0]
int *ip2 = &ia[4];      //ip2 points to ia[4], we can use subscripting operator to find the address of certain element 
int *ip3 = ip1 + 4;     //ip3 points to is[4], We can use pointer arithmetic to get certain address.
ptrdiff_t diff = ip3 - ip1;    // we can subtract two pointers as long as they point into same array

Pointer arithmetic is legal only if the original pointer and the newly calculated pointer address elements of the same array or an element one past the end of that array.

ptrdiff_t is defined in cstddefheader, it is used to store the difference of two pointers while size_t is used to store the size of array.

We can dereference a pointer expression:

int last = *(ia +4);    //last is ia[4]
int last = *ia + 4;     //last is ia[0] + 4

We can use the subscript operator on any pointer, as long as that pointer points to an element in an array:

int *p = &ia[2];           
int j = p[1];           //p[1] equals to *(p+1), which is ia[3]
int k = p[-2];          //equals to *(p-2), which is ia[0] 
It is legal to compute an address one (only one!) past the end of an array, but it's illegal to dereference that address:

int arr[3] = {1, 2, 3};
int *p = arr;
int *p2 = p + 3;      //ok, p2 points one past the end of arr. But you cannot dereference p2

We can define multiple variables inside the init-statement of a for loop, as long as the variables are defined using the same type.

4.2.5 Pointers and the const Qualifier

Pointers to const value:

const double *p1;         //const qualifies the type of object to which p1 points, not p1 itself.
const double pi = 3.14;
double *p2 = &pi          //error: p2 is a plain pointer
p1 = &pi                   //ok
*p1 = 3.2;                //error: *p1 is const

We need to use const void* to hold the address of a const object.

A pointer to a const object can be assigned the address of a nonconst object, but we cannot change the object through the pointer (we can still change the variable by other method: another pointer or the assign the variable itself):

double dval = 3.2;
const double *p1 = &dval;  //ok
*p1 = 3.5;                 //error, cannot change dval through p1

In real-world programs, pointers to const occur most often as formal parameters of function.

We can also have const pointer, whose own value is const:

int a = 0;
int *const ptr_a = &a;   //ptr_a is a constant pointer

We cannot assign to a constant pointer. And we must initialize a const pointer when we create it.

Const pointer to a const object:

const double pi = 3.14;
const double *const pi_ptr = π //pi_ptr is const and points to a const object

Typedef problem:

1. const can go either before or after the type:

string const s1;       //s1 and s2 have same type,
const string s2;       //they're both strings that are const

2. When writing const definition using typedef, we must figure out what is modified by const:

typedef string *pstring;     //pstring is a pointer, which points to string object
const pstring a = &s;        //const is modifies the type of pstring, which is a pointer
pstring const b = &s;
string *const c = &s;

a, b, c have the same type: they are all const pointers to string.

4.3 C-Style Character Strings

Although C++ supports C-style strings, they should not be used by C++ programs.

C-Style strings are null-terminated arrays of characters:

C-style strings are created by character array or character pointers while c++ strings are defined by string type.

char ca1[]= { 'c', '+', '+'};        //not null terminated. Not C-style string
char ca2[]= { 'c', '+', '+', '\0'};  //C-style
char ca3[]= "C++";                   //C-style
const char *cp = "C++";              //C-style
char *cp1 = ca1;                     //points to ca1. Not C-style string
char *cp2 = ca2;                     //C-style

C-style strings are manipulated through (const) char * pointers. One frequent usage pattern uses pointer arithmetic to traverse the C-style string:

const char *cp = "something";
while (*cp){                         //the loop will terminate when encounter null
//do something
++cp;
}

ps: if the above string does not contain null, the loop will fail!

C Library String Functions           P132

To use C library string functions, we must include the C header:

#include <cstring>     //this is C library string header
#include <string>      //this is C++ library string header

4.3.1 Dynamically Allocating Arrays

Limitation of normal array:

1. its size is fixed

2. the size must be known at compile time

3. the array exists only until the end of the block in which it was defined

For dynamically allocated arrays, we can determine the size at run time. And the array will continue to exist until it is explicitly freed by the program.

Every program has a pool of available memory it can use during program execution to hold dynamically allocated objects. The pool is referred to asfree store or heap.

C program use malloc and free to allocate space from free store. C++ usenew anddelete to do that.

When we dynamically allocate an array, we specify the type and size and return a pointer, which points to the first element. (normal array need to specify type, size and name):

int *pia = new int[10];   //array of 10 uninitialized ints

When we allocate array of class type, the type's default constructor is used to initialize each element. If the array is of built-in type, then the elements are uninitialized:

string *paa = new string[10];        //array of 10 empty strings
int *pia = new int[10];              //array of 10 uninitialized ints

We can value-initialize elements by following the array size by an empty pair of parentheses(we can only value-initialize, cannot initialize with separate value for each element):

int *pib = new int[10]();           //array of 10 ints, the value is 0

Value-initialize is the only way to initialize const objects of built-in type (class type may have default constructor):

//error: uninitialized const array
const int *pci_bad = new const int [100];       //we also need to add const in the right side
//ok: value-initialized
const int *pci_ok = new const int[100]();
//ok: default constructor make empty string
const string *pcs = new const string[100];

It is legal to dynamically allocate an empty array:

char arr[0];                //error: cannot define zero-length array
char *cp = new char[0];     //ok; but cp cannot be dereferenced

We apply delete [ ] to a pointer, which address the array, to release memory.

The empty bracket pair between the delete keyword and the pointer is necessary: It indicates to the compiler that the pointer addresses an array of elements on the free store and not simply a single object:

delete [] pia;

4.3.2 Interfacing to Old Code 

We can using an array to initialize a vector:

To initialize a vector from an array, we specify the address of the first element and one past the last element that we wish to use as initializers:

const size_t arr_size = 6;
int int_arr[arr_size] = {0,1,2,3,4,5};
//ivec has 6 elements each a copy of the corresponding element in int_arr
vector<int> ivec(int_arr, int_arr+arr_size);
//ivec2 has 3 elements from int_arr[1] till int_arr[3]
vector<int> ivec2(int_arr[1], int_arr +4);

4.4 Multidimensioned Arrays

In C++, we could have an array whose elements are arrays of elements that are arrays, and so on (which means we could have multiple dimensions). For two dimensioned arrays, the first dimension is often referred to as row and the second as the column.

When initialize multidimensioned arrays, we should specifying bracketed values for each row:

int ia[3][4] = {      
{0,1,2,3},           //initialize row 0
{4,5,6,7},           //initialize row 1
{8,9,10,11}          //initialize row 2
};

The above code is equal to:

int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};

We can also initialize part of the arrays:

int ia[3][4] = {{0}, {4}, {8}};   //initialize the first element of each row
int ia[3][4] = {0,4,8,7};         //initialize the first row only

To fetch data of inside multidimention arrays, we must provide a subscript for each dimension:

ia[2][3];       //this is a element
is[2];          //it fetches the array that is the last row in ia

Because a multidimensioned array is really an array of arrays, the pointer type to which the array converts is a pointer to the first inner array:

int is[3][4];
int (*ip)[4] = ia;    //ip points to an array of 4 elements
ip = &ia[2];
int *ipp[4];          //array of pointers to int

We can use typedef to simplify the process:

typedef int int_array[4];
int_array *ip = ia;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值