CPP-03-数组

数组

声明数组的通用格式如下

  • 存储在每个元素中的值的类型
  • 数组名
  • 数组中的元素数
1
typeName arrayName[arraySize];

包含了每个元素的值类型、数组名、数组中的元素个数,arraySize 必须是整形常量(如10)或 const值,也可以是常量表达式(如 8 * sizeof(int)),即其中所有的值在编译时都是已知的

数组的初始化有以下几种

1
2
3
4
int cards[4] = {1, 2, 3, 4};
int hand[4];
hand[4] = {5, 6, 7, 8};
hand = cards;

另外还有特殊的初始化

1
2
3
4
float hotelTips[5] = {5.0, 2.5};	//只初始化hotelTips的前两个元素
long totals[500] = {0}; //全部是0
long nums[500] = {1}; //第一个是1,其他都是0
long sum[500] = {}; //???

如果只对数组的一部分进行初始化,则编译器将把其他元素设置为 0

如果不指定数组的元素个数,编译器将计算元素个数

1
2
short things[] = {1, 5, 3, 8};	//编译器将使things数组包含4个元素
int num_elements = sizeof(things)/sizeof(short);

C++11将使用大括号的初始化(列表初始化)作为一 种通用初始化方式,可用于所有类型,C++11 中的列表初始化新增了一些功能

  • 初始化数组时,可省略等号 =
  • 可不在大括号内包含任何东西,这将把所有元素都设置为
  • 列表初始化禁止缩窄转换
1
2
3
4
5
6
double earnings[4] {1.2e4, 1.6e4, 1.1e4, 1.7e4};
unsigned int cunts[10] = {}; //all elements set to 0
float balances[100] {}; //all elements set to 0
long plifs[] = {25, 92, 3.0}; //not allowed
char slifs[4] {'h', 'i', 1232311, '\0'}; //not allowed
char tlifs[4] {'h', 'i', 112, '\0'}; //allowed

C++标准模板库STL提供了一种数组替代品—模板类vector,而 C++11新增了模板类array

字符数组

字符串是存储在内存的连续字节中的一系列字符,意味着可以将字符串存储在char数组中,其中每个字符都位于自己的数组元素中,但注意以 以空字符(null character)结尾!!!!

1
2
3
char dog[10] = {'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'};	//not a string
char cat[10] = {'h', 'e', 'l', 'l', 'o', 'b', 'a', 'b', 'y', '\0'}; //a string
char cat[10] = "hellobaby"; //等价第 2 行

所以字符常量 'S' 与字符串常量 "S" 是不等价的,"S" 表示的是两个字符(字符S和\0)组成的字 符串。更糟糕的是,"S"实际上表示的是字符串所在的内存地址,以下是非法的

1
char shirt_size = "S"; //illegal type mismatch

另外,跟数组的初始化类似,字符数组也会为没有初始化的自动设置值

c++_0726_11

为了在数组中使用字符串,可以有两种办法

  • 将数组初始化为字符串常量 char cat[10] = "hellobaby";
  • 将键盘或文件输入读入到数组中,如下
1
2
3
4
5
6
const int Size = 15;
char name1[Size];
char name2[Size] = "Hello World!";
cin >> name1;
cout << "name1 len " << strlen(name1) << " size " << sizeof(name1) << endl;
cout << name1 << " | " << name2;

输出结果

1
2
3
nihao
name1 len 5 size 15
nihao | Hello World!

可以看到 strlen 返回的是字符串的长度,但是 sizeof 返回的数组的长度。另外 strlen 只计算可见的字符,而不把空字符计算在内。

如果输入长度超过字符数组长度怎么办?

1
2
3
4
5
6
7
const int Size = 5;
char name1[Size];
char name2[Size];
cin >> name1;
cout << name1 << " " << strlen(name1) << endl;
cin >> name2;
cout << name2 << " " << strlen(name2) << endl;

运行结果

1
2
3
ni hello world 	#输入
ni 2
hello 5

这里有两个注意的地方

  1. name1ni 而不是 ni he ,这是因为cin 使用空白(空格、制表符和换行符)来确定字符串的结束位置
  2. 没有执行(用户输入) cin >> name2;cinhello 放入 name1 中并添加空字符。然后输出队列中剩余的 world 直接输入到 name2 中,所以感觉不到输入第二次

要是就是想将 ni hello world 保存到一个字符串中怎么办?

  1. getline() 函数读取整行,通过换行符来确定输入结尾

    1
    2
    3
    4
    5
    6
    7
    const int Size = 5;
    char name1[Size];
    char name2[Size];
    cin.getline(name1, Size);
    cin.getline(name2, Size);
    cout << name1 << " " << strlen(name1) << endl;
    cout << name2 << " " << strlen(name2) << endl;

    输出结果

    1
    2
    3
    ni hello world	#输入
    ni h 4
    0

    这里不一样的是

    • 输入超过字符数组长度的会被自动截断
    • 如果输入超过长度,那么后面的会等于直接输入'\0'
  2. get()与 getline() 不同的是,get() 并不再读取并丢弃换行符,而是将其留在输入队列中

    1
    2
    3
    4
    5
    6
    7
    const int Size = 5;
    char name1[Size];
    char name2[Size];
    cin.get(name1, Size);
    cin.get(name2, Size);
    cout << name1 << " " << strlen(name1) << endl;
    cout << name2 << " " << strlen(name2) << endl;

    输出结果

    1
    2
    3
    ni hello world	#输入
    ni h 4
    ello 4
  3. 可以将上述逻辑合并到一起

    1
    2
    3
    4
    5
    6
    const int Size = 20;
    char name1[Size];
    char name2[Size];
    cin.getline(name1, Size).getline(name2, Size);
    cout << name1 << " " << strlen(name1) << endl;
    cout << name2 << " " << strlen(name2) << endl;

    运行结果

    1
    2
    3
    4
    hello world	#输入
    hello #输入
    hello world 11
    hello 5
  4. 当读取空行后将设置失效位 (failbit)。这意味着接下来的输入将被阻断,但可以用下面的命令来cin.clear()恢复输入,从上次结尾的地方继续读取

    1
    2
    3
    4
    5
    6
    7
    8
    const int Size = 5;
    char name1[Size];
    char name2[Size];
    cin.getline(name1, Size);
    cin.clear();
    cin.getline(name2, Size);
    cout << name1 << " " << strlen(name1) << endl;
    cout << name2 << " " << strlen(name2) << endl;

    运行结果

    1
    2
    3
    ni hello world	#输入
    ni h 4
    ello 4

如果手动设置字符数组指定位置为空字符,那么将导致字符串被截断

1
2
3
4
5
const int Size = 15;
char name2[Size] = "Hello World!";
cout << name2 << " " << strlen(name2) << endl;
name2[4] = '\0';
cout << name2 << " " << strlen(name2) << endl;

输出结果

1
2
Hello World! 12
Hell 4

最后一种输入的异常是混合输入

1
2
3
4
5
6
7
8
9
cout << "Waht year was your house built?\n";
int year;
cin >> year;
cout << "Wat is its street address?\n";
char address[80];
cin.getline(address, 80);
cout << "Year build: " << year << endl;
cout << "Address: " << address << endl;
cout << "Done! \n";

运行结果是

1
2
3
4
5
6
Waht year was your house built?
1966 #输入
Wat is its street address?
Year build: 1966
Address:
Done!

用户根本没有输入地址的机会。问题在于,当cin读取年份,将回车键生成的换行符留在了输入队列中。后面的cin.getline( )看到换行符后,将认为是一个空行,并将一个空字符串赋给address数组。

解决办法是,在读取地址之前先读取并丢弃换行符。可以通过 空参数的cin.get() 或者 cin.clear() 解决

数组的替代

1
2
vector<typeName> vt(n_elem);
array<typeName, n_elem> arr;

参考链接

  1. 《C++ Primer Plus》