c++ primer 第四章 数组和指针

数组

数组的定义和初始化

  • 数组的维数只能包含整型字面值常量、枚举常量(第 2.7 节)或者用常量表达式初始化的整型 const 对象
  • 非 const 变量以及要到运行阶段才知道其值的 const变量都不能用于定义数组的维数
    例子:
const unsigned buf_size = 512, max_files = 20;
int staff_size = 27;		// nonconst
const unsigned sz = get_size(); // const value not known until run time

char input_buffer[buf_size];		// ok: const variable
string fileTable[max_files + 1]; 	// ok: constant expression;常量表达式

double salaries[staff_size];		// error: non const variable
int test_scores[get_size()];		// error: non const expression
int vals[sz];                       // until run time

初始化数组元素

  • 在定义数组时,可为其元素提供一组用逗号分隔的初值,这些初值用花括号{}括起来,称为初始化列表,
int ia[3] = {0, 1, 2};
  • 如果没有显式提供元素初值:
  1. 在函数体外定义的内置数组,其元素均初始化为 0
  2. 在函数体内定义的内置数组,其元素无初始化
  3. 不管数组在哪里定义,如果其元素为类类型,则自动调用该类的默认构造函数进行初始化;如果该类没有默认构造函数,则必须为该数组的元素提供显式初始化
  • 如果维数大于列出的元素初值个数,则只初始化前面的数组元素;剩下的其他元素,若是内置类型则初始化为 0,若是类类型则调用该类的默认构造函数进行初始化
  • 字符数组既可以用一组由花括号括起来、逗号隔开的字符字面值进行初始化,也可以用一个字符串字面值进行初始化;
  • 要注意这两种初始化形式并不完全相同,字符串字面值包含一个额外的空字符(null)用于结束字符串
char ca1[] = {'C', '+', '+'};			// no null
char ca2[] = {'C', '+', '+', '\0'};		// explicit null
char ca3[] = "C++";						// null terminator added automatically
  • ca1 的维数是 3,而 ca2 和 ca3 的维数则是 4
  • 注意:与 vector 不同,一个数组不能用另外一个数组初始化,也不能将一个数组赋值给另一个数组

指针

  • 指针的概念很简单:指针用于指向对象,具体来说,指针保存的是另一个对象的地址
  • 每个指针都有一个与之关联的数据类型,该数据类型决定了指针所指向的对象的类型。例如,一个 int 型指针只能指向 int 型对象

指针变量的定义

vector<int> *pvec;		// pvec can point to a vector<int>
int *ip1, *ip2; 		// ip1 and ip2 can point to an int
double dp, *dp2;   		// dp2 is a ponter, dp is an object: both type double

指针可能的取值

  • 一个有效的指针必然是以下三种状态之一:保存一个特定对象的地址;指向某个对象后面的另一对象;或者是 0 值
  • 若指针保存 0 值,表明它不指向任何对象
int ival = 1024;
int *pi = 0;			// pi initialized to address no object
int *pi2 = & ival; 		// pi2 initialized to address of ival

int *pi3;				// ok, but dangerous, pi3 is uninitialized
pi = pi2;				// pi and pi2 address the same object, e.g.ival
pi2 = 0;				// pi2 now addresses no object
  • 避免使用未初始化的指针
  • 使用未初始化的指针相当于操纵这个不确定地址中存储的基础数据,引起程序崩溃
  • 如果必须分开定义指针和其所指向的对象,则将指针初始化为 0。因为编译器可检测出 0 值的指针,程序可判断该指针并未指向一个对象

void* 指针

  • C++ 提供了一种特殊的指针类型 void*,它可以保存任何类型对象的地址:
double obj = 3.14;
double *pd = &obj;		// ok: void* can hold the address value of any data pointer type
void *pv = &obj;		// obj can be an object of any type
pv = pd;				// pd can be a pointer to any type
  • void* 表明该指针与一地址值相关,但不清楚存储在此地址上的对象的类型
  • void* 指针只支持几种有限的操作:
  • 与另一个指针进行比较;
  • 向函数传递void* 指针或从函数返回 void* 指针;
  • 给另一个 void* 指针赋值
  • 不允许使用void* 指针操纵它所指向的对象

指针操作

  • 解引用可访问它所指的对象,*sp;
  • 修改指针所指对象的值,*sp = "goodbye";
  • 修改指针 sp 本身的值,使 sp 指向另外一个新对象:sp = &s2;

指针和引用的比较

  • 引用总是指向某个对象:定义引用时没有初始化是错误的,只是一个别名
  • 引用一经初始化,就始终指向同一个特定对象
  • 给引用赋值修改的是该引用所关联的对象的值,而指针是使其与另一个对象关联

指向指针的指针

  • 指针本身也是可用指针指向的内存对象:
int ival = 1024;
int *pi = &ival; 		// pi points to an int
int **ppi = &pi; 		// ppi points to a pointer to int

在这里插入图片描述

int *pi2 = *ppi; 		// ppi points to a pointer
int value = **ppi;

注意:对**ppi解引用,先得到指针(int *),第二次解引用才得到具体数值

使用指针访问数组元素

int ia[] = {0,2,4,6,8};
int *ip = ia; 	// ip points to ia[0]
  • 数组名就是数组中第一个元素的地址

指针的算术操作

  • 在指针上加上(或减去)一个整型数值 n 等效于获得一个新指针,该新指针指向指针原来指向的元素之后(或之前)的第 n 个元素
  • 注意:在不同类别下,指针加1或减1,移动的位数是不同的。int类型,将移动4位,char类型,只会移动1位
  • 只要两个指针指向同一数组或有一个指向该数组末端的下一单元,C++ 还支持对这两个指针做减法操作:
ptrdiff_t n = ip2 - ip; // ok: distance between the pointers
  • 结果是 4,这两个指针所指向的元素间隔为 4 个对象
  • 结果是标准库类型(library type)ptrdiff_t 的数据,与 size_t 类型一样,ptrdiff_t也是一种与机器相关的类型,在 cstddef 头文件中定义。
  • size_t 是unsigned 类型,而 ptrdiff_t 则是 signed 整型

计算数组的超出末端指针

const size_t arr_size = 5;
int arr[arr_size] = {1,2,3,4,5};
int *p = arr;				// ok: p points to arr[0]
int *p2 = p + arr_size; 	// ok: p2 points one past the end of arr

C++ 允许计算数组或对象的超出末端的地址,但不允许对此地址进行解引用操作

遍历数组:

  1. 已知数组长度
const size_t arr_sz = 5;
int int_arr[arr_sz] = { 0, 1, 2, 3, 4 };	
// pbegin points to first element, pend points just after the last

for (int *pbegin = int_arr, *pend = int_arr + arr_sz;
pbegin != pend; ++pbegin)
	cout << *pbegin << ' '; 	// print the current element
  1. 数组长度未知(字符数组)
char a[] = "hello world";
for(int i = 0;a[i] != '\0';++i)
   cout << a[i] << ' ';
  cout << endl;

指针和 const 限定符

指向 const 对象的指针
  • 如果指针指向const 对象,则不允许用指针来改变其所指的 const 值,但可以改变指针,使得指针指向其他元素(const 或非const):
const double *cptr; // cptr may point to a double that is const

const double pi = 3.14;
cptr = &pi;			//ok

double PI = 3.14;
cptr = &PI;  		//ok
PI = 3.1;			//ok
*cptr = 3.1;		//error, 不能通过指针修改值
  • 把一个 const 对象的地址赋给一个普通的、非 const 对象的指针也会导致编译时的错误:
const double pi = 3.14;
double *ptr = &pi;			// error: ptr is a plain pointer
const double *cptr = &pi; 	// ok: cptr is a pointer to const
const 指针
  • 本身的值不能修改,但其指向的元素的值可以改变
int errNumb = 0;
int *const curErr = &errNumb; 	// curErr is a constant pointer

*curErr = 0; // ok: reset value of the object to which curErr is bound

int nextNumb = 1;
curErr = &nextNumb;  // error, can't change

指针和 typedef

typedef string *pstring;
const pstring cstr;
  • cstr是什么类型?
  1. 第一行,typedefstring * 定义为pstring,那么是否意味着有:
const string *cstr

故而,cstr代表了指向const string对象的指针,这是错误的!!!
2. 需要明确的是,const修饰的是pstring(指针),因此应为:

string const *cstr

typedef不是简单的文本替换!!!

C 风格字符串

在这里插入图片描述

  • 头文件:#include <cstring>
  • 注意:
  • strlen是通过字符串结束符null来判断长度的,若字符串没有null,该函数不能正常工作
  • 传递给标准库函数 strcat 和 strcpy 的第一个实参数组必须具有足够大的空间存放新生成的字符串

创建动态数组

动态数组的定义

  • 动态分配数组时,只需指定类型数组长度,不必为数组对象命名,new 表达式返回指向新分配数组的第一个元素的指针
int *pia = new int[10];

int n;
cin >> n;
int *p = new int[n]; //ok
  • 此 new 表达式分配了一个含有 10 个 int 型元素的数组,并返回指向该数组第一个元素的指针,此返回值初始化了指针 pia
  • new 表达式需要指定指针类型以及在方括号中给出的数组维数,该维数可以是任意的复杂表达式
  • C++ 虽然不允许定义长度为 0 的数组变量,但明确指出,调用 new 动态创建长度为 0 的数组是合法的

初始化

  • 态分配数组时,如果数组元素具有类类型,将使用该类的默认构造函数)实现初始化;如果数组元素是内置类型,则无初始化
  • 也可使用跟在数组长度后面的一对空圆括号,对数组元素做值初始化
string *S = new string[10];  //默认构造函数初始化
int *P = new int[10];			//无初始化

int *p = new int[10]();			//有初始化,全为0

const 对象的动态数组

如果我们在自由存储区中创建的数组存储了内置类型的 const 对象,则必须为这个数组提供初始化:因为数组元素都是 const 对象,无法赋值

// error: uninitialized const array
const int *pci_bad = new const int[100];
// ok: value-initialized const array
const int *pci_ok = new const int[100]();

动态空间的释放

delete [] pia;

string和c语言风格字符串

  • 要求 C 风格字符串的地方不可直接使用标准库 string 类型对象
  • 例如:无法使用 string 对象初始化字符指针
char *str = st2; // compile-time type error

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

使用数组初始化 vector 对象

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);

传递给 ivec 的两个指针标出了 vector 初值的范围。第二个指针指向被复制的最后一个元素之后的地址空间

指针和多维数组

  • 定义指向多维数组的指针时,千万别忘了该指针所指向的多维数组其实是数组的数组
int ia[3][4];			// array of size 3, each element is an array of ints of size 4

int (*ip)[4] = ia; 		// ip points to an array of 4 ints(指向含有4个int的数组)
ip = &ia[2];			// ia[2] is an array of 4 ints

注意:括号一定不能少!!!

int *ip[4];

int (*ip)[4];

第一个代表,ip是一个含有4个元素的数组,且这四个元素都是整数指针
第二个代表,ip是一个指针,指向一个含有4个整数的数组

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值