数组与指针
与vertor类型相比,数组的显著缺陷在于:数组的长度是固定的,没有获取其容量大小的size操作,也不提供push_back操作在其中自动添加元素。更改长度只能创建一个更大的数组。指针则可以像迭代器一样用于遍历和检查数组中的元素。
现在C++程序应尽量使用vector和迭代器类型,避免使用低级的数组和指针。程序只有在强调速度时才在类实现的内部使用数组和指针。
1.数组的定义和初始化
数组的维度必须在一对方括号[ ]内指定,用值大于等于1的常量表达式定义。非const常量以及到运行阶段才知道值的const变量不能用于定义数组维度。
int staff_size=27;
const unsigned sz=get_size;
double salaries[staff_size];//error:non const variable
int test_scores[get_size];//error:non const expression
int vals[sz];//error:size not known until run time
显式初始化数组元素
const unsigned array_size=3;
int ia[arrar_size]={0,1,2}
特殊的字符数组
字符数组用字符串字面值初始化包含一个额外的空字符null用于结束字符串。
char ca1[]={'c','+','+'};//no null维度3
char ca2[]={'c','+','+','\0'}; //explict null维度4
char ca2[]="c++"; //自动加入空字符,维度4
数组不能直接复制和赋值
2.数组操作
数组下标的正确类型是size_t,vector使用vector::size_type作为下表的类型。
3.指针的定义和初始化
指针用于指向对象,使用*符号把一个标识符声明为指针,&符号是取地址操作符。
string s("hello world");
string *p=&s;
可用空格将符号*与其后的标识符分隔开。
string* ps;
有效指针的三种状态:
a.保存一个特定对象的地址b.指向某个对象后面的另一个对象c.0值
int ival=1024;
int *pi=0;
int *pi2=&ival;//a
int *pi3;//未初始化,危险
pi=pi2;//b
pi2=0; //c
避免使用未初始化的指针。
对指针初始化或赋值只能使用四种类型的值:
a.0值常量表达式b.类型匹配的对象的地址c.另一个对象的下一个地址d.同类型的另一个有效指针
把指针初始化为NULL等效于初始化为0值。
void*可以保存任何类型的对象的地址。
double obj=3.14;
double *pd=&obj;
void *pv=&obj;
pv=pd;
3.指针操作
给指针直接赋值即可修改指针的值。
string s("hello world");
string *sp=&s;
*sp="goodbye"; //解引用操作符返回指定对象的值,可修改指针所指对象的值
string s2="some value";
sp=&s2;
指针和引用的区别:一是引用总是指向某个对象,即定义引用时没有初始化是错误的。二是给引用赋值修改的是该引用所关联的对象的值。
int ival=1024,ival2=2048;
int *pi=ival,*pi2=ival2;
pi=pi2;//pi所指向的ival对象值保持不变,修改了pi指针的值
int &ri=ival,&ri2=ival2;
ri=ri2;//修改了ri引用的值ival对象,并非引用本身
**操作符指派一个指针指向另一指针
int ival=1024;
int *pi=&ival;
int **ppi=π
4.使用指针访问数组元素
在表达式中使用数组时,会子的那个转换为指向数组的第一个元素的指针。
在指针加上或减去一个整型数值n等效于获取一个新指针。
两个指针相减的结果是标准库类型ptrdiff_t。可能是负数,所以是signed类型,而size_是unsigned类型。
解引用操作符的优先级高于加法操作符。
计算数组的超出末端指针
const size_t arr_size=5;
int arr[arr_size]={1,2,3,4,5];
int *p=arr;
int *p2=p+arr_size;//超出末端指针,只能用来与其他指针比较,不能进行解引用操作
5.指向const对象的指针
我们可以通过指针来修改其所指对象的值,但如果指针指向的是const对象,则不允许使用指针来改变其所指的const值。C++要求指向const对象的指针也是const类型。
const double *pd; //可以指向一个const double类型。
这里的const限定的是pd所指的对象,而不是pd。这里的pd是可以重新指向另外的一个地址的,但不允许使用pd来改变所指向对象的值。
把一个const对象的地址赋给非const对象的指针会导致编译时错误,例如:
const int a=8;
int *p=&a; //error
const int *cp=&a; //ok
不能使用void*指针保存const对象的地址,可以使用const void*类型的指针保存const类型的地址。
允许把非const对象的地址赋值给指向const对象的指针:
int c=3;
cp=&c;
虽然c不是const类型,但是使用cp修改c的值会导致编译错误。cp被定义为指向const对象的指针,那么cp就不能改变其所指对象的值。
指向const对象的指针所指的对象不一定是const对象,所以不能保证所指的对象的值不可更改,应为毕竟其所指的对象可以是非const对象,可以使用其他方法更改其值。
可以把指向const对象当做“自以为指向const对象的指针”来理解。
指向const对象的指针常用作函数的形参,这样可以保证在函数中不会应为形参改变实参所指的对象。
6.const指针
const指针是指该指针不可改变。
int num=4;
int *const p=#
可以把p理解为p是指向int类型的const指针。这就意味着不能使p指向其他对象,任何试图给p赋值的操作导致编译错误。const指针必须在编译时初始化。
该指针是const并不代表就不能修改其所指对象的值。如果const指针指向非const对象,那么就可以使用该指针修改所指对象的值,例如
int e=9;
int *const p=&e;
*p=6;
这样,e的值被修改为6.
还有一种指向const对象的const指针,如下使用:
const int d=4;
const int *const p=&d;
这样p的值不能修改,p指向对象的值也不能修改。
指向const对象的const指针
const double pi=3.14;
const double *const pi_str=π//不能修改pi_str所指向的对象的值,也不能修改指针的指向
typedef string *psstring;
const pstring sctr;//stcr的类型是string *const
7.C风格字符串
C++语言通过(const)char*类型的指针来操作C风格字符串。
<pre name="code" class="cpp">const char *cp="some value";
while(*cp)
{
++cp;
}
若所指的字符数组没有
null
结束符,则此循环失败。循环从
cp
指向的位置开始读数,知道遇到类存中某处为
null
结束符为止。
C语言标准库提供了一系列处理C风格字符串的库函数,要使用这些标准库函数,必须包含相应的C头文件
#include<cstring>
传递给这些标准库函数的指针必须有非零值,并且以null结束的字符数组中的第一个元素。并假设它们操作的字符串有足够大的空间接收本函数新生成的字符串。
<span style="font-size:14px;">srlen(s) 返回s的长度,不包含字符串结束符null
strcmp(s1,s2) 比较两个字符串s1和s2是否相同。若s1与s2相等,则返回0;若s1大于s2,则返回整数;若s1小于s2,则返回负数。
strcat(s1,s2) 将字符串s2连接到s1后,并返回s1
strcpy(s1,s2) 将s2复制给s1,并返回s1
strncat(s1,s2,n) 将s2的前n个字符连接到s1后面,并返回s1
strncpy(s1,s2,n) 将s2的前n个字符复制给s1,并返回s1 </span>
C++提供的<,>,=<,=>可以比较指向相同C风格字符串的指针,指向字符串后面的大于前面的,若两个指针指向不同的字符串,则没有定义。
尽可能的使用标准库类型string。
8.创建动态数组
int *p = new int[3]; //内置类型分配空间 无初始化
int *p = new int[4](); 值初始化为0, 但没有初始化值列表的形式
const int *p = new const int[22](); //最后的()是必须的,不然就没有初始化const元素 ,虽然vs2005通过
string *pstr = new string[4]; // 分配空间 使用默认构造函数初始化
const char *p4 = s2.c_str();//c_str返回c风格字符串,即返回指向字符数组首地址的指针
int arr[0]; // error,不能为0
int *parr = new int[0]; // ok, 可以为0,但是parr不能进行解引用操作
delete[] parr; // 漏掉[]编译器不会发现错,运行时错
使用数组初始化vector
in iarr[6] = {0,1,2,3,4,5};
vector<int> ivec(iarr+1, iarr+4); //[a,b),不包括iarr[4]
<span style="font-size:14px;"> vector<string> ivec;
string str;
while (cin >> str) { // 读入一组string放在vector中
ivec.push_back(str);
}
//下面是将vector这组string拷到一个字符指针数组中。
char **pa = new char*[ivec.size()];
size_t k = 0; // #include <cstddef>
for(vector<string>::iterator it = ivec.begin(); it != ivec.end(); ++it) {
char *p = new char[(*it).size() +1]; // 分配空间
strcpy(p, (*it).c_str()); //拷贝 #include <cstring>
pa[k++] = p;
}</span>
9.多维数组
<pre name="code" class="cpp"><span style="font-size:18px;">使用typedef简化多维数组</span>
int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};//等价于int ia[3][4]={{0,1,2,3},{4.5.6.7},{8,9,10,11}}
typedef int int_array[4];
int_array *p; // p也可以定义为 int (*p)[4];
for (p = ia; p != ia + 3; ++p;)
{
for(int *q = *p; q != *p+ 4; ++q)
{
cout<< *q << endl;
}
}