随笔 - 56, 文章 - 0, 评论 - 0, 引用 - 0
数据加载中……

第四章 数组和指针


与vector类型相似,数组也可以保存某种类型的一组对象;而它们的区别在于,数组
的长度是固定的。数组一经创建,就不允许添加新的元素。
数组的定义和初始化
数组的维数必须用值大于等于1的常量表达式。此常量表达式只能包含整型字面值常量、
枚举常量或者有常量表达式初始化的整型const对象。非const变量以及要到运行阶段才
知道其值的const变量都不能用于定义数组的维数。
数组类型的变量有三个重要的限制:数组长度固定不变,在编译时必须知道其长度,数组只
在定义它的块语句内存在。

const unsigned buf_size=512,max_files=20;
int staff_size=27;
const unsigned sz=get_size();
char input_buffer[buf_size];                 //ok!const variable
string fileTable[max_files+1];               //ok:constant expression
double salarises[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


虽然staff_size是用字面值常量进行初始化,但staff_size本身是一个非const对象,只有在
运行时才能获得它的值,因此,使用该变量来定义数组维数是非法的。而对于sz,尽管它是
一个const对象,但它的值要到运行时调用get_size函数后才知道,因此不能定义数组维数。

◇ 在函数体外定义的内置数组,其元素均初始化为0;
◇ 在函数体内定义的内置数组,其元素无初始化;
◇ 不管数组在哪里定义,如果其元素为类类型,则自动调用该类的默认构造函数进行
   初始化;如果该类没有默认构造函数,则必须为该数组的元素提供显式初始化。
   显式初始化的数组不需要指定数组的维数值,编译器会根据列出的元素个数来确定长度
   int ia[]={0,1,2};
  
特殊的字符数组:

char ca1[]={'C','+','+'};                    //no null
char ca2[]={'C','+','+','\0'};               //explicit null
char ca3[]="C++";                            //null terminator added automatically

ca1的维数是3,而ca2和ca3的维数则是4。下面的初始化将导致编译时的错误:

const char ca3[6]="Daniel";                  //error:Daniel is 7 elements

用下标访问元素时,vector使用vector::size_type作为下标的类型,而数组下标的正确类型
则是size_t。

动态数组的定义
C语言程序使用一对标准库函数malloc和free在自由存储区中分配存储空间,而C++语言则使用
new和delete表达式实现相同的功能。
静态数组变量通过制定类型、数组名和维数来定义。而动态分配数组时,只需指定类型和数组
长度,不必为数组对象命名,new表达式返回指向新分配数组的第一个元素的指针:
int *pia=new int[10];                        //array of 10 uninitialized ints
此new表达式分配了一个含有10个int型元素的数组,并返回指向该数组第一个元素的指针,此
返回值初始化了指针pia。


指针的引用
string* ps1, ps2;                             //实际上只把ps1定义为指针,而ps2并非指针,
只是一个普通的string对象而已。如果需要在一个声明语句中定义为两个指针,必须在每个变量
标识符前加符号*声明:
string* ps1,*ps2;

一个有效的指针必然是以下三种状态之一:保存一个特定对象的地址;指向某个对象后面的另一个对象;
或者是0值。若指针保存0值,表明它不指向任何对象。未初始化的指针式无效的,直到给该指针
赋值后,才可使用它。下列定义和赋值都是合法的:

int ival=1024;
int *pi=0;                                   //pi initialized to address no object
int *pi2=&ival;
int *pi3;                                    //ok,but dangerous
pi=pi2;
pi2
=0;                                       //pi2 now addresses no object

避免使用未初始化的指针
如果可能的话,除非所指向的对象已经存在,否则不要先定义指针,这样可避免定义一个未初始化的指针。
如果必须分开定义指针和其所指向的对象,则将指针初始化为0.或者编译器可检测出0值的指针,程序
可判断该指针并未指向一个对象。
把int型变量赋给指针是非法的,尽管此int型变量的值可能为0,但允许把树枝0或在编译时获得0值的const量赋给指针:

int ival;
int zero=0;
const int c_ival=0;
int *pi=ival;   //error:pi initialized from int value of ival
pi=zero;        //error:pi assigned int value of zero
pi=c_ival;      //ok:c_ival is a const with compile-time value of 0
pi=0;           //ok:directly initialize to literal constant 0
pi=NULL;        //ok!


除了使用数值0或在编译时值为0的const量外,还可以使用C++语言从C语言中继承下来的预处理器变量
NULL,该变量在cstdlib头文件中定义,其值为0.

两个指针减法操作的结果是标准库类型ptrdiff_t的数据。与size_t类型一样,ptrdiff_t也是一种与机器相关的类型,
在cstddef头文件中定义。size_t是unsigned类型,而ptrdiff_t则是signed类型。
size_t类型用于指明数组长度,它必须是一个正数;ptdiff_t类型则应保证足以存放同一数组中两个指针之间的间距,
它可能是负数。

下标和指针

int ia[0]={0,2,4,6,8};
int i=ia[0];
int *p=&ia[2];                                //ok:p points to the element indexed by 2
int j=p[1];                                   //ok:p[1] equivalent to *(p+1)
                                                   p[1is the same element as ia[3]
int k=p[-2];                                  //ok:p[-2] is the same element as ia[0]

C++允许计算数组或对象的超出末端的地址,但不允许对此地址进行解引用操作。而计算数组超出末端位置之后后
数组首地址之前的地址都是不合法的。

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

const int universe=42;
const void *cpv=&universe;                    //ok
void *pv=&universe;                           //error:universe is const

允许把非const对象的地址赋给指向const对象的指针。

double dval=3.14;
cptr
=&dval;


与字符指针相关的使用

 1#include<iostream>
 2using namespace std;
 3int main()
 4{
 5    char s[21],*ps=s;
 6    for(int i=0;i<20;++i)
 7    s[i]='A'+i;
 8    s[20]='\0';
 9    ps++;
10    cout<<"ps="<<ps<<endl;
11    ps+=2;
12    cout<<"ps="<<ps<<endl;
13    for(ps=&s[19];ps>&s[11];ps-=2)
14    {
15        cout<<"*ps="<<*ps<<endl;
16        cout<<"ps="<<ps<<endl;
17    }

18    return 0;
19}

20

运行结果:

ps=BCDEFGHIJKLMNOPQRST
ps
=DEFGHIJKLMNOPQRST
*ps=T
ps
=T
*ps=R
ps
=RST
*ps=P
ps
=PQRST
*ps=N
ps
=NOPQRST

分析:
对字符型指针"char *"值的输出,实际上时输出从该字符指针所指位置开始直到串尾结束符('\0')为止的那一个字符串。

C风格字符串的标准库函数
操纵C风格字符串的标准库函数

strlen(s)                                     //返回s的长度,不包括字符串结束符null
strcmp(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

传递给这些标准库函数例程的指针必须具有非零值,并且指向以null结束的字符数组中的第一个元素。

用于不要忘记字符串结束符null
在使用处理C风格字符串的标准库函数时,牢记字符串必须以结束符null结束

char ca[]={'C','+','+'};
cout
<<strlen(ca)<<endl;

在这个例题中,ca是一个没有null结束符的字符数组,则计算的结果不可预料。
标准库函数strlen总是假定其参数字符串以null字符结束。

从标准输入设备读入一个C风格字符串可如下实现:

char str[80];
cin
>>str;



新旧代码的兼容
在要求C风格字符串的地方不可直接使用标准库string类型对象。例如:

string st2;
char *str=st2;                                //complie-time type error

但是,string类提供了一个名为c_str的成员函数,以实现我们的要求:
char *str=st2.c_str();
c_str函数返回C风格字符串,其字面意思是:"返回C风格字符串的表示方法",即返回指向字符数组首地址的指针,
该数组存放了与string对象相同的内容,并且以结束符null结束。
因为c_str返回的指针指向const char类型的数组,所以上述初始化失败,这样做是为了避免修改该数组。
正确的初始化为:
const char *str=st2.c_str();
这样str就相当于是string类型,输出字符串:
cout<<str;
就可以了。输出首字符:
cout<<*str;



动态数组的定义
int *pia = new int [10];
此new表达式分配了一个含有10个int型元素的数组,并返回指向该数组第一个元素的指针,此返回值初始化了指针pia。
在自由存储区中创建的数组对象是没有名字的,程序员只能通过其地址间接地访问堆中的对象。
初始化动态分配的数组
string *psa = new string[10];  //array of 10 empty strings
int *pia = new int[10];        //array of 10 unitialized ints
也可使用跟在数组长度后面的一对空圆括号,对数组元素做初始化:
int *pia2 = new int[10]();      //array of unitialized ints

圆括号要求编译器对数组做值初始化,在本例中即把数组元素都设置为0。
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]();

posted on 2009-08-11 22:59 八路 阅读(427) 评论(0)  编辑 收藏 引用 所属分类: 学习笔记C++primer 笔记


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理