4.1 数组
数组array是一种能够存储多个同类型的值的数据格式。每个值都存储在一个独立的数组元素中,计算机在内存中依次存储数组的各个元素,即连续内存(关于内存的知识请看计算机组成与体系结构)。由此,可以单独地访问数组元素,使用下标或者索引对元素进行编号即可(自0开始)。
注意:在调用数组元素时,下标不要溢出,否则程序异常。
创建数组需要声明三点:存储在每个元素中地值的类型;数组名称;数组中的元素个数。
typeName arrayName[arraySize]
arraySize指定元素数目,必须是整型常数或const值,也可以是常量表达式。总而言之其值是已知的,这点将在后面的代码中解释。
// arrayone.cpp -- small arrays of integers
#include <iostream>
int main()
{
using namespace std;
int yams[3]; // creates array with three elements
yams[0] = 7; // assign value to first element
yams[1] = 8;
yams[2] = 6;
int yamcosts[3] = {20, 30, 5}; // create, initialize array
// NOTE: If your C++ compiler or translator can't initialize
// this array, use static int yamcosts[3] instead of
// int yamcosts[3]
cout << "Total yams = ";
cout << yams[0] + yams[1] + yams[2] << endl;
cout << "The package with " << yams[1] << " yams costs ";
cout << yamcosts[1] << " cents per yam.\n";
int total = yams[0] * yamcosts[0] + yams[1] * yamcosts[1];
total = total + yams[2] * yamcosts[2];
cout << "The total yam expense is " << total << " cents.\n";
cout << "\nSize of yams array = " << sizeof yams;
cout << " bytes.\n";
cout << "Size of one element = " << sizeof yams[0];
cout << " bytes.\n";
// cin.get();
return 0;
}
该段代码展示了数组的声明,初始化,赋值。
上面有两种初始化方式,一种是在声明时不进行初始化之后对每个元素进行赋值;第二种是使用{}进行初始化,但这种初始化方式只有在定义数组时才能使用。PS:无法将一个数组赋值给另一个数组。
若只对数组的一部分进行初始化,则其他元素设置为0.
若初始化数组时[arraySize]为[],则C++编译器根据元素个数计算长度。
ps: 数组的初始化也要考虑基础数据类型的赋值规则,对于不同类型与表示范围,要谨慎考虑数组的类型。
4.2 字符串
字符串时存储在内存的连续字节中的一系列字符,其意味着可以将字符串存储在char数组中,在C-风格字符串中,以空字符 null character 结尾,空字符被写作 \0 ,其ASCII码为0,用于标记字符串的结尾。
char dog[8] = {'a','b','c','d','e','f','g','h','i'};
char cat[8] = {'a','b','c','d','e','f','g','h','\0'};
两个都是char数组,但是只有第二个数组时字符串。空字符对于C-风格字符串而言至关重要。因为C++有很多处理字符串的函数都是逐个地处理字符串中地字符,直至空字符为止。
上述代码的初始化很冗杂,因此有一种更方便的方法:使用引号括起字符串(字符串常量/字符串字面值):
char bird[8]="Mr.Liu";
char fish[]="BuleFish";
用引号括起的字符串隐式地包括结尾的空字符,处理字符串的函数根据空字符的位置而不是数组长度。(请测试“Blue Fish”)
测试结果:在进行字符串初始化时,“Blue Fish”是可行的。通过观察局部变量,在字符串中的空格是ASCII码32,而空字符是ASCII码0。
但是在调试窗口输入时,即被cin获取时,空格被认为是空字符。(留做思考)
PS:""表示的是字符串常量; ' ' 表示的是字符常量。
/*******拼接字符串常量&在数组中使用字符串*******/
// strings.cpp -- storing strings in an array
#include <iostream>
#include <cstring> // for the strlen() function
int main()
{
using namespace std;
const int Size = 15;
char name1[Size]; // empty array
char name2[Size] = "C++owboy"; // initialized array
// NOTE: some implementations may require the static keyword
// to initialize the array name2
cout << "Howdy! I'm " << name2;
cout << "! What's your name?\n";
cin >> name1;
cout << "Well, " << name1 << ", your name has ";
cout << strlen(name1) << " letters and is stored\n";
cout << "in an array of " << sizeof(name1) << " bytes.\n";
cout << "Your initial is " << name1[0] << ".\n";
name2[3] = '\0'; // set to null character
cout << "Here are the first 3 characters of my name: ";
cout << name2 << endl;
// cin.get();
// cin.get();
return 0;
}
任何两个有空白(空格,制表符tab、换行符)分隔的字符串常量都将自动拼接为一个;拼接是不会在被链接的字符串之间添加空格,第二个字符串的第一个字符紧跟第一个字符串的最后一个字符——不包括\0,\0将会被第二个字符串的第一个字符取代——后面。
要将字符串存储到字符数组中,第一种是将数组初始化为字符串常量;第二种是将键盘或文件输入读入到数组中,请看上述代码。
cin通过空白(空格、制表符、换行符)来确定字符串的结束位置。
/*****为此,下面介绍getline()*******/
// instr2.cpp -- reading more than one word with getline
#include <iostream>
int main()
{
using namespace std;
const int ArSize = 20;
char name[ArSize];
char dessert[ArSize];
cout << "Enter your name:\n";
cin.getline(name, ArSize); // reads through newline
cout << "Enter your favorite dessert:\n";
cin.getline(dessert, ArSize);
cout << "I have some delicious " << dessert;
cout << " for you, " << name << ".\n";
// cin.get();
return 0;
}
getline函数读取整行,通过回车键键入的换行符来确定输入结尾。该函数有两个参数:第一个参数用于存储输入行的数组的名称;第二个参数是要读取的字符数(若此参数为20,则函数最多读取19个字符,余下一个空间用于存储自动在结尾处添加的空字符)。
/*****与之类似,还有一个函数get******/
// instr3.cpp -- reading more than one word with get() & get()
#include <iostream>
int main()
{
using namespace std;
const int ArSize = 20;
char name[ArSize];
char dessert[ArSize];
cout << "Enter your name:\n";
cin.get(name, ArSize).get(); // read string, newline
cout << "Enter your favorite dessert:\n";
cin.get(dessert, ArSize).get();
cout << "I have some delicious " << dessert;
cout << " for you, " << name << ".\n";
// cin.get();
return 0;
}
get函数有很多变体,其中一种变体的工作方式与getline类似,参数相同,但是不能读取并丢弃换行符,而是将其留在了输入队列中,即第二次调用时直接看见换行符,无法进行后续的读取。
get的另一种变体——cin.get()可以读取下一个字符。
参考上述代码。
getline与get函数的区别:getline使用更简单,而get哈市农户使得检查错误更加方便(为何停止,是因为换行符还是因为数组空间满了?)
PS:还有两个问题——读取空行、输入字符串比分配空间长。将在后面讨论。这类问题我平时也没碰到,希望诱人指点。
/******混合输入字符串与数字******/
// numstr.cpp -- following number input with line input
#include <iostream>
int main()
{
using namespace std;
cout << "What year was your house built?\n";
int year;
cin >> year;
// cin.get();
cout << "What is its street address?\n";
char address[80];
cin.getline(address, 80);
cout << "Year built: " << year << endl;
cout << "Address: " << address << endl;
cout << "Done!\n";
// cin.get();
return 0;
}
由此可见,输入数字之后,未能输入地址。why?当cin读取年份是,将回车键申城的换行符留在了输入队列中,其后的cin.getline()看到换行符后认为其是一个空行。应该在读取地址前读取并丢弃换行符,可以使用没有参数的get()与接受一个char参数的get()。
cin>>year;
cin.get;
//或者使用 cin.get(char);
也可以使用表达式cin>>year返回cin对象
(cin>>year).get();
//或者 (cin>>year).get(chr)
关于字符串
C++程序通常使用指针而不是数组来处理字符串
4.3 String类简介
// strtype1.cpp -- using the C++ string class
#include <iostream>
#include <string> // make string class available
int main()
{
using namespace std;
char charr1[20]; // create an empty array
char charr2[20] = "jaguar"; // create an initialized array
string str1; // create an empty string object
string str2 = "panther"; // create an initialized string
cout << "Enter a kind of feline: ";
cin >> charr1;
cout << "Enter another kind of feline: ";
cin >> str1; // use cin for input
cout << "Here are some felines:\n";
cout << charr1 << " " << charr2 << " "
<< str1 << " " << str2 // use cout for output
<< endl;
cout << "The third letter in " << charr2 << " is "
<< charr2[2] << endl;
cout << "The third letter in " << str2 << " is "
<< str2[2] << endl; // use array notation
// cin.get();
// cin.get();
return 0;
}
由上述代码可知:可以使用C-风格字符串来初始化string对象;可以使用cin将键盘输入存储到string对象中;可以使用cout显示string对象;可以使用数组表示法来访问存储在string对象中的字符。
string类的设计能自动处理string的大小,例如str1声明时是一个长度为0的string对象,但程序将输入读取到str1中时,将自动调整str1的长度。
string对象使用更方便安全。
// strtype2.cpp –- assigning, adding, and appending
#include <iostream>
#include <string> // make string class available
int main()
{
using namespace std;
string s1 = "penguin";
string s2, s3;
cout << "You can assign one string object to another: s2 = s1\n";
s2 = s1;
cout << "s1: " << s1 << ", s2: " << s2 << endl;
cout << "You can assign a C-style string to a string object.\n";
cout << "s2 = \"buzzard\"\n";
s2 = "buzzard";
cout << "s2: " << s2 << endl;
cout << "You can concatenate strings: s3 = s1 + s2\n";
s3 = s1 + s2;
cout << "s3: " << s3 << endl;
cout << "You can append strings.\n";
s1 += s2;
cout <<"s1 += s2 yields s1 = " << s1 << endl;
s2 += " for a day";
cout <<"s2 += \" for a day\" yields s2 = " << s2 << endl;
//cin.get();
return 0;
}
赋值,拼接,附加都变得简洁。
// strtype3.cpp -- more string class features
#include <iostream>
#include <string> // make string class available
#include <cstring> // C-style string library
int main()
{
using namespace std;
char charr1[20];
char charr2[20] = "jaguar";
string str1;
string str2 = "panther";
// assignment for string objects and character arrays
str1 = str2; // copy str2 to str1
strcpy(charr1, charr2); // copy charr2 to charr1
// appending for string objects and character arrays
str1 += " paste"; // add paste to end of str1
strcat(charr1, " juice"); // add juice to end of charr1
// finding the length of a string object and a C-style string
int len1 = str1.size(); // obtain length of str1
int len2 = strlen(charr1); // obtain length of charr1
cout << "The string " << str1 << " contains "
<< len1 << " characters.\n";
cout << "The string " << charr1 << " contains "
<< len2 << " characters.\n";
// cin.get();
return 0;
}
该段代码对比了string类对string对象的操作与原本C-风格字符串的操作函数。
函数strcopy()将字符串复制到字符数组中;函数strcat()将 字符串 加到字符数组末尾。
而在string类的语法中,则方便简单。
string类的I/O
// strtype4.cpp -- line input
#include <iostream>
#include <string> // make string class available
#include <cstring> // C-style string library
int main()
{
using namespace std;
char charr[20];
string str;
cout << "Length of string in charr before input: "
<< strlen(charr) << endl;
cout << "Length of string in str before input: "
<< str.size() << endl;
cout << "Enter a line of text:\n";
cin.getline(charr, 20); // indicate maximum length
cout << "You entered: " << charr << endl;
cout << "Enter another line of text:\n";
getline(cin, str); // cin now an argument; no length specifier
cout << "You entered: " << str << endl;
cout << "Length of string in charr after input: "
<< strlen(charr) << endl;
cout << "Length of string in str after input: "
<< str.size() << endl;
// cin.get();
return 0;
}
无需赘言。
其他形式的字符串字面值则有空再补充了。
4.4 结构 简介
结构是一种比数组更灵活地数据格式,同一个结构可以存储多种类型的数据,使得比较复杂的信息结合体可以存放在一个结构中。
结构是用户定义的类型,而结构声明定义了这种类型的数据属性。
创建结构分为两步:声明结构描述(秒数并表示了能够存储在结构中的各种数据类型),然后按描述创建结构变量(结构数据对象)
// structur.cpp -- a simple structure
#include <iostream>
struct inflatable // structure declaration
{
char name[20];
float volume;
double price;
};
int main()
{
using namespace std;
inflatable guest =
{
"Glorious Gloria", // name value
1.88, // volume value
29.99 // price value
}; // guest is a structure variable of type inflatable
// It's initialized to the indicated values
inflatable pal =
{
"Audacious Arthur",
3.12,
32.99
}; // pal is a second variable of type inflatable
// NOTE: some implementations require using
// static inflatable guest =
cout << "Expand your guest list with " << guest.name;
cout << " and " << pal.name << "!\n";
// pal.name is the name member of the pal variable
cout << "You can have both for $";
cout << guest.price + pal.price << "!\n";
// cin.get();
return 0;
}
结构的声明分为外部声明与内部声明。
// assgn_st.cpp -- assigning structures
#include <iostream>
struct inflatable
{
char name[20];
float volume;
double price;
};
int main()
{
using namespace std;
struct perks
{
}
inflatable bouquet =
{
"sunflowers",
0.20,
12.49
};
inflatable choice;
cout << "bouquet: " << bouquet.name << " for $";
cout << bouquet.price << endl;
choice = bouquet; // assign one structure to another
cout << "choice: " << choice.name << " for $";
cout << choice.price << endl;
// cin.get();
return 0;
}
将结构作为参数传递给函数;让函数返回一个结构;同类结构之间的赋值(成员赋值)
可以同时完成定义结构与创建结构变量的工作:
struct perks
{
int key_number;
char car[12];
}mr_simth, ms_jones;
struct perks
{
int key_number;
char car[12];
}mr_glitz =
{
7;
"packard";
}; //注意这里需要写分号
一般将定义与声明分开,便于阅读和理解。
还可以声明没有名称的变量——省略名称,同时定义一种结构类型和一个这种类型的变量
struct
{
int x;
int y;
}position;
这种形式怎么说呢,鸡肋?
// arrstruc.cpp -- an array of structures
#include <iostream>
struct inflatable
{
char name[20];
float volume;
double price;
};
int main()
{
using namespace std;
inflatable guests[2] = // initializing an array of structs
{
{"Bambi", 0.5, 21.99}, // first structure in array
{"Godzilla", 2000, 565.99} // next structure in array
};
cout << "The guests " << guests[0].name << " and " << guests[1].name
<< "\nhave a combined volume of "
<< guests[0].volume + guests[1].volume << " cubic feet.\n";
// cin.get();
return 0;
}
结构数组:创建元素为结构的数组,方法与创建基本类型数组完全相同,初始化可以结合初始化数组的规则——用逗号分隔每个元素的值,并将这些值用花括号括起。
上述代码展示了如何定义与创建结构数组并初始化。
4.5 共用体
共用体union是一种数据格式,可以存储不同的数据类型,但是只能同时存储其中的一种类型。
结构可以同时存储int、long 和 double,共用体只能存储int、long 或 double。
struct widget
{
char brand[20];
int type;
union id
{
long id_num;
char id_char[20];
}id_val;
}
...
widget prize;
...
if(prize.type==1)
cin >>prize.id_val.id_num;
else
cin >>prize.id_val.id_char;
由于共用体每次只能存储一个值,因此它必须由足够的空间来存储最大的成员,所以共用体的长度为其最大成员的长度。
上述代码显示了共用体的用途之一:当数据项有多种格式表示某项数据时,可以节省空间。
匿名共用体anonymous union 没有名称,使用时也有些许变化:
struct widget
{
char brand[20];
int type;
union
{
long id_num;
char id_char[20];
};
};
...
widget prize;
...
if(prize.type==1)
cin >> prize.id_num;
else
cin >> prize.id_char;
共用体常用于节省内存;也用于操作系统数据结构或硬件数据结构。