与vector 类似,数组也是容器。
数组的缺陷:
1、长度固定
2、没有size 操作,故,无法获取容器大小
3、没有push_back 操作,无法向其添加元素
4、无法更改数组长度,只能创建新的更大的数组,然后,将原数组复制到新数组。
数组维数,必须是大于1 的常量表达式。
也就是说,必须是在编译时,就知道的值。如:const unsigned buf_size = 512;
凡是在运行时才知道的值,都不能定义数组的维数。
- const unsigned buf_size = 512, max_files = 20;
- int staff_size = 27;
- char input_buffer[buf_size]; // 正确, buf_size 是 const
- string fileTable[max_files + 1]; // 正确, max_files+1 是常量表达式
- const unsigned sz = get_size(); // 错误,get_size() 在运行时才知道,结果值
- double salaries[staff_size]; // 错误,staff_size 非const
- int test_scores[get_size()]; // 错误,同 get_size()
- int vals[sz]; // 错误,同 get_size()
const unsigned buf_size = 512, max_files = 20;
int staff_size = 27;
char input_buffer[buf_size]; // 正确, buf_size 是 const
string fileTable[max_files + 1]; // 正确, max_files+1 是常量表达式
const unsigned sz = get_size(); // 错误,get_size() 在运行时才知道,结果值
double salaries[staff_size]; // 错误,staff_size 非const
int test_scores[get_size()]; // 错误,同 get_size()
int vals[sz]; // 错误,同 get_size()
显示初始化数组:
- // 显示初始化
- const unsigned array_size = 5;
- int iarr[array_size] = {0, 1, 2, 3, 4};
- // 省略维数
- int iarr2[] = {0, 1, 2, 3};
- // 相当于:{1, 2, 3, 0, 0}
- int iarr3[array_size] = {1, 2, 3};
- // 相当于:{"aa", "bb", "cc", "", ""}
- string sarr[array_size] = {"aa", "bb", "cc"};
// 显示初始化
const unsigned array_size = 5;
int iarr[array_size] = {0, 1, 2, 3, 4};
// 省略维数
int iarr2[] = {0, 1, 2, 3};
// 相当于:{1, 2, 3, 0, 0}
int iarr3[array_size] = {1, 2, 3};
// 相当于:{"aa", "bb", "cc", "", ""}
string sarr[array_size] = {"aa", "bb", "cc"};
特殊字符:
- char ca1[] = {'C', '+', '+'}; // size: 3
- char ca2[] = {'C', '+', '+', '\0'}; // size:4
- char ca3[] = "C++"; // size:4,末尾自动添加 null
char ca1[] = {'C', '+', '+'}; // size: 3
char ca2[] = {'C', '+', '+', '\0'}; // size:4
char ca3[] = "C++"; // size:4,末尾自动添加 null
数组不可直接复制或者赋值:
- char ca1[] = {'C', '+', '+'};
- char ca2[] = ca1;// 错误
char ca1[] = {'C', '+', '+'};
char ca2[] = ca1;// 错误
vector 的下标类型: vector::size_type
数组的下标类型 : size_t
数组下标超出正确范围,即,发生数组越界。将会产生严重错误。类似的安全问题,称为:缓冲区溢出
- const unsigned array_size = 5;
- int iarr1[] = {1, 2, 3, 4, 5};
- int iarr2[array_size];
- for(size_t i=0; i!=array_size; ++i)
- iarr2[i] = iarr1[i];
const unsigned array_size = 5;
int iarr1[] = {1, 2, 3, 4, 5};
int iarr2[array_size];
for(size_t i=0; i!=array_size; ++i)
iarr2[i] = iarr1[i];
指针,&符号是:取地址操作符(address-of)
- string s("This is a string");
- string *sp = &s;
string s("This is a string");
string *sp = &s;
注:指针多数用于低级操作,容易产生错误,尽量少用。而通过 vector 、迭代器等方式取代一般的数组、指针的使用。 使用 string 类型,取代 C 风格字符串。
指针定义、初始化
- vector<int> *pvec;
- int *ip1, *ip2;
- string* sp1, sp2; // sp1 是指针,而 sp2 只是 string
vector<int> *pvec;
int *ip1, *ip2;
string* sp1, sp2; // sp1 是指针,而 sp2 只是 string
指针保存 0 值 或者 NULL ,表明不指向任何对象。(NULL 实际上等价于 0)
特殊的指针:void * ,可以保存任何类型对象的地址
- int ival = 1024;
- int *pi = 0;
- int *pi2 = &ival;
- void *pi3 = pi2;
- pi = pi2;
int ival = 1024;
int *pi = 0;
int *pi2 = &ival;
void *pi3 = pi2;
pi = pi2;
数组的算术操作。 *pi2、*pi3 结果相同,但, int *pi3 = iarr + 4 表述更直观。
指针作减法操作,结果是 ptrdiff_t 类型的数据。
使用下标访问数组时,实际上,是使用下标访问了指针。
- int iarr[] = {1, 2, 3, 4, 5};
- int *pi = iarr;
- int *pi2 = &iarr[4];
- int *pi3 = iarr + 4;
- ptrdiff_t n = pi3 - pi;
- int i = *(iarr + 3); // iarr[3], 4
- int *pi4 = pi3 - 4; // &iarr[0]
- int x = pi3[-4]; // iarr[0] equals to pi3[-4]
- // 指向数组的超出末端的地址,可以计算,不能解引用
- int *ipend = iarr + 5;
int iarr[] = {1, 2, 3, 4, 5};
int *pi = iarr;
int *pi2 = &iarr[4];
int *pi3 = iarr + 4;
ptrdiff_t n = pi3 - pi;
int i = *(iarr + 3); // iarr[3], 4
int *pi4 = pi3 - 4; // &iarr[0]
int x = pi3[-4]; // iarr[0] equals to pi3[-4]
// 指向数组的超出末端的地址,可以计算,不能解引用
int *ipend = iarr + 5;
指针是数组的迭代器
- const size_t array_size = 3;
- int iarr[array_size] = {1, 2, 3};
- for (int *pbegin = iarr, *pend = iarr + array_size; pbegin != pend; ++pbegin)
- *pbegin = 100;
- vector<int> ivec(10, 0);
- for (vector<int>::iterator iter = ivec.begin(); iter!=ivec.end(); ++iter)
- *iter = 1;
const size_t array_size = 3;
int iarr[array_size] = {1, 2, 3};
for (int *pbegin = iarr, *pend = iarr + array_size; pbegin != pend; ++pbegin)
*pbegin = 100;
vector<int> ivec(10, 0);
for (vector<int>::iterator iter = ivec.begin(); iter!=ivec.end(); ++iter)
*iter = 1;
指针和引用的区别
- #include <iostream>
- #include <string>
- #include <bitset>
- using std::cin;
- using std::cout;
- using std::endl;
- using std::string;
- using std::bitset;
- int main()
- {
- int ival = 1024, ival2 = 2048;
- int *pi = &ival, *pi2 = &ival2;
- pi = pi2;
- *pi = 5000;
- cout << "pi:" << pi << ", pi2:" << pi2 << endl;
- cout << "*pi :" << *pi << ", *pi2 :" << *pi2 << endl;
- int &ri = ival, &ri2 = ival2;
- cout << *(&ri) <<endl;
- ri = ri2;
- cout << "ri:" << ri << ", ri2:" << ri2 << endl;
- cout << "&ri:" << &ri << ", &ri2:" << &ri2 << endl;
- system("pause");
- return 0;
- }
#include <iostream>
#include <string>
#include <bitset>
using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::bitset;
int main()
{
int ival = 1024, ival2 = 2048;
int *pi = &ival, *pi2 = &ival2;
pi = pi2;
*pi = 5000;
cout << "pi:" << pi << ", pi2:" << pi2 << endl;
cout << "*pi :" << *pi << ", *pi2 :" << *pi2 << endl;
int &ri = ival, &ri2 = ival2;
cout << *(&ri) <<endl;
ri = ri2;
cout << "ri:" << ri << ", ri2:" << ri2 << endl;
cout << "&ri:" << &ri << ", &ri2:" << &ri2 << endl;
system("pause");
return 0;
}
结果如下:
指向指针的指针
- #include <iostream>
- #include <string>
- #include <bitset>
- using std::cin;
- using std::cout;
- using std::endl;
- using std::string;
- using std::bitset;
- int main()
- {
- int x = 10;
- int *pi = &x;
- int **ppi = π
- cout << "x:" << x << endl;
- cout << "*pi:" << *pi << endl;
- cout << "**ppi:" << **ppi << endl;
- system("pause");
- return 0;
- }
#include <iostream>
#include <string>
#include <bitset>
using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::bitset;
int main()
{
int x = 10;
int *pi = &x;
int **ppi = π
cout << "x:" << x << endl;
cout << "*pi:" << *pi << endl;
cout << "**ppi:" << **ppi << endl;
system("pause");
return 0;
}
结果如下
指针和 const 限定符
- double dheight = 100.5;
- const double dprice = 10.5;
- const double *pd = &dprice; // pd 指向的 dprice 是const 的
- pd = &dheight; // pd 可以指向非const 的对象
- int ierr = 0;
- int *const curErr = &ierr; // 指针 curErr, 是const的
- const int iMsg = 10;
- const int *const pmsg = &iMsg; // 指针 和 指向的对象,都是const的
double dheight = 100.5;
const double dprice = 10.5;
const double *pd = &dprice; // pd 指向的 dprice 是const 的
pd = &dheight; // pd 可以指向非const 的对象
int ierr = 0;
int *const curErr = &ierr; // 指针 curErr, 是const的
const int iMsg = 10;
const int *const pmsg = &iMsg; // 指针 和 指向的对象,都是const的
指针和 typedef。以下:ps,ps2,ps3 都是等价的。
原因是:const 限定符可以放在类型前,也可以放在类型后。
就是说, const string a;
也可以写成: string const a;
- typedef string * pstring;
- string s("Hello");
- const pstring ps = &s;
- string *const ps2 = &s; // 与上式,等价
- pstring const ps3 = &s; // 与上式,等价
typedef string * pstring;
string s("Hello");
const pstring ps = &s;
string *const ps2 = &s; // 与上式,等价
pstring const ps3 = &s; // 与上式,等价
此处,const 修饰的是 pstring 类型,也就是说,const 修饰的是指针。typedef,不是简单的文本扩展!
因此,const pstring ps = &s; 其实等价于: string *const ps = &s;
C风格字符串
字符串字面值 的类型,是字符常量的数组。
也就是,const char 类型的数组。
C++ 从C语言 继承下来的一种通用结构就是 C风格字符串(C-style character string)
而,字符串面值,就是该类型的实例。
实际上,C风格字符串既不能归结为C语言的类型,也不能归结为C++语言的类型,
而是以空字符 null 结束的字符数组。
- char ca1[] = {'C', '+', '+'};
- char ca2[] = {'C', '+', '+', '\0'}; // C风格
- char ca3[] = "C++"; // C风格,末尾自动加 null
- const char *cp = "C++"; // C风格
- char *cp1 = ca1; //
- char *cp2 = ca2; // C风格
char ca1[] = {'C', '+', '+'};
char ca2[] = {'C', '+', '+', '\0'}; // C风格
char ca3[] = "C++"; // C风格,末尾自动加 null
const char *cp = "C++"; // C风格
char *cp1 = ca1; //
char *cp2 = ca2; // C风格
C++ 通过 (const) char * 类型的指针,来操作C风格字符串。
- const char *cp = "some value";
- while(*cp)
- {
- ++cp;
- }
const char *cp = "some value";
while(*cp)
{
++cp;
}
如果 cp 指向的字符串数组没有null 字符,则,无法达到预期效果。
直到在内存中找到null 字符,循环才会终止。
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 |
注意:C风格字符串的操作容易出错。
通常使用标准库类型 string 来代替此类操作
动态数组
动态数组,不必在编译时知道其长度,可以是在运行时再确定数组长度。
每个程序,执行时都占用一块可用内存,用于存放动态分配的对象,
此内存称为:自由存储区(Free Store)或者 堆(Heap)
C++ 使用 new 和 delete 来分配和释放 自由存储区。
- string *sp = new string[10];
- int *ip = new int[20];
- size_t n = get_size();
- int *ip3 = new int[n];
- string *sp2 = new string[30]();
- const int *ip2 = new const int[11]();
- // char arr[0] 是错误的, 然而,动态分配时,长度可以是0,并返回非0 的指针,但该指针不能解引用
- char *cp = new char[0];
- delete [] ip3;
string *sp = new string[10];
int *ip = new int[20];
size_t n = get_size();
int *ip3 = new int[n];
string *sp2 = new string[30]();
const int *ip2 = new const int[11]();
// char arr[0] 是错误的, 然而,动态分配时,长度可以是0,并返回非0 的指针,但该指针不能解引用
char *cp = new char[0];
delete [] ip3;
使用动态数组,通常是因为,编译时无法知道数组的准确长度。如:以下场景
- const char *noerr = "success";
- const char *err188 = "Error Code: 188";
- const char *errorText;
- if(errorFound)
- errorText = err188;
- else
- errorText = noerr;
- int dimension = strlen(errorText) + 1;
- char *errMsg = new char[dimension];
- strncpy(errMsg, errorText, dimension);
const char *noerr = "success";
const char *err188 = "Error Code: 188";
const char *errorText;
if(errorFound)
errorText = err188;
else
errorText = noerr;
int dimension = strlen(errorText) + 1;
char *errMsg = new char[dimension];
strncpy(errMsg, errorText, dimension);
混合使用标准库类 string 和 C风格字符串
c_str () :返回C风格字符串的表示方法
- string str("Hello");
- const char *cp = str.c_str(); // C 风格字符串自动转换为,指向字符数组首地址的指针
- // char *cp = str.c_str(); 错误,因为c_str() 返回的是 const char 类型的数组
- // char *cp = str; 错误,无法使用 string 对象来初始化 字符指针
- // str.c_str() 返回的数组未必永远有效。因为 str 有可能被修改。
- // 因此,最好是复制 str.c_str() 返回的数组
string str("Hello");
const char *cp = str.c_str(); // C 风格字符串自动转换为,指向字符数组首地址的指针
// char *cp = str.c_str(); 错误,因为c_str() 返回的是 const char 类型的数组
// char *cp = str; 错误,无法使用 string 对象来初始化 字符指针
// str.c_str() 返回的数组未必永远有效。因为 str 有可能被修改。
// 因此,最好是复制 str.c_str() 返回的数组
- string s("abcdefg");
- // char *a; 错误
- // char *a = "x";
- // 错误,"x"的内存是在栈上分配的,栈上的内存无法修改
- // 返回的其实是 const char *,而不是 char *, 因此strcpy 将会无法写入,报错
- char *a = new char[s.length()+1];
- strcpy(a, s.c_str());
string s("abcdefg");
// char *a; 错误
// char *a = "x";
// 错误,"x"的内存是在栈上分配的,栈上的内存无法修改
// 返回的其实是 const char *,而不是 char *, 因此strcpy 将会无法写入,报错
char *a = new char[s.length()+1];
strcpy(a, s.c_str());
使用数组初始化 vector
- const size_t array_size = 5;
- int int_arr[array_size] = {88, 32, 13, 34, 65};
- // 实际复制了 int_arr[1], int_arr[2], int_arr[3], int_arr[4]
- vector<int> ivec(int_arr + 1, int_arr + array_size);
const size_t array_size = 5;
int int_arr[array_size] = {88, 32, 13, 34, 65};
// 实际复制了 int_arr[1], int_arr[2], int_arr[3], int_arr[4]
vector<int> ivec(int_arr + 1, int_arr + array_size);
多维数组,即:数组的数组
- int arr_first[2][3] = { {1, 2, 3}, {4, 5, 6} };
- // 与上式等价
- int arr_second[2][3] = {1, 2, 3, 4, 5, 6};
- // 等价于:{ {1, 0, 0}, {2, 0, 0} }
- int arr_third[2][3] = { {1}, {2} };
- // 等价于:{ {1, 2, 0}, {0, 0, 0} }
- int arr_fourth[2][3] = { 1, 2 };
- // ip 是数组,长度为3,数组元素是,指向int 的指针
- int *ip[3];
- // ip_second 是指针,指向一个长度为3 的int 数组
- int (*ip_second)[3];
- typedef int int_array [3];
- for(int_array *p_arr = arr_first; p_arr!=arr_first+2; ++p_arr)
- for(int *p_int = *p_arr; p_int!=*p_arr+3; ++p_int)
- *p_int = 100;
- int c = arr_first[0][1];
int arr_first[2][3] = { {1, 2, 3}, {4, 5, 6} };
// 与上式等价
int arr_second[2][3] = {1, 2, 3, 4, 5, 6};
// 等价于:{ {1, 0, 0}, {2, 0, 0} }
int arr_third[2][3] = { {1}, {2} };
// 等价于:{ {1, 2, 0}, {0, 0, 0} }
int arr_fourth[2][3] = { 1, 2 };
// ip 是数组,长度为3,数组元素是,指向int 的指针
int *ip[3];
// ip_second 是指针,指向一个长度为3 的int 数组
int (*ip_second)[3];
typedef int int_array [3];
for(int_array *p_arr = arr_first; p_arr!=arr_first+2; ++p_arr)
for(int *p_int = *p_arr; p_int!=*p_arr+3; ++p_int)
*p_int = 100;
int c = arr_first[0][1];