C++ Primer 学习笔记与思考_5 数组和动态数组易错点解读

1. 数组的定义

数组定义中的类型名可以是内置数据类型或者类类型;除了引用之外,数组元素的类型还可以是任意的复合类型。另外,非const变量以及要到运行阶段才知道其值的const变量都不能用于定义数组的维数。

#include <iostream>
#include <string>
#include <cstring>
#include <bitset>
using namespace std;
int main()
{
	const int MAXN1 = 5;
	int MAXN2 = 6;
	const int MAXN3 = get_size();

	int num1[MAXN1];  // ok
	int num2[MAXN2];  // error,non const variable
	int num3[MAXN3]; // error size not known until run time

	return 0;
}

虽然MAXN2使用字面值常量初始化的,但是他是一个非const对象,只有在运行的时候才能够获得他的值,因此定义维数是非法的。同理MAXN3的值也要等到运行时才能够得出get_size()。

2.数组的初始化

当我们没有显式提供数组的初值时,是怎样的初始化规则呢?

-- 在函数体外定义的内置数组,其元素均初始化为0。

-- 在函数体内定义的内置数组,其元素无初始化。

-- 不管数组在哪里定义,如果其元素类型为类类型,则自动调用该类的默认构造函数进行初始化;如果该类没有默认构造函数,则必须为该数组提供显示初始化。

#include <iostream>
#include <string>
#include <cstring>
#include <bitset>
using namespace std;
int a[5];
int main()
{

	int b[5];
	for (int i = 0; i < 5; i++)
		cout << a[i] << " ";
	cout << endl;
	for (int i = 0; i < 5; i++)
		cout << b[i] << " ";
	cout << endl;

	return 0;
}

所以在函数内部定义的数组一定要初始化,否则这些操作没有意义。

3.赋值和复制

与vector不同,一个数组不能用另一个数组初始化,也不能将一个数组赋值给另一个数组,这些操作是非法的。

#include <iostream>
#include <string>
#include <cstring>
#include <bitset>
using namespace std;
int a[5];
int main()
{

	int b[5];
	b = a; //error
	return 0;
}

(一)动态数组:

动态分配的数组不必在编译的时候知道其长度,可以在运行的时候才确定长度,而且必须显式释放它。动态分配的对象存放于堆中。

动态分配的数组没有名字,只能通过其地址间接地访问堆中的对象。

此外,数组的const对象必须进行初始化,实际上,我们只要记住,定义动态数组时最好加上( )进行初始化。

如下例,当get_size()返回的n是0时,也是正确的,我们允许动态分配空数组,但是不能进行解引用操作,因为毕竟没有元素。

数组,必须用delete [] *p显式释放。 []不可少。

#include <iostream>
#include <string>
#include <cstring>
#include <bitset>
#include <algorithm>
using namespace std;
int main()
{
	int *p1 = new int[10];  //未初始化
	int *p2 = new int[10](); //初始化为0
	int num = 5;
	int *p3 = new int[num];

	const int *p4 = new const int[100];   //error,必须初始化
	const int *p5 = new const int[100](); //ok

	size_t n = get_size();
	int *p = new int[n]; //ok,but if n=0,then & is unable to use

	delete[] p;
	return 0;
}

(二)混合使用string和c风格字符串

string可以用c风格字符串初始化,但是无法用string对象初始化字符指针,可使用c_str进行转换。

  string s = "fdsafd";
	//char *p = s; // error
	const char *p = s.c_str(); //ok, return const

/ 注意,如果之后s发生改变,那么*(p+n)取出的是变化后的值

//原返回数组失效,如果想使用原数组,最好复制保存下来

(三)最后稍微提一下多维数组和指针之间的一点问题,看程序吧:

#include <iostream>
#include <string>
#include <cstring>
#include <bitset>
#include <algorithm>
using namespace std;
int main()
{
	int ia[3][4];
	int(*ip)[4] = ia; // ip points to an array of 4 ints
	//int *ip[4];  // array of points to int
	//ip = &ia[2]; 

	for (ip = ia; ip != ia + 3;ip++)
	for (int *q = *ip; q != *ip + 4; ++q)
		cout << *q << endl;

	return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-04 23:03:27

C++ Primer 学习笔记与思考_5 数组和动态数组易错点解读的相关文章

C++ Primer 学习笔记与思考_5 bitset你用的正确吗?

bitset类型比整型值上的低级位操作更容易使用,简化了位集的处理.在定义bitset时,要明确bitset含有的位数,在尖括号内给出它的长度值. 注意位集合的编号从0开始,低位存低阶位. 最重要的一个问题:bitset的下标是从右边开始的!!!!很多人的博客都写错了,希望引起大家注意. 1.使用unsigned long值初始化bitset对象 若bitset类型长度大于unsigned long值的二进制位数,则其余的高阶位将置为0:若bitset类型长度小于unsigned long值的二

C++ Primer学习笔记与思考_1----变量和基本类型

C++ Primer学习笔记与思考_1----变量和基本类型 类型有什么作用? 类型告诉我们数据代表什么意思以及可以对数据执行哪些操作. 一:基本内置类型 C++标准规定了每个算术类型的最小存储空间,但是它并不阻止编译器使用更大的存储空间.对于int,几乎所有的编译器使用的存储空间都比所要求的大. 字符类型有两种char和wchar_t,其中wchar_t用于扩展字符集,比如汉字和日文,这些字符集中的一些字符不能用单个的char表示.  c++ primer第32页      和其他类型不同,c

C++ Primer 学习笔记与思考_9 表达式中过的那些容易忽略的地方

(一)移位操作符用于IO 输入输出标准库分别重载了位操作符>>和<<用于输入和输出.并且像其他的二元操作符一样,移位操作符也是左结合的,这也正好说明了我们为什么可以把多个输入输出操作连接为单个语句: cout<<"hi"<<"there "<<endl; 将其执行为: ( (cout<<"hi")<<"there" )<<endl;

C++ Primer 学习笔记与思考_7 void和void*指针的使用方法

(一)void的含义 void的字面意思是"无类型",void差点儿仅仅有"凝视"和限制程序的作用,由于从来没有人会定义一个void变量,让我们试着来定义: void a; 这行语句编译时会出错.提示"illegal use of type 'void'".只是.即使void a的编译不会出错.它也没有不论什么实际意义. void真正发挥的作用在于: (1) 对函数返回的限定: (2) 对函数參数的限定. int f(void); equal t

C++ Primer 学习笔记与思考_4 ---getline&amp;&amp;size_type&amp;&amp;const_iterator混搭

(一)getline的用法 我们在使用string读入字符串的时候,其输入操作符: * 读取并忽略开头所有空白字符(如空格,换行符,制表符) * 读取字符直至再次遇到空白字符,读取终止. 所以如果我们输入"hello world",string将只输出hello. 为了解决这个问题,我们引入getline函数,它接受两个参数:一个输入流对象和一个string对象,但是getline会读取换行符,保存到string中时却忽略换行符,所以string中是空串. 我们通常用getchar()

C++ Primer 学习笔记与思考_3 ---头文件那些事儿(extern)

(一)extern在头文件里的使用方法 由于头文件包括在多个源文件里.而且变量的定义仅仅能出现一次,所以在头文件里. 仅仅能够声明不能够出现定义. 我们能够在头文件里用extern声明全局变量,这样在包括此头文件的cpp 中可直接使用 声明过的这个全局变量(例如以下面程序中的变量a). 可是有三个例外: 1.头文件里能够定义类 2.值在编译时就已经知道的const对象 3.能够定义inline函数 extern int ival; // yes extern int ival=1; //erro

C++ Primer 学习笔记与思考_10 类型转换易错处大总结

(一)隐式转换 先来看一组样例: int ival=0; ival=3.451+3; 首先做加法操作,操作数分别为int和double类型,c++并没有直接把两个数加在一起,而是提供了一组转换规则,以便在执行算术操作之前,将两个操作数转换为同一种数据类型.在这里是自动执行的,所以他们被成为隐式转换. 因为 C++定义了算术类型之间的内置转换以尽可能防止精度损失,所以这里3转换为double型相加得6.451.但是下一步赋值的时候,右操作数进行截断处理,对此精度会有损失,编译器也会给出警告. 2.

C++ Primer 学习笔记与思考_7 void和void*指针的用法

(一)void的含义 void的字面意思是"无类型",void几乎只有"注释"和限制程序的作用,因为从来没有人会定义一个void变量,让我们试着来定义: void a; 这行语句编译时会出错,提示"illegal use of type 'void'".不过,即使void a的编译不会出错,它也没有任何实际意义. void真正发挥的作用在于: (1) 对函数返回的限定: (2) 对函数参数的限定. int f(void); equal to in

C++ Primer 学习笔记_20_类与数据抽象(6)_深拷贝与浅拷贝、空类与空数组

C++ Primer 学习笔记_20_类与数据抽象(6)_深拷贝与浅拷贝.空类与空数组 一.深拷贝与浅拷贝 浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换而言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象. 深拷贝:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量.那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象.换而言之,深拷贝把要复制的对象所引用的对象都复制了一遍. 浅拷贝可