《C++primer》读书笔记---数组和指针

数组

1.1 数组

1.1.1数组的初始化和定义
数组的维数必须是大于等于1的常量表达式定义。此常量表达式只能用整型字面值常量、枚举常量或者用常量表达式初始化的整型const对象。非const变量以及就要到运行阶段才能确定其值的const变量都不能用于定义数组的维数。
在函数体外定义的内置数组,其元素初始化为0;
在函数体内定义的内置数组,其元素无初始化;
如果为类类型则自动调用构造函数初始化,如果无构造函数则必须显示的初始化。

特殊字符初始化:
char ca1[] = {'C', '+', '+'}; // no null 正常初始化后结尾不会自动加'\0'空字符
char ca2[] = {'C', '+', '+', '\0'}; // explicit null
char ca3[] = "C++"; // null terminator added automatically使用字符串字面值初始化后自动+‘\0’,此时size为4‘;

不能对数组对象进行直接复制或者赋值
int ia2[](ia); // error: cannot initialize one array with another
ia3 = ia; // error: cannot assign one array to another

1.1.2数组操作
数组在遍历和赋值时可以使用下标操作访问,下标的正确类型为size_t

1.2.1指针的初始化和赋值操作的约束
对指针进行初始化或者赋值可以使用如下:
1>0值常量表达式    例如,在编译时可获得0值的整型const对象或者字面值常量0
2>类型匹配的对象的地址
3>另一个对象之后的下一个地址
4>同一类型另一个有效的指针
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
把int变量赋值给指针是非法的,尽管int型变量值可能为0:
int *pi = NULL; // ok: equivalent to int *pi = 0;

1.2.2void*指针
void*指针可以保存任何类型对象的地址:
double d1 = 3.14;
double *d = &d1;
void *v = d;
void*表明改制真与已低至相关,但不清楚此地址上的对象的类型,不可以使用void*指针操作它指向的对象。

1.2.3指针操作
指针提供简介操作器所指对象的功能,类似于对迭代器进行解引用 操作一样,对指针解引用可以访问他所指向的对象。

1.2.4指针和引用的比较
区别:
1>引用总是指向某个对象:定义引用时没有初始化时错误的。
2>给引用赋值修改的是该引用所关联的对象的值,而并不是是引用于另一个对相关联。
3>引用一经初始化就始终指向同一个特定对象。
例如:
int ival = 1024, ival2 = 2048;
int &ri = ival, &ri2 = ival2;
ri = ri2; // assigns ival2 to ival
ri = ri2使得ival的值等于ival2的值2048;

1.2.5指针和数组
类似对数组的下标访问操作,通过指针的算术操作也可以达到同样的效果(此时指针类似vector里的迭代器)。
int ia[] = {0,2,4,6,8};
int *ip = ia; // ip points to ia[0]
ip = &ia[4]; // ip points to last element in ia
int *ip2 = ip + 4; // ok: ip2 points to ia[4], the last element in ia
通常在数组指针上加上(或减去)一个整型数值n,等效于获得一个新指针,该指针指向原指针指向元素之后(或之前)的第n个元素。(注意:不要越界)

只要两个指针指向同一个数组或者有一个指向该数组末端的下一个单元,C++支持对这两个指针做减法操作:
ptrdiff_t n = ip2 - ip;
结果是4,两个指针指向元素的间隔。结果是标准库类型ptrdiff_t类似size_t但ptrdiff_t是signed,因为连个指针间的差距可能是负数。
 
在一个指针上加一个整数仍然是一个指针,并且支持直接解引用:int last = *(ia + 4);等价于ia[4];


下标和指针:
使用下标访问数组,实际是使用下标访问指针
int ia[] = {0, 2, 4, 6, 8};
int i = a[0];
int *p = &ia[2];
int j = p[1]; // j = ia[3];  相当于*(p + 1);
int k = p[-2]; // k = ia[0]; 相当于*(p -  2);

计算数组超出末端指针
nt iarr[] = {0, 2, 4, 6, 8};
int *p = arr;
int *p2 = p + 4; //允许计算,但不许解引用和赋值。
超出末端指针常用于数组的遍历,判断是否已到数组尾:
const size_t arr_sz = 5;
int int_arr[arr_sz] = { 0, 1, 2, 3, 4 };

for (int *pbegin = int_arr, *pend = int_arr + arr_sz; pbegin != pend; ++pbegin)
cout << *pbegin << ' '; // print the current element
类似标准库容器的迭代器,指针与数组一起使用时,指针其实是数组的迭代器

1.2 指针和const限定符

1.2.1 指向const对象的指针
const double pi = 3.14;  (可以不进行初始化)
double pi2 = 3.14;
const double *cptr;
double *cptr2;
特性:
1>不可以使用指向const对象的指针对对象进行修改; 例如:*cptr = 3.142;  //error !
2>C++强制要求指向const对象的指针必须具有const特性; 例如:constdouble *cptr;
3>指向const对象的指针本身不是const,可以赋值; 例如:cptr = cptr2;
4>const对象不能赋值给普通或者非const对象的指针 例如:double *cptr3 = &pi;   //error !
5>不能使用void *保存const对象地址,必须使用const void*
6>允许把非const对象地址保存到const对象的指针 例如:const double *cptr = & pi2;  
     此时不能通过该指针对pi2的指针进行修改,但可以通过普通指针修改   例如: double *cptr3 = &pi2;  *cptr3  = 3.1432;
指向const对象的指针,所指对象不一定为cosnt对象(因为可以指向普通非const对象),可以理解为“自以为指向cosnt的指针
实际使用中,指向const的指针常用作函数的形参,以确保传递给函数的实际对象在函数中不被修改;

1.2.2 const指针
const指针---本身的值不能修改:
int errNum = 0;
int *const curerr = &errNumb;
curerr = curerr; //即使是赋值本身也不行
const指针所指向的对象能否修改取决于对象本身,如果对象本身是const则不能修改,否则可以通过*curerr = ···修改;

1.2.3指针和typedef
typedef string *pstring;
const pstring cstr;     //等同于 string *const cstr
const double db == double const db; // const修饰符可以放在类型的前面或者后面,作用都是使其修饰的变量成为const;
typedef string *pstring  使得*string 成为一个类型(指针类型的string --> pstring),此时const 位于pstring前或者后均可,都表示声明一个const (string*)类型变量。

2 C风格字符串 --- 以NULL结束的字符数组:
char ca1[ ] = {'c', '+', '+'};  //没有使用NULL字符结尾,并非C风格字符串
char ca2[ ] = {‘c’,  '+', '+', '\0'};
char ca3[ ] = "c++"; //自带NULL字符结尾
const char *cp = “c++”; //自带NULL字符结尾
char *cp1 = cp1; //指向数组首元素,但不是C-Style字符串
char *cp2 = ca2; //指向数组首元素,以NULL字符结尾

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

strlen(s) Returns the length of s, not counting the null.
strcmp(s1, s2) Compares s1 and s2 for equality. Returns 0 if s1 == s2,
positive value if s1 > s2, negative value if s1 < s2.
strcat(s1, s2) Appends s2 to s1. Returns s1.
strcpy(s1, s2) Copies s2 into s1. Returns s1.
strncat(s1, s2,n) Appends n characters from s2 onto s1. Returns s1.
strncpy(s1, s2, n) Copies n characters from s2 into s1. Returns s1.

C++提供关系操作符对C风格字符串进行比较,但意义不同:
if (cp1 < cp2) // compares addresses, not the values pointed to

C风格标准库函数仅适用于以null结尾的C_Style字符串,乱用会出错:
char ca[] = {'C', '+', '+'}; // not null-terminated
cout << strlen(ca) << endl; // disaster: ca isn't null-terminated


使用strn函数处理C风格字符串相对更安全,但尽可能使用C++标准库类型string

解释下列两个

while 

循环的差别:

 

const char *cp = "hello"; 

int cnt; 

while (cp) { ++cnt; ++cp; } 

while (*cp) { ++cnt; ++cp; }

解释下列两个

while 

循环的差别:

 

const char *cp = "hello"; 

int cnt; 

while (cp) { ++cnt; ++cp; } 

while (*cp) { ++cnt; ++cp; }

解释下列两个

while 

循环的差别:

 

const char *cp = "hello"; 

int cnt; 

while (cp) { ++cnt; ++cp; } 

while (*cp) { ++cnt; ++cp; }

解释下列两个

while 

循环的差别:

 

const char *cp = "hello"; 

int cnt; 

while (cp) { ++cnt; ++cp; } 

while (*cp) { ++cnt; ++cp; }

const char *cp = "hello";
int cnt;
while (cp) {++cnt, ++cp};		//结束条件为cp指针为 0 ;死循环
while(*cp) {++cnt, ++cp};		//结束条件为cp所指向的值为0,即末尾\0,可以正确计算出字符串长度。

2.2创建动态数组
初始化:
动态分配数组时,如果元素具有类类型,将使用该类的默认构造函数实现初始化,如果数组元素师内置类型则无初始化。
string *psa = new string[10]; // array of 10 empty strings
int *pia = new int[10]; // array of 10 uninitialized ints
可以使用数组长度后面的一对空圆括号对数组元素做值初始化,不能像数组那样使用初始化列表为数组提供各不相同的初始值,只能将元素初始化为元素类型的默认值
int *pia2 = new int[10]()   //把十个int初始化为0;
const 对象的动态数组
在堆中创建动态数组就必须对成员初始化,内置类型必须在后边带“()”,类类型必须提供默构造函数。已创建的const常量元素不允许修改,因此const动态数组无用处。
	const int *pci_bad = new const int[100];		// error: uninitialized const array
	const int *pci_ok = new const int[100]();		// ok: value-initialized const array
	const string *pcs = new const string[100];	<span style="white-space:pre">	</span>// ok: array of 100 empty strings

允许动态分配空数组
在不知道数组长度的时候,我们编写如下代码:
size_t n = get_size(); // get_size returns number of elements needed
int* p = new int[n];
for (int* q = p; q != p + n; ++q)
;
如果get_size()返回 0 ,代码将仍然执行,C++语序使用new动态创建长度为0的数组,new返回有效的非零指针,但不能对其解引用。

动态数组空间的释放

delete [ ]  pia;

2.3 混合使用标准库类string和C风格字符串

可以使用字符串字面值初始化string对象;
可以使用C风格字符串对string对象初始化或者赋值;
string类型加操作可以使其中一个操作数为C风格字符串,也可将C风格字符串用作复合赋值操作的右操作数;
反之则不成立:

string提供一个名为c_str的函数:
const char *str = st2.c_str();
c_str函数返回C风格字符串,即:指向字符数组首地址的指针,该数组厨房了与string对象相同的内容,并且以null结束。
c_str返回的数组并不保证一定有效,接下来如果对st2操作可能会改变st2的值,使返回的数组失效。如果程序需要持续访问该数据,则应该赋值c_str返回的数组
	string st2 = "hello";
	const char *str = st2.c_str();
	cout<<str<<endl;<span style="white-space:pre">	</span>//输出结果hello
	st2 = "world";
	cout<<str<<endl;<span style="white-space:pre">	</span>//输出结果world
参考:http://blog.csdn.net/baizhengbiao/article/details/7341666

2.4 用数组初始化vector对象
使用数组初始化vector必须指出用于初始化式的第一个第一个元素及数组最后一个元素的下一个位置的地址:
const size_t arr_size = 6;
int int_arr[arr_size] = {0, 1, 2, 3, 4, 5};  <pre name="code" class="cpp">// ivec has 6 elements: each a copy of the corresponding element in int_arr<span style="font-family: Arial, Helvetica, sans-serif;">		</span><span style="font-family: Arial, Helvetica, sans-serif;">				</span>
vector<int> ivec(int_arr, int_arr + arr_size);

传递给ivec的两个指针标出了vector初值的范围,第二个指针指向被复制的最后一个元素之后的地址空间。被标出的元素范围可以使数组的子集:
vector< int >(int_arr + 1, int_arr + 4 );//复制3个元素
2.5 多维数组
C++中无多维数组,通常所指为数组的数组:  int ia[3][4]  三个含有4个元素的int 数组
多维数组的初始化:
<pre name="code" class="cpp">int ia[3][4] = { 	/* 3 elements, each element is an array of size 4 */
{0, 1, 2, 3} , 		/* initializers for row indexed by 0 */
{4, 5, 6, 7} , 		/* initializers for row indexed by 1 */
{8, 9, 10, 11} 		/* initializers for row indexed by 2 */
};
int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};		//以上两种方法等效
int ia[3][4] = {{ 0 } , { 4 } , { 8 } };			//只初始化每行的第一个元素
int ia[3][4] = {0, 3, 6, 9};				//初始化第0行的元素,其他未初始化


多维数组访问时需要提供行下标和列下标,如果只提供一个下标如ia[2],将只获得ia数组最后一行,即内层数组本身,并非数组中任意一元素。

多为数组是数组的数组,所以由多为数组转换成的指针类型应该是指向第一个内层数组的指针:
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;
ip  = &ia[2];        //ia[2] is an array of 4 ints;
定义之像数组的指针与如何定义数组本身类似:首先声明元素类型,后接(数组)变量名字和维数。*p可以理解为:int[4]4类型 --- 即ip是一个指向有4个元素的数组的指针。
圆括号必不可少:
int *ip[4]; // array of pointers to int<span style="white-space:pre">		</span>指针的数组
int (*ip)[4]; // pointer to an array of 4 ints<span style="white-space:pre">	</span>数组的指针
使用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;
}
}







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章