要创建数组可以使用声明语句。数组语句应该包含三个要素:
typeName arrayName[arraySize]
具体例子如下:
short month[12];
另外对数组使用sizeof函数可以得到数组的字节数。
数组的初始化方式如下:
int demo1[]{ 1,2,3,4 };//等价于int number[4]={ 1,2,3,4 };
int demo2[4]{};//在大括号中什么都不加代表全0
如果只初始化一部分元素则剩下的元素默认为0。
C++中字符串有两种形式,一种是C-风格字符串,另一种基于string类库的字符串。
对于C-风格字符串,其最后总是以’\0’结尾,因此在确定字符串数组的最短长度时,’\0’字符也应该被计算在内。如下所示:
char animal1[4] = { 'd','o','g','s' };//仅仅是一个char数组
char animal2[4] = { 'c','a','t','\0' };//是一个字符串
cout << animal1 << endl;//使用cout会导致持续输出直到遇到\0
cout << animal2 << endl;//正常输出
其初始化还有更加简便的方法——字符串常量:
char animal3[] = "fish";
cout << sizeof(animal3) << endl;
//长度为5,最后一位为0,如果数组长度大于字符串常量长度,多余的位置全部被赋值为\0;得出结论:数组大小至少等于字符串长度+1
这里"fish"就是一个字符串常量,其表示的实际上是该字符串常量(由’f’,‘i’,‘s’,‘h’,’\0’组成)所在的内存地址。
字符串可以进行拼接,将需要拼接的字符串放在一起即可,如下所示:
cout << "hel""l""o" << endl;//字符串常量的拼接
对于C-风格字符串我们常常使用sizeof和stlen计算其长度,其区别为:
将输入的字符串传入字符串数组中有多种方式,第一种就是使用cin,但是该方法有一个缺陷,那就是直接使用cin,其传入的字符串会在遇到空白时结束,空白后面的剩余字符串无法被存储到字符串数组中。
另一个方法是使用cin.getline,第一个参数为用于存储输入字符串的数组名称,第二个为需要读取字符串的长度(包括’\0’)。
char name[20];
cin.getline(name,20);//读取20个字符保存在name中,如果在读取到第20个字符之前遇到了换行符则提前停止,并将遇到的换行符丢弃;
cout << name;
与cin.getline类似的还有cin.get。cin.get遇到换行符停止后不会丢弃换行符。因此连续两个get可能会导致第二个get无法读取到任何字符(碰到换行直接停止)。如果get不包含任何参数则会读取一个字符,使用cin.get()可以来处理换行。
char name[20];
cin.get(name, 20).get();//cin.get()会返回一个cin对象,之后调用get读取换行符
int year;
cin >> name;
cout << name << "len:"<<strlen(name)<<endl;
对于基于string类库的字符串,使用前需要包含头文件stirng,未初始化之前长度为0,使用size()方法可以获取字符串长度,其大小根据所赋的值由系统自行调整,所以更加安全。
其初始化可以直接使用字符串常量。
通过输入来为字符串赋值也有多种方法,其中一种就是直接使用cin,其缺点与C-风格字符串使用cin来初始化类似,遇到空白会自动停止。
也可以使用getline来对string进行初始化,其输入的参数与cin.getline有区别,如下所示:
string str;
getline(cin, str);//第一个参数为输入源,第二个为赋值对象
C++ 11中加入了原始字符串的概念,原始字符串中不会进行转义操作。其格式为以大写R开头,以"(开头,以)"结尾。
cout << R"(this \n is a raw string)" << endl;;
结构的定义如下所示:
struct inflatable
{
char name[20];
float volume;
double price;
};
结构体的声明、内部元素的访问以及结构体数组的声明如下所示:
inflatable hat = {"lee",1.88,29.99}; //初始化,等号也可以省略,不允许缩窄转换
hat.volume;//访问结构体中的元素
//结构数组与其他数组类似,构造方式如下:
inflatable guest[2] = {
{"Bambi",0.5,21.99},
{"Godzilla",2000,565.99}
};
共用体Union,可以储存不同类型的数据,但是同时只能存储一种,这样可以达到节省内存的效果。
其声明形式如下:
union one4all
{
int int_val;
long long_val;
double double_val;
};
可以使用上述共用体存储int、long或者double类型的数据,但是同一时刻只能存储一种数据。可以加入到结构体中。如果想要
匿名结构体:没有名称,里面的成员是相同内存地址处的变量,其访问方式如下:
struct widget
{
char brand[20];
int type;
union
{
long id_num;
char id_char[20];
};
};
widget prize;
if (prize.type == 1) //直接将两个变量看作是prize的两个成员
{
cin >> prize.id_num;
}
else
{
cin >> prize.id_char;
}
使用枚举类型可以轻易的定义常量,定义方式如下:
enum spectrum {red,orange,yellow,green,blue,violet,indigo,ultraviolet};
该语句首先定义了一种枚举类型spectrum,其中read、orange、yellow则作为符号常量,分别代表0~7。
spectrum band;
枚举类型的值可以被提升为int型,但是不能反向转换。因此该变量可以使用red、orange等等变量为其赋值,但是不可以使用0~7为其赋值(除非使用强制转换)。如果只打算使用定义的常量而不需要使用枚举类型的变量,则枚举类型的名称可以省略:
enum {paramsError,memoryError,unkonwError}; //0~2分别代表参数错误、内存错误以及未知的错误
cout << memoryError;
在C++在变量名前加上&符号可以查看该变量名的地址,储存着内存地址的变量称为指针,‘*’代表解除引用运算符,将其加在指针变量前则可以查看储存在对应内存地址的值。
声明指针的格式为:
int* p1, * p2;//每声明一个指针都要在前方加上*
int foo = 100;
p1 = &foo;//也可在声明时直接赋值
指针也存在着安全隐患,声明一个指针只会为其分配用于存储地址的内存,但是不会为其分配指针所指向数据的内存,因此在对指针使用解除引用运算符(*)之前一定要对其进行初始化。
如果想对指针直接使用数字赋值,则在这之前需要进行强制的类型转换:
int* p = (int*)0xB8000000;
我们可以使用new进行动态内存的分配,在使用完成后应该使用delete对分配的内存进行释放,数组和实体的delete方式有区别:
int arraySize = 20;
int* pArray = new int[arraySize];//为数组动态分配内存
delete []pArray;//释放内存
int* p = new int;//为一个实体分配内存
delete p;//释放内存
总之,new和delete之前必须遵守如下规则:
动态数组的访问:访问方式与数组的访问方式类似,事实上C++中数组和指针基本等价,不同的地方是可以修改指针的值但是不可以修改数组名的值,另外对数组使用sizeof函数返回的将是数组的长度,而对指针使用则返回的是指针的长度。对于整数变量,其加1之后其值会加1;而对于指针,其加1之后增加的两等于它指向的类型的字节数
pArray += 1;//此时应该指向的是第二个元素
pArray -= 1;//此时应该指向的是第一个元素
如果使用cout显示字符串指针,则会显示字符串指针指向的内容,如果需要显示字符串指针的地址则需要进行强制转换。
char fruit[10] = "apple";
char* pointer;
pointer = fruit;
cout << pointer << (int*)pointer << endl; //第一次输入的是apple,第二次输出的是地址
pointer = new char[strlen(fruit) + 1];
strcpy(pointer,fruit);//将fruit储存的字符串复制到pointer指向的内存地址
cout << pointer << (int*)pointer << endl;//发现地址改变
char* newPointer = new char[10];
strcpy(newPointer, "hello");
cout << newPointer << endl;
对于指向结构体的指针,访问结构体成员应该使用->,如:inflatable *ps = new inflatable; ps->price//访问成员price。