一、前言
2018年一月,闭关九日,进度比预期慢了许多,但这几日阅读《C++ primer plus》以及诸位前辈的技术博客也学到了许多新知识,今晚将这几日收获做了简单总结,是一些十分浅显的C++入门知识,其中省略了一些c语言的基础知识。
二、预备知识
1、c++的三种模式
【1】OPP面向对象编程,是管理大型项目的工具。
【2】泛型编程(c++模板),用于执行常见任务。
【3】过程化编程(c语言)
2、类是一种规范,描述新型数据结构,同时规范有哪些数据及如何对其操作。对象是根据这种规范的特定数据结构。
即类指某方面类别,对象指具体情况。如 类:头衔,对象:副主席。
3、opp的设计方法首先设计类,是从低级组织(类)到高级组织(程序)的,叫做自下而上的编程,好处在于创建可重用的代码。
4、int main()是函数头,作为与操作系统的接口。
5、位于函数名前的类型名叫做返回类型,函数名后的括号叫做形参列表,空括号意味着不接受任何参数。参数(argument)指一个函数传递给另一个函数的信息。
6、int main(void)在c++中指不接受任何参数,与空着等效。C语言中,空着表示沉默。
7、常规编程必须有main函数,有极少数例外情况,非独立程序(如DLL模块),专用环境的程序(如机器人)。
8、头文件,iostream 中的io指输入输出,使用cin cout必须使用iostream。ANSI/ISO委员会同意不使用扩展名.h,所以应使用名称空间编译指令,使他对程序可用。名称空间支持是C++特性。名称空间:namespace。一般用using namespace std; 就是指用名称空间中的std。
这叫做using编译指令。
9、类、函数、变量为C++编译器标准组件,他们都被放置于名称空间(std)中,这实际上是一种偷懒行为,且违背了名称空间的初衷。若不偷懒,应使用类似于using std::cout这样。
10、cout 和 cin的最大区别是:cout后面跟的是<<,表示是输出内容。
比如cout<<”孙dua真高啊”<<endl; 代表输出文字 孙dua真高啊,并且换行(endl;)。
而cin 表示的是输入,后面跟的是>>, 比如:
int height; cout<<”输入孙刚新的身高<<endl; cin >> height; cout <<”孙刚新”<<height<<”了”<< endl;
代表先显示让你输入孙刚新的身高,然后height 被你输入数字赋值。
这里面的>>和<<表示信息流的方向。
11、C++中可以连续使用赋值。
Licaodan = sungangxin = 2.88;
顺序为从右到左。
12、cout是ostream的类对象,cin是istream的类对象,在iostream中定义,类描述指定了可对类对象执行的所有操作。
13、C++的两种发送消息方式。(1)使用类方法【即函数调用】(2)重新定义运算符。
14、函数是用于创建C++的模块,参数是发送给函数的信息,返回值是从函数中返回的值。C++允许在任何地方声明新变量,还允许在创建时对他赋值。函数不允许嵌套,所有函数平等。
15、应在首次使用前提供原型,把原型放在main函数前面。(库函数),自己定义的放在main后面。
三、处理数据
1、变量名有三点注意。
(1)只能用字母、数字、下划线(2)第一个不可为数字(3)区分大小写。
在这里也要提一句,相信看这一份笔记的同学和我现在一样都是初学者,在编程中对变量的命名最好有自己的规范,整洁优美,我选用的是驼峰命名法。
2、以两个下划线或下划线和大写字母开头的名称被保留给编译器使用。以一个下划线开头的名称保留给编译器,用作全局标识符。(这两处编译器,在c++ primer plus中被翻译为实现,指编译器及其使用资源,我在这里直接称作编译器。)
3、climits定义了符号常量。
4、sizeof对类型名使用时应放入括号内,如 sizeof (int),对变量名使用时,括号可有可无。
5、若使用表达式来初始化变量,条件是当程序执行到该声明时,表达式中所有的值都是已知的。
6、C++中有一种c没有的初始化语法,int nb(111);
7、C++11中有一种初始方式用于数组和结构,但在c++98中,也可用于单值变量。
Int rheas = {24};(1)等号可以省略(2)大括号中可不包含任何东西,初始化为零。(3)可防范类型转换错误。
注意,一些oj和比赛不支持c++11 (比如蓝桥杯)。
8、基本类型有两组,整型和浮点类型。
整型从小到大排列:
bool(bool只有1和0,即true or false)、char、signed char、unsigned char、short、unsigned short、int、unsigned int、long、unsigned long
以及c++11新增的long long和unsigned long long。外加一种wchar_t
为节省内存(只有大型整型数组时),使用short。如只需一个字节,使用char。
C++11还有新增的类型char16_t和char32_t, 这两个的宽度足以用于存储16位和32位的字符编码。
在长度上,short至少为16位,int大于等于short的长度,long最少32位,long大于等于int的长度。
字符可以通过其数值编码显示,而I/O系统决定编码到底显示为字符还是数字。(比如cout和cin)
而浮点类型三种,按长度排序float,double,long double。即float不比double长度长,而double不比float长。通常来说float使用32位内存(6位有效数字),double占用64位内存,而long double占用更多内存。
浮点数的位指有效位,即数字中有意义的位,举例子很好弄懂了。12345有5个有效位,12300的却只有2个有效位,12045有5个有效位,12040有4个有效位。
也就是说,数字中从第一个非0数字开始算起,到最后一个非0数字为止,中间的数字都属于有效数位。至于中间有没有小数点,并不重要。例如12345和1.2345的有效数位是一样的。
而浮点数的表示方法有两种:
<1>带小数点,并且至少带小数点后一个0。
例如:3.1415 ; 165.1 ; 0.331 ; 6.0等
<2>不带小数点,带E,具体方式是:aEb——(E或者e表示10,b表示10的b次方,aEb表示数字a乘以10的b次方)
例如:1.2E5表示1.2*100000;3.11E-4表示3.11乘以10的-4次方(即0.0001);-10e4表示-10乘以10000等。
b理解为小数点往右(b为正时)或者往左(b为负时)移动b位。
9、进制一般使用三种,但无论使用哪种,在计算机眼里都是二进制(红粉骷髅hhhh),不需要白费力气非要翻译成十进制。
(1)十进制:基数为10。 第一位是1~9。例如11他就是十进制的11;
(2)八进制:基数为8(逢8进一)。第一位是0,例如033,是十进制的27;
(3)十六进制:基数是16(逢16进一)。第一位是0X或者0x,例如0X11,是十进制的17(1*16+1)。
这三种的控制符分别为 dec、hex、oct。
cout输出时,默认使用十进制。十六进制一般用来表示内存地址,而内存地址没有符号,因此unsighed int 比long更适合用来表示16位地址。
见以下范例。
#include <iostream> using namespace std; int main() { int a = 123; int b = 0123; //b为八进制的0123 int c = 0x123; //c为十六进制的0x123 cout << "a= " << a << endl; //显示a的结果 cout << "b= " << b << endl; //显示b的结果(注:转为十进制显示) cout << "c= " << c << endl; //显示c的结果(注:转为十进制显示) cout << endl; cout << hex; //告诉程序你应该以十六进制(hex)方式显示整数 cout << "a= " << a << " ←这里以十六进制的方式显示a" << endl; //显示a的结果(注:转为十六进制显示) cout << oct; //告诉程序你应该以八进制(oct)的方式显示整数 cout << "a= " << a << " ←这里以八进制的方式显示a" << endl; //显示c的结果(注:转为十进制显示) cout << dec; //告诉程序你应该以十进制(dec)的方式显示整数 cout << "a= " << a << " ←这里以十进制的方式显示a" << endl; //显示a的结果 system("pause"); return 0; }
输出结果为:
a= 123
b= 83
c= 291
a= 7b ←这里以十六进制的方式显示a
a= 173 ←这里以八进制的方式显示a
a= 123 ←这里以十进制的方式显示a
请按任意键继续. . .
(1)八进制那里的显示,前面没有0。但是输入的时候要加0,不然程序以为你输入的是十进制。
(2)cout<<hex; 并不会显示任何东西,只是单纯的告诉编译器,以什么方式显示数字。(3)(3)标识符hex存在于命名空间std中,因此,变量名不能用hex,除非你不用using namespace std; 这行命令。另外,也只有用了这行命令,你才能简写hex,dec,oct。否则你就要用std::hex、std::dec、std::oct 等。
10、比如有一个short a; 我们有时候会给前面加上一个unsigned ,就变成了unsigned short a;
这个时候呢,因为short是16位,16位能表示出65536种数字。如果是short a;他的范围就是-32768 ~ +32767。加上signed也是一样的,如果加上unsigned呢,他的范围就变成了0 ~ 65535 了。
ps:unsigned 本身是 unsigned int 的缩写。
例如short a; 这里a的范围是-32768 ~ +32767
而unsigned b; 这里b的范围是0 ~ +65535
short a(以下是a的取值) |
a-1 |
a+1 |
0 |
-1 |
1 |
-32768 |
32767 |
-32767 |
32767 |
32766 |
-32768 |
a的取值范围 |
-32768 ~ +32767 | |
| ||
unsigned b(以下是b的取值) |
b-1 |
b+1 |
0 |
65535 |
1 |
1 |
0 |
2 |
65535 |
65534 |
0 |
b的取值范围 |
0 ~ 65535 |
即 cout.put ()通过类对象cout使用函数put,可以用来替代<<运算符。
13、用cout.put(); 如果里面是变量,则显示变量的ASCII值所代表的字符,如果是整数,则显示ASCII值为该整数所代表的字符。里面先计算整数,再将整数转为字符输出。
用cout.put(''); 如果单引号里面是数字,则显示最后一位数字(小数也显示最后一位),如果单引号里面是字母,则显示字母。因为'a'是一个文本,所以直接显示这个文本,而不是转成ASCII值再输出字符。
14、转义序列。
换行符 \n
水平制表符 \t
垂直制表符 \v
退格键 \b
回车键 \r
振铃(就是会bi~一声) \a
反斜杠 \\
15、将转义字符作为字符常量的时候,用单引号,用在字符串中的时候,用双引号,字符串会在字符的最后加上空字符\0。
16、浮点运算的速度通常比整数慢,且精确度会降低。
17、应在声明中对const初始化。
18、强制转换有两种格式。
(1) (long)thorn 这种来自C语言 (2)long (thorn)这种是纯C++
19、C++引进了四种强制类型转换运算符,static_cast<>可用于值转换。如static_cast<long>(thorn)
20、类型转换,已经知道,int,double,short,char,long,float等多个类型,其中有整型,也有浮点类型。然而,在实际应用中,经常会涉及到类型转换问题。
例如 double a=13.33333333; float b=a;
这个时候,实际上就是把double a的值赋值给float b,而又因为变量a和变量b的精度不同,所以在赋值的时候,C++需要将其类型进行转换。
又比如,两个类型为short的变量相加的结果,可能和2个long相加的结果不同(因为可能存在溢出,例如unsigned short的最大值为65535,如果2个short整型的65535相加,且将结果赋值给short类型的变量,新变量的数值必定不是65535*2)。
21、在以下三种情况,C++自动执行类型的转换(即无需我们进行转换)
①将一种算数类型的值赋给另外一种算数类型的变量时,C++自动进行转换。例如将int a=54321赋值给short b时,b的值则不为54321。但是如果将short b=1234赋值给int a时,a的值依然为1234。
即表示范围更大的类型(如int)赋值给表示范围更小的类型(如short)时,数值可能发生变化。超出部分通常只复制靠右边的部分。
表示范围更小的类型(如char)赋值给表示范围更大的类型(如short)时,数值不会发生变化,只是会占用更多的字节。
浮点类型转换成整型(如double转换为int),则自动丢失小数部分。
其他类型赋值给boo时,非0值被转换为true(1),0值为false(0)。
22、c++11中也新增了auto工具,会根据初始量的类型推断变量的类型。
auto a=1; 变量a类型为int
auto b=2.0; 变量b类型为double
auto c=3.1L; 变量c类型为long double——因为后缀加L表示是long double类型
auto d=4.333F; 变量d类型为float——因为后缀加F(f)表示是float类型
注意:auto a=0; 的时候,变量a的类型为int
按照说明,auto在处理简单变量的时候,使用的意义并不大,处理复杂类型,如标准模块库(STL)中的类型时,自动类型推断的优势才能显现出来
四、复合类型
1、数组(aaray)是一种数据格式,能够存储多个同类型的值。创建数组的声明应指出以下三点:
①存储在每个元素中的值的类型;②数组名;③数组中的元素数。
即 typeName arrayName【arraySize】
2、在初始化的时候,可以不用给所有数组中的值进行赋值,没有赋值的数组中的数,被认为是0。
3、确保程序只使用有效的下标值。
4、不能将一个数组中所有数直接复制给另外一个数组中的数;
5、一般不要让编译器计算数组中元素的个数,因为可能你自己在输入的时候漏掉一个或者几个数,这就麻烦了。但是这种方法,对于将字符数组初始化为一个字符串来说比较安全。
6、可以用sizeof来确认你数组中的元素数是否如你计划中的数量。比如int变量,一个是4字节,如果你数组中有5个数,那么sizeof数组应该为20个字节。
Int numElements = sizeof things / sizeof (int);
7、C++11可以省略等号,可以不在大括号内包含任何使用东西使所有元素为零。
8、STL提供了数字替代品-----模板类vector
9、字符串是存储在内存的连续字节中的一系列字符。因为是内存中的连续字节中的一系列字符,意味着可以将字符串存储在char数组中。(因为字符是被存储为char类型的,又因为是连续的,可以认为是若干char类型的字符所组成的数组)。
10、字符串需要以空字符结尾,写作\0,其ASCII码为0,用于标记字符串的结尾。如果没有以空字符结尾的话,是不行的。——因为可能会显示更多的(不在字符串内容中)的字符。
11、字符串需要用char的数组,但是 不可以将字符数组当做字符串处理。
12、C++处理字符串有两种方式。
(1)来自c语言的c-风格字符串 (2)基于string类库的方法。(对于string,可以直接使用“ ”。
13、区分字符常量‘ ’和字符串常量“ ”,后者包括空字符\0,且指内存地址。
14、在数组中使用字符串方法有两种,(1)将数组初始化为字符串常量 (2)用cin输入。
15、头文件<string>中包含,strlen()确定长度,strlen只计算可见的,不包括空字符,数组长度需要为strlen+1。sizeof确定长度时包括空字符。
16、由于cin的机理,在读取用户输入的时候,对空格,tab,或者换行符没有响应,读取到空格,即以上三个情况后,自动停止读取。需要使用cin的较高级特性,或者采用getline()或者get()来读取一行。
getline()将读取到换行符位置,通过确认回车输入的换行符来确定输入的结尾,并将换行符丢弃(即不会读取换行符),将其换成空字符。而get()读取到换行符为止,并储存换行符。
17、调用getline()来读取一行,需要使用cin.getline(数组名,读取字符数),注意这里的读取字符数需要+1来包括空位符。
18、使用cin.getline()的确可以读取一整行,并且显示输出结果的时候,不会帮我们换行。
而cin.get()函数与cin.getline()函数的区别也在这里体现。
19、假如我们输入内容为:abc(回车),使用cin.getline()的时候会读取abc,然后把(回车)丢掉,这样当我们下次输入def(回车)的时候,将读取def,然后再丢掉(回车)。
假如我们使用cin.get(),当输入abc(回车)的时候,先读取abc,但是并没有把(回车)丢掉,下一次输入cin.get()的时候,会首先读取回车,然后因为发现回车了,认为是最后一个字符,所以停止读取后面的内容,比如说def————准确说,这里的cin.get()是带参数的cin.get()会这样。
假如在两个带参数的cin.get()之间,加入一个不带参数的cin.get(),这个不带参数的cin.get()便可以处理掉那个未被读取的换行符。
举例
cout << "请输入你的身高:"; cin.get(height, 20); cin.get(); //无参数的用于读取换行符 cout << "请输入你喜欢的女生名字:"; cin.get(girl, 20);
也可以写为:
cout << "请输入你的身高:"; cin.get(height, 20).get(); //在第一个cin.get()后面,加上.get()用于读取换行符 cout << "请输入你喜欢的女生的名字:"; cin.get(girl, 20);
两个效果是一样的。和上面cin.getline()效果也相同。
注意,也可以这样输入:
cin.getline(height, 20).getline(girl,20 ); //连续读取两行输入的文字,并且分别储存于两个字符串之中
不过我觉得,这些知识了解就好,这么写不美观也不便于理解,没什么太大意义,用来应试考试的填空题倒是可以hh
20、假如用户输入的字符串超出字符串预设的长度(即比分配的长),那么多出来的部分将被下一个字符串所读取(在使用cin.get()的情况下),或者被舍弃,但下一个字符串用户需要输入的时候无法输入(在使用cin.getline()的情况下)。
21、string类可以将字符串视为输入到一个简单变量中,像使用一个变量那样使用他(即对象),而不是像字符数组来储存字符串。
前提是:使用头文件#include<string>,另外,string类位于名称空间std中,因此需要使用using namespace std;或者std::string 或者using std::string。
在其他方面,又类似字符串那样使用。例如,在声明并初始化的时候,需要给字符串加上双引号或者单引号,像 string abc="aaa"; 这样。
22、想要读取输入中的空格,就使用函数getline(cin,string变量名)。
23、string类的赋值:
我们知道,当有两个数组aaa,bbb时,其中aaa已经被初始化,但是bbb不能通过赋值的方式,将aaa中的字符串赋给数组bbb,如bbb=aaa;这种方式是错误的。
但是当有两个string类型的变量ccc和ddd时,假如ccc={"abbb"};已被初始化,是可以通过赋值的方式,将ccc的字符串赋给ddd,如ddd=ccc;cout<<ddd;的输出结果是abbb。
24、string类的相加,就是将两个string类型的变量进行合并,有两种方式。(1) str3=str1+str2; (2)str1 += str2
第一种方式是相加的两个变量没有变化,第三个变量为相加两个变量合起来之后的字符串。第二种方式是被变的string变量没有变量,目标string变量变化为两个变量合并起来之后的字符串。
25、sizeof无法识别出string类变量的所占空间,因为他是动态分配的。用 变量名.size()。例如 cout<<hh.size()<<endl; 此方法不计空白符。
26、至于c-风格,用strlen(hh)显示字符数,如果用sizeof(hh)则就包括空白符。
27、如果不是string类,而是C-风格的字符串。
可以使用strcat(目标字符串A,字符串B)来将字符串B的内容复制到字符串A的后面;
另外是strcpy(目标字符串A,字符串B)可以将字符串B全部复制到(即理解为赋值)到目标字符串A之中。
这就涉及到库函数的使用,在这里不展开叙述,我在打这篇笔记的时候看了很久qsort的使用,后来发现我用C++直接sort就好,所以各位这里可以只做了解,毕竟使用c++是为了提供效率,使用string类就好啦。
28、我们知道,cin是istream类的一个函数。cin.getline()的前缀是cin,通过英文句号和getline()相连。
因此,在这里,getline()是istream的一个类方法。cin.getline()是面对C-风格的字符串使用的。
而面对string风格的字符串时,我们使用的是getline(cin,变量名)因为没有使用句点的表示法,所以在这里,getline()便不是一个类方法。
他将cin作为参数,指出去哪查找输入,也没有指出字符串长度——因为string类可以自动调整长度。
29、其他类型的字符串字面值
之前在声明字符串的时候,C-风格是char 变量名[长度+1]={字符串}; 而string类风格是string 变量名={字符串};
除此之外,还有其他类型,
(1)如wchar_t; 声明方法(字符串带L前缀):如wchar_t=L"abc"; (2)C++11后新增的char16_t和char32_t; 声明方法(16的前面带小写u,32的前面带大写U)。如: char16_t=u"aabb"; char32_t=U"aaabbb"; (3) 原始(raw)字符串,在原始字符串里面,输入的字符都表示自己,例如\n在普通字符串里是换行,在原始字符串里显示\n。方法是前面加R,字符串的前后使用引号括号和括号引号。假如要在让原始字符串里面显示"(和)",方法是,把字符串前后原本的"(和)"改成"+*(和)+*"。
30、结构是OPP的基础,结构可以同时储存int、float、double、char类型。
31、结构的声明格式为:
struct 结构名 { 类型名1 变量名1; 类型名2 变量名2; …… …… }
一般使用外部结构声明,即在int main(){}外进行声明。
在创建结构数组的时候,一般在int main()的内部进行。
使用同一种结构的两个结构,可以直接通过“=”等号进行赋值。
32、在创建结构的时候同时赋值。方法是:
struct 结构名 { 类型名 变量名; …… ……; }使用该结构的某个结构变量名= { 值1; ……; };
如代码:
#include<string>
struct abc{std::string name;std::string price;}apple = { "苹果","5元一斤" };//在结构定义后直接声明变量//非直接声明的形式如下://abc apple={ "苹果","5元一斤" };//相当于节省了一个结构定义名 int main(){using namespace std;abc banana = apple;cout << "水果名字为:" << apple.name << "。价格为:" << apple.price << endl;banana.name = "香蕉";banana.price = "3元一斤";cout << "水果名字为:" << banana.name << "。价格为:" << banana.price << endl;system("pause");return0;}
但这种办法不清晰,将结构定义和声明结构(初始化结构)的时候分开使用,可以让代码阅读起来更清晰和容易理解。
33、若有两个变量,可以创建一个数组,数组的每个元素都是同一个结构定义。
#include <iostream> #include<string> using namespace std; struct abc { string name; string price; }; int main() { abc shuiguo[2]= //创建数组shuiguo,数组元素数为2(即数组中有两个结构作为元素),第一个元素为shuiguo[0],第二个元素为shuiguo[1] { {"苹果","5元一斤"}, //给数组中第一个元素——shuiguo[0]结构进行初始化,注意,结尾是逗号。赋值方法等同给普通结构初始化。 {"香蕉", "3元一斤"} //给数组中第二个元素——shuiguo[1]结构进行初始化,注意。结尾无符号。 }; cout << "第一个水果名称为:" << shuiguo[0].name << "。价格为:"<<shuiguo[0].price << endl;//显示数组中第一个元素相应的值 cout << "第二个水果名称为:" << shuiguo[1].name << "。价格为:"<<shuiguo[1].price << endl;//显示数组中第二个元素相应的值 //注意用数组的时候,原本是 结构名.变量名,现在为 数组名[N-1].变量名 ,N为第N个元素。数组名 其实就是变相的 结构名, //只不过结构名需要自己在初始化的时候进行声明,而 数组名 是共享一个名字,只不过括号里的数字不同,实质上还是一样的。 system("pause"); return0; } 输出: 第一个水果名称为:苹果。价格为:5元一斤 第二个水果名称为:香蕉。价格为:3元一斤
34、
定义结构时,使用指定的位数:
在定义结构的时候,可以指定每个变量使用特定长度位数,比如4位,8位等。格式为:
类型名 变量名:位数;
变量名后为冒号,位数填写具体需要的位数,之后为分号。
35、共用体(union)又称联合体,可以用来节省内存。
标准格式为:
union 共用体名 { int变量名1; char 变良名2; double 变量名3; }; 共用体名 使用该共用体的共用体变量名;
36、枚举使用enum工具,提供了另一种创建符号常量的方式,用来替代const,使用方法类似于结构。
枚举的格式为: enum 枚举名{枚举成员1,枚举成员2,……};
37、枚举的特点:
①在枚举的大括号里,填写成员名,可不填写值(将自动获取默认值);
②若在声明的时候,不给枚举的成员赋值,在第一个成员的默认值为0,后面的为前一个的值加1;
③在赋值的时候,枚举的成员的值,可以相同,可以不同,可以后面比前面大,也能小;
④无需给每个枚举的成员赋值,未赋值的成员,其值为前一个成员的值加1;
⑤不能在声明之后,再次给枚举的成员赋值——只能在声明的时候同时赋值;
⑥枚举bi作为类型名时,其变量只能通过枚举bi来赋值,不能被直接赋值,例如abc = bi(8);或abc = a5th;是可行的,但是abc = 5不可行的;
⑦枚举bi作为类型名时,其变量不能为枚举相加(或相减相乘)的算数结果。例如:abc = a4th + a5th;或abc = bi(4) + bi(5);是不可行的,有其他整型的数字参与在内,也是不可行的。例如:abc = a4th + 4;
⑧当int或者其他整型、浮点类型作为类型名时,其变量可以是枚举成员或者枚举范围值的算数结果。例如:double aabc = a4th + 4;是可行的。
38、指针跟内存地址有关,他表示的是一个内存地址。
指针操作符为*和&,其中&变量名为变量的内存地址,*变量名为该内存地址的值。
可以通过修改某内存地址的值,来使得其变量的值变化。
39、
int a——int类型的变量(也是值); int *b ——某内存地址储存了int类型的值(决定了该内存地址的宽度); a——变量(值); &a和b ——地址(指针); *b——该内存地址的值;
在这里面,a和*b是完全等价的,是一体两面,&a和b是一个指针,指针表示的是a的地址。&b与所有的都不同,只存储a地址(一个值)的地址(&b也是一个地址)。
40、int *b; b=&a这两行代码,也可以写为int * b = &a;;但是不能写成int * b = a;也不能写成int * b;*b = a;
原因在于,初始化时,实际上可以理解为b是地址,int * b实际上是int*和b,int*的意思就是指向int类型的内存地址。而不是int 和*b。
int *b=&a;中,int *b整体,被声明为一个指针变量(因此*的存在,b是指针*b是指针变量)。而=&a;这一部分属于初始化部分(initializer),用于初始化前面的变量(b)。
41、int * a, b;这行代码,创造的是指针a和int变量b,即a为地址,b为一个变量。
int * a, *b;这行代码,则是两个地址,分别是指针a和指针b。
int*a只能被初始化为地址,其中a是地址,int*a,b中,a为地址,b为int变量。
42、给指针的地址赋值有两种方式。
(1)int*a,b;b=&a;将a的内存地址赋值给指针b。
(2)Int*a;a = (int*)0x0037FCB8;必须加上(int*),表示这个是一个地址,不然编译器会误以为是一个值,而非地址。另外,地址前面的0x也是不能省略的。
(int*)是强制类型转换,比如0x0037FCB8是一个值(16进制的),只有加了强制类型转换,于是二者的类型才匹配,否则左边是内存地址,右边是一个值,虽然看起来一样,但本质上二者类型不同,是不能赋值的。
43、使用new来分配内存标准格式为:
类型名 * 指针名=new 类型名;
44、在使用new来分配内存地址的时候,具体地址如何,是随机的,多个new之间,他们的地址都是不同的,不存在后面的new将前面new的内存地址覆写了。
45、(1)int* 在C++中,是一个类型,这个类型是 指向整型变量的指针。
(2)a是内存地址,也是int* 类型的变量。
(3)sizeof()表示的是某种类型(如int、double,这里是int*)的内存宽度,如果括号里是变量,则显示该变量类型的内存宽度。
(4)而sizeof(a)表示的是 int* 类型的内存宽度,因为a是int*类型。
(5)因为int*类型的内存宽度是4,所以sizeof(a)输出的结果是4。
46、在使用new的时候,可以在声明的时候使用,也可以在声明后使用。
例如:
int *a=new int;
int *a; a = new int;//注意,需要使用a(即内存地址),而不是*a这样,类似int *a=内存地址这样
是一样的
47、在使用new请求内存之后,内存地址被占用。同样,也可以通过相反的方式释放内存,如delete。
标准的格式为: delete 指针名;
注意,指针名前无* ,表示只是释放该内存地址的值,但指针仍然存在。
48、在释放内存地址之后,如果要继续使用指针,就需要给指针new一个新的内存地址,方法是: 指针名=new 类型名;
49、使用声明类型变量,在编译时为之分配内存被称为静态联编(static binding),意味着数组(或字符串,结构)是在编译时被加入内存的。
使用new,则被称为动态联编(dynamic binding),意味着数组(或其他)是在程序运行时被创建的。这种数组被称为动态数组(dynamic array)。
使用静态联编时,需要在编译阶段确定数组的长度,而动态联编,程序在运行时决定数组的长度。new相比声明类型变量,更加灵活,也更加节省内存(因为无需一直占用)。特别是面对大型数据(数组,字符串,结构)
50、创建动态数组的格式为:
类型名 *指针名=new 类型名[数组长度]; 释放数组时,也需要使用delete,格式为 delete []指针名; 注意:这个时候,中括号里不加数字,则释放整个数组的内存空间。
51、①在使用动态数组时,可以用指针名代替数组名,即a为指针,a[0]为数组的第一个元素的值,而非第一个元素的地址
②*a显示的是数组第一个元素的值,而非数组全部元素的值。
③显示指针地址时,显示的是数组第一个元素的地址。
④在给指针+1时,指针的地址右移了(从B0到B8),而数组的位置没变,因此原本指向 a[0]的地址,在右移后(移动了一个double单位),指向了a[1]。这个时候,输出a[0]的命令,实际上输出的是a[1]。于是,想继续输出原本a[0]的话,就必须打a[-1]了。
⑤在移动指针之后,如果想delete指针释放内存,那么就需要将指针移动回来,例如之前是a=a+1; 那么移动回来就是a=a-1; 只有这样,才能正确释放指针。指针加1表示指针右移,减1表示指针左移,并不影响内存地址储存的值。
正因为右移和左移,所以叫指针才名副其实,因为正指的是指针指向的位置。
⑥创建指针时如果加了中括号,那么删除指针时也要加中括号。——有中括号代表数组。 只不过创建动态数组时需声明数组的元素个数,而删除的时候不用,删除只能删除整个动态数组。
⑦a[0]指的是指针所指当前位置内存地址所储存的值,a[1]指的是指针所指内存地址位置再右移(类似指针位置+1)的内存地址所储存的值。也这是为什么数组第一个元素是数组名,然后中括号里的数字为0了。因为0表示当前位置。
52、对指针加1,加的是指针类型的字节数。
例如:
int*a[5]; a+1;
这个时候,由于int是4字节,因此实际上是地址的位置加4,也就是向右移动一个int字节的宽度。
减一刚好相反,是向左。
而*a+1; 是指针a所指的地址的变量+1,但是指针位置是不变的。
这说明 c++将数组名解释为地址。
53、两个指针可以相减,如代码:
#include<iostream> int main() { using namespace std; int a[10]; int *b, *c; b = &a[0]; c = &a[5]; cout << "指针c为&a[5],指针b为&a[0]。指针c-指针b: c - b = " << c - b << endl; system("pause"); return 0; } 输出结果为: 5
指针相减的结果,相当于两个元素之间指针需要偏移的距离(5个int)。
54、如果要输出地址,则需要给数组前面加&,或者给指针前面加&,才能输出地址。但两个输出的地址不同,&a是储存指针a的地址,&b是数组b的地址。a由于输出的是字符串,所以无法输出地址。只有加了(int*)或者其他(类型名*)才能将其强制转换为内存地址,若是(char*)将依然输出字符串
55、在cout和C++的多数表达式中,char 数组名、char 指针以及用括号引起的字符串常量(比如"abc"),都被解释为字符串的第一个字符的地址。而输出的是字符串。只有当在之前加上 & 之后,才能显示出内存地址。
56、在使用定义的ab结构时,创建一个结构。
假如使用的是普通结构。即:“定义结构名 结构名”如ab bi; 那么表达bi这个结构中某个变量的办法是bi.变量名。
假如使用new来创建动态结构,创建方法为“定义结构名 *指针=new 定义结构名”即ab*shuiguo = new ab;
在表达结构中的变量时,有两种表达形式:(*指针名).结构变量名 或者 指针名->结构变量名 。即(*shuiguo).a或者shuiguo->a 。前者是类似普通结构,指针加解除运算符(*)相当于 结构名 来理解。对比而言,二者形式相近:(*bi).a 和 shuiguo.a
②无论是(*shuiguo).a或者是shuiguo->a,都可以直接运用在表达式之中,和我们使用普通结构没什么区别。
③由于shuiguo是指针,所以cout<<shuiguo<<endl;输出的是指针指向的内存地址
④因为是使用new创建动态结构,因此对应的可以使用delete来删除动态结构。具体格式为:delete 指针名。如:delete shuiguo;
#include<iostream> #include<string> using namespace std; struct ab//定义一个结构ab { string a;//名字 int b;//重量或数量 double c;//单价 }; int main() { ab *shuiguo = new ab;//声明结构水果(shuiguo),使用结构ab形式,通过new的形式创建动态结构 cout << "输入水果的名称:" << endl; getline(cin, shuiguo->a);//因为是string类型,所以需要使用getline(cin,结构中的变量名)这种形式,对于非动态结构,shuiguo->a就类似shuiguo.a cout << "输入水果的重量(斤):" << endl; cin >> (*shuiguo).b;//(*shuiguo).b和shuiguo->b是等价的 cout << "输入水果的单价(元/斤):" << endl; cin >> shuiguo->c;//指针名->结构变量名 和 结构名.结构变量名 是等价的 cout << "输入结束。\n经过计算:" << shuiguo->a << "的重量为" << shuiguo->b << "斤,单价为" << shuiguo->c << "元/斤。" << endl; //使用箭头运算符来表示动态结构中的某个变量 cout << (*shuiguo).a << "的总价值为" << shuiguo->b*(*shuiguo).c << "元" << endl; //带箭头运算符表示的结构中某个变量,可以直接用于表达式 cout << shuiguo << endl; delete shuiguo; cout << endl; ab bi;//声明结构笔(bi),使用ab结构的形式 bi.a = "铅笔";//结构bi的名称为 铅笔 bi.b = 15;//数量为15 bi.c = 0.5;//单价为0.5元/根 cout << bi.a << "的单价为" << bi.c << "元/根,我们要买" << bi.b << "根" << bi.a << "。\n"; cout << "经过计算,需要花费" << bi.b*bi.c << "元。" << endl; system("pause"); return 0; } 输出: 输入水果的名称: 苹果 输入水果的重量(斤): 25 输入水果的单价(元/斤): 3.33 输入结束。 经过计算:苹果的重量为25斤,单价为3.33元/斤。 苹果的总价值为83.25元 005B09F8 铅笔的单价为0.5元/根,我们要买15根铅笔。 经过计算,需要花费7.5元。 请按任意键继续. . .
57、自动变量:在函数内部声明使用,随着函数的退出而终止。使用自动存储空间,通常存储在栈中。
静态变量:在函数外部(程序运行期间有效)或函数内部使用关键词static(在函数消亡时保留,再次使用函数时,值为上一次退出时的值),
自由存储空间:又称为堆,在使用的时候分配内存,在不使用的时候可以释放他。
58、
|
普通数组 |
vector类 |
array类 |
备注 |
表示数组成员 |
数组名[编号] |
数组名[编号] |
数组名[编号] |
第一个成员编号为0,后面一次类推 |
将一个数组赋值给另外一个数组 |
不可 |
可,需类型数相同 |
可,类型和成员数需相同 |
|
显示成员数 |
size(数组名) |
数组名.size() |
数组名.size() |
|
头文件 |
无需 |
vector |
array |
|
名称空间 |
无需 |
std |
std |
|
声明时,进行初始化赋值 |
可 |
可,但不同于其他两类。具体见前文。 |
可 |
|
数组成员数变更 |
不可 |
可 |
不可 |
|
在数组成员之间插入新成员 |
不可 |
可 |
不可 |
|
删除现有数组成员 |
不可 |
可 |
不可 |
|
使用内存空间 |
使用栈(静态内存分配) |
使用堆(自由存储空间) |
使用栈(静态内存分配) |
|
初始化成员数 |
常量 |
常量或者变量 |
常量 |
|
能否访问数组范围外的数据 |
可 |
可 |
可 |
例如数组名[-40]或者数组名[100] |
能否使用 数组名.at(编号)检查是否在数组的范围外 |
不可 |
可 |
可 |
例如array<int, 4>a= { 1,2,3,4 }; cout << a.at(-1) << endl; 会在运行时提示出错 |