C++:数组、字符串、结构体、共用体、枚举和指针

C++:数组、字符串和指针

  • 字符串
  • 字符串
    • C-风格字符串
    • 基于string类库的字符串
  • 结构体
  • 共用体
  • 枚举
  • 指针和自由存储空间

字符串

要创建数组可以使用声明语句。数组语句应该包含三个要素:

  • 存储在每个元素中的值的类型;
  • 数组名;
  • 数组中的元素数
    通用格式为:
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-风格字符串

对于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计算其长度,其区别为:

  • sizeof运算指出的是整个字符串数组的长度,即初始化和未初始化的元素长度之和,单位为字节;
  • strlen返回的是储存在字符串数组中字符串的长度(不包括’\0’),因此字符串的实际长度应该是strlen的返回值加1。该函数的原理是从第一个元素开始依次扫描直到遇到’\0’,因此如果将未初始化的字符串数组传入该函数,由于值不确定,返回的长度也是不确定的。

将输入的字符串传入字符串数组中有多种方式,第一种就是使用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类库的字符串

对于基于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之前必须遵守如下规则:

  • 不要使用delete来释放不是new分配的内存;
  • 不要使用delete释放同一块内存两次
  • 如果使用new[]为数组分配内存,则应该使用delete []来释放
  • 如果使用new为一个实体分配内存,应使用delete(没有方括号)来释放。
  • 对空指针(指针中存储的地址为0)使用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。

你可能感兴趣的:(C++,c++,指针,字符串)