Home > C++ > C++ Primer 学习笔记:数组和指针 (2)

C++ Primer 学习笔记:数组和指针 (2)

C++ Primer 学习笔记四:数组和指针 (2)

1. 指向const 对象的指针。

如果指针指向const对象,则不允许用指针来改变其所指的const值,C++ 语言强制要求指向const对象的指针也必须具有const特性。

const double *cptr; // cptr may point to a double that is const.

注意这里cptr本身并不是一个const,可以让它指向另一个double对象(可以不是const类型的),但是不能通过cptr来改变所指对象的值。

不能使用void* 指针保存const对象的地址,必须使用const void* 类型的指针保存const对象的地址:

const int universe = 42;
const void *cpv = &universe;

允许把非const对象的地址赋给指向const对象的指针,例如:

double dval = 3.14;
const double *cptr = &dval; // const double* 型的指针可以指向double类型对象。

*cptr = 1.0 // 会产生编译错误,通过它修改对象的值。
const double pi = 3.1415926

cptr = π // 可以给cptr 重新赋值
double cptr1 = π // 编译错误,必须用const double* 型的指针来指向 const double对象。

虽然dval不是const对象,但不允许通过cptr来修改其值。

不能使用指向const对象的指针修改基础对象,然而如果该指针指向的是一个非const对象,则可以用其它方法修改其所指的对象。

将const对象认为是“自以为指向const的指针”可以更好地理解这个概念。

2. const指针。

const指针代表指针本身的值不能修改,即不能修改它所指向的对象。但是指针本身是const的事实没有说明是否能使用该指针修改它所指向的对象的值:指针所指的对象的值能否修改完全取决于该对象的类型。

一般的声明方式是:

int errnum = 0;
int *const curerr = &errnum;

3. 指向const对象的const指针。

const double pi = 3.14159;
const double *const pi_ptr = π

表示:pi_ptr首先是一个const指针,其次它指向double型的const对象。

4.指针和typedef。

使用typedef 经常带来意外的结果,例如:

typedef string *pstring;
const pstring cstr;

因为typedef并不是文本扩展,在这里声明const string时,const修饰的是pstring类型,这是一个指针,因此该声明语句应该是把cstr定义为指向string类型对象的const指针。

所以它等价于下面的代码:

string *const cstr; // 而不是:const string* cstr;

用typedef写const类型定义时,const限定符加在类型名前面容易引起对所定义的真正类型的误解,下面三种声明意义都是相同的:

string s;
typedef string *pstring;
const pstring cstr1 = &s;
pstring const cstr2 = &s;
string *const cstr3 = &s;

习惯于将const置于前面,但是把声明语句重写为置const于类型之后更易于理解。

5. C风格字符串。

尽管C++支持C风格的字符串,但是不应该在C++程序中使用这个类型,因为C风格的字符串常带来许多错误,可能导致大量安全问题。

字符串字面值实际上就是const char类型的数组:以null结束的字符数组。

一般来说,在C++中,使用(const) char* 类型指针的算术操作符来遍历C风格的字符串:

const char *cp = “some value”

while( *cp ) {
      // do something
      ++ cp;
}

string.h 中的标准库函数不会检查其字符串参数,但一般来说,传递给这些标准库函数例程的指针必须具有非零值。

在使用处理C风格字符串的标准库函数时,必须牢记:

  • 字符串必须以结束符null结束。
  • 调用时必须保证目标字符串具有足够的大小。
  • strlen返回的是字符串的长度,并不包含结束的空字符,因此必须加1以在动态分配时预留结束符的存储空间。
  • 如果必须使用C风格字符串,使用strncat和strncpy比strcat和strcpy函数更安全。

 

6. 动态数组

数组类型的变量有三个重要的限制:数组长度固定不变,在编译时必须知道其长度,数组只能在定义它的块语句内存在。

动态分配的地址克服了上述缺点,而且直到程序显式地释放它时才会消失。每一个程序在执行时都占用一块可用的内存空间,用于存放动态分配的对象,此内存空间称为程序的自由存储区或者堆。C语言中使用malloc和free在自由存储区中分配空间,而C++ 则使用new和delete表达式来实现相同的功能。

定义动态数组中需指定类型和数组长度,不必为数组对象命名,new表达式返回指向新分配数组的第一个元素的指针:

int *pia = new int[10];

动态分配数组时,如果数组元素具有类类型,将使用该类的默认构造函数实现初始化;如果数组元素是内置类型,则无初始化:

string *psa = new string[10]; // 10个空的字符串
int *pia = new int[10]; // 数组元素的值未初始化

可以使用跟在数组长度后面的一对空圆括号,对数组做值的初始化:

int *pia2 = new int[10](); // 数组元素的值初始化为0

对于动态分配的数组,其元素只能初始化为元素类型的默认值,而不能像数组变量一样,用初始化列表为数组元素提供各不相同的初始值。

可以编写如下的代码:

size_t n = get_size(); // 如果 n = 0也同样正确执行,但不能进行解引用操作。
int *p = new int[10];
for (int *q = p; q!= p+n; ++q)
       /* precess the array */

释放内存空间应当使用:

delete [] pia; // 如果遗忘空方括号将导致内存泄漏问题

7. 与C代码的兼容。

可以使用C风格字符串对string对象进行赋值操作。

但是不能用string对象初始化字符指针,而应当使用string的c_str方法:

char *str = st2.c_str(); // almost ok, but not quite
const char *str = st2.c_str(); // ok

这是因为c_str返回的是指向const char类型的数组,所以要用const char*来定义。并且,c_str() 返回的数组并不保证一定是有效的,因为接下来st2的操作可能改变st2的值,使刚才返回的数组失效。如果程序需要持续地访问该数据,则应该复制c_str函数返回的数组。

C++ 允许用数组初始化vector对象,此时必须指出用于初始化的数组的第一个元素及最后一个元素的下一位置的地址:

const size_t arr_size = 6;
int int_arr[arr_size] = { 0, 1, 2, 3,4,5 };
vector <int> ivec(int_arr, int_arr + arr_size);
vector <int> ivec2(int_arr+1, int_arr + 4); // 复制的是arr[1] 到arr[3] 元素的值

8. 多维数组

关于初始化要注意的问题:

int ia[3][4] = { { 0 } , { 4 } , { 8 } }; // 其余元素不确定
int ia[3][4] = { 0, 3, 6, 9 }; // 其余元素均初始化为0

与普通数组一样,实际上将其自动转换为指向该数组的第一个元素的指针,并且要注意,指针所指向的是数组的数组。

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

用typedef可以简化指向多维数组的指针,如:

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

可以使用typedef 类型输出ia的元素:

for ( int_array *p = ia; p != ia + 3; ++p)
        for( int *q = *p; q!= *p +4; ++q)
              cout<< *q <<endl;

Related posts:

  1. C++ Primer 学习笔记:数组和指针 (1)
  2. C++ Primer 学习笔记:变量和类型
  3. APUE 中的 ourhdr.h 头文件
  4. C++ Primer 学习笔记:标准库类型
Categories: C++ Tags:
  1. No comments yet.
  1. No trackbacks yet.