C++ primer读书笔记1(常见错误)

C++ 常见错误总结
1:关于定义在for循环语句头的变量,在标准C++中是不可以用于循环体外的其他地方,但是由于某些编译器的缘故,它可以在某些不遵守C++标准的编译器内使用与循环体的外部。
2:八进制以0开始,十六进制以0X或者0x开始,如024 ,0x14 表示成10进制都是20;
3:字面值:字面值就是程序中的常量,比如,1,‘a’等,只能用它的值来称呼它。
4:字面值的修饰,102L,102u,分别表示long,unsigned int 型的值。同理,102.0f,102.0F表示浮点型的字面值,L'a',表示宽字符 'a'.
5:非打印字符的转义序列。
换行符 \n 水平制表符 \t 纵向制表符 \v 退格符 \b
回车符 \r 进纸符   \f 报警响铃符 \a 反斜线 \\
疑问号 \? 单引号     \' 双引号     \"
任何字符都可以表示为八进制或者16进制的通用转义字符
八进制 :\ooo;16进制 :\xddd; 其中ooo 和 ddd 表示为8进制以及16进制的数。
6:可以用空格,制表符,或者换行符 将一个长字符串分开。
如:"fdsfg" "gret" = "fsdfggret"
7: 行的连接符 \
std::cout<<endl;
等效于:std::cout<\
<endl;
要注意的是\必须是该行的最后一个字符,不能有注释或者空格之类的。
8:关于变量
C++中变量必须先定义才能使用。变量是左值(可以出现在赋值语句的左边或者右边)(右值就是只能出现在
赋值与语句右边的值)
9:对象,对象是在内存区域中具有类型的区域。
10: 变量名规则,以小写字母开头,每个单词之间用‘_’连接,或者第二个单词用大写开头。
11:关于变量的初始化
初始化和赋值是两个完全不同的概念,初始化时在变量定义时给其一个初值,赋值是给一个已经有初始值的变量赋予一个新的值。
C++支持两种初始化,一个是直接初始化,一个是复制初始化。如:int aa(1024) //直接初始化
int a = 1024 //赋值初始化
12:变量初始化原则
内置类型,如果变量定义在函数体外,则会初始化为0,定义在函数体内的变量(包括main函数)都不会初始化。
13:关于extern的使用
C++所有的变量使用前都要声明,如果没有声明是不能使用的,其实在定义变量的时候已经包括了变量的声明。一个变量可以多次声明,但是定义只有一次。
//main.cpp
#include<iostream>
#include"test.h"
using namespace std;
int xx = 100;
void main()
{
print();
}
//test.h
#ifndef XX_TEST_H
#define XX_TEST_H
#include<iostream>
extern int xx;  //因为要用,所以必须先声明
void print();
#endif
//test.cpp
#include "test.h"
void print()
{
std::cout<<xx<<std::endl;
}
14:变量作用域
变量作用域以{}包围
{
    int xx =10;
{
  int xx = 100;
}
}
15:关于全局变量
extern修饰变量的时候,表示变量的声明,一个变量可以声明很多次,但是定义只有一次。对于全局变量,如果不加static,const的修饰,那么在文件开头声明的全局变量,就是真正的全局变量,这个时候我们只需要在包含其申明的其中一个文件中定义一次即可。对于在全局定义的const变量,这个时候处理会有点奇怪,因为编译器此时会把const变量当做该单元文件的局部变量,所以此时我们如果在头文件中定义一个const变量,然后在其他文件中引用这个头文件,不会出现重复定义的错误,原因就是此时的const变量虽然是写在全局的,但其本质就是一个针对该编译单元的局部变量。当然如果此时修改在头文件中定义的const变量,在前面加上一个extern修饰的话,并且在声明的同时也定义了变量,那么这个时候编译就会出错,因为此时的const变量是全局变量,当然会出现重复定义的问题啦。
如果在VC中我们想跟踪定义的const局部变量地址,你在监视窗口对变量取地址操作,你会发现此时取地址是一个错误,因为VC会把常量定义的const变量进行优化处理,此时的const int a = 5,就相当于#define a 5 ,编译器根本没有为a分配内存,那么取地址就是一个错误了。如果在程序中有对a地址进行操作的语句,如 int *p = (int *)&a,那么此时a就就会有被分配的内存空间,你在监视窗口可以看到a的地址。
16 引用
引用就是变量的别名,几个变量在内存中占有相同的地址。其中最牛逼的引用就是const的引用了,因为非const引用只能用于非const的同类型的变量,但是const类型引用不仅可以用于同类型的,而且可以用于不同类型的变量,可以用于字面值常量的引用。
const int i = 10;
const int &ri = i;
const int &r2 = 190;
const int &r3 = 7238+ i;
double db = 3.132;
const int &r5 = db;
17 enum
关于enum,构造方法为:enum enumName{member1,member2……}。如果没有显式的指定成员值,那么它的值便从0开始,后面值依次比前面的大1.
enum成员值本身是常量表达式,所以不能改变它的大小。
构造了enum,相当于构造了新的数据类型,可以用enumName 来定义变量。如 enumName tt = member1;
18 关于类数据成员初始化
类里面有函数成员,数据成员,但是类里面的数据成员的定义只表示了一个变量名和类型,不能在其定义打分时候初始化,数据成员的初始化应该在构造函数中。
19 struct and class
C++中,struct和class的唯一区别就是默认的访问级别,默认情况下,struct的是public,class的是private。
20 关于const变量的非常量表达式初始化
C++primer中讲如果const变量用的是非常量表达式初始化,那么应该放在源文件中。但是经过测试,我把定义放在头文件中,其实也并没出现问题,不明其因,然后在网上看了很多参考,众说纷纭,有的说是这样关于造成每个编译单元都初始化一次浪费内存,感觉这个还比较靠谱。


21 ctrl + z
vs下,对于输入流,ctrl + z 表示的输入流的结束。但是对于这个ctrl + z ,它的前后字符必须是换行键,以保证 ctrl + z 的独立性。
关于数组和指针
1 数组大小
int a[N],N 是数组的大小,必须是一个常量表达式,包括字面值常量,枚举常量,或者用常量表达式初始化的const对象,非const对象
或者到运行时才能确定的const变量不能用于定义数组的维数。
2 数组的初始化
函数外定义的数组如果没有显式初始化,那么元素初始化为0。函数体内定义的无初始化,如果数组元素为类类型,那么没有显式初始化会自动调用类的默认构造函数进行元素的初始化。
初始化不必 决定维数,编译器会根据元素个数确定 int a[]={1,2,4};
如果指定维数,那么显式初始化元素不能超过数组维数,没有超过的若是内置类型会被初始化为0,类类型的调用默认构造函数初始化
3 [] 的含义
p[i],p要表示一个地址,或者指针。整体的含义就是p所指的地址值加上偏移i个单位后的地址所存储的内容。
4 指针的两种表示
type *p;type* p。这两种表示都是正确的,但是第二种在连续声明多个指针变量是,容易出错。
5 关于野指针,悬垂指针
悬垂指针是指指向曾经存放对象的内存,但是对象已经不再存在。野指针指的还没有初始化的指针,所以野指针和悬垂指针很类似,指向的内容都是不合法的。至于为什么说他们危险,原因是因为它们指向的内存可能是非常重要的程序段或者数据,这时候要是对这些指针进行写操作的话,会引起不可估量的危险。解决方法就是要指针指向释放后要将其NULL,指针要初始化为NULL。
6 数组越界
数组越界编译时是检查不到错误的,但是在运行时就会出错,所以只有靠自己去检查。
7 const 和指针
指向const对象的指针:
const类型的指针:指针所指的对象不能被修改。不能用void*保存const对象,但可以用const void* 对象指向任何const对象。允许把
非const对象的指针赋值给 保存const对象的指针。如 const int * p;int i; p = &i;


const 指针:const指针必须在定义式初始化,指针值不能修改,但是指针所指的内容可以修改。const指针不能指向const对象,因为const
对象的指针类型为 const * type p;
指向const对象的const指针:就是指针的值和指针所指向的值都不能改变的指针了。
8 void* 指针
这个指针可以保存任何类型对象的指针,const另加修饰。但是它支持的操作仅仅有限几种,1是与另一个指针相互比较;2是向函数传递void* 指针,或者返回void* 指针。3是给另一个void* 指针赋值。但是不允许用void*操纵它指向的对象。
9 指针和const
typedef string *pstring;
const pstring cstr;
此时不能单纯的把pstring展开,从而把这个cstr理解为const string *cstr。因为const直接修饰的是pstring,所以这里表示的是一个const型指针,因此这样定义会cstr会引起一些误解,所以在定义const型的指针使,最好把类型写写在前面,如pstring const cstr;
10 C字符串
c字符串是指结尾带有'\0',或者说是null的字符数组。有关c字符串的常用函数有strlen,strcmp,strcat,strcpy.但是经常在编译时遇到这些函数,编译器指出危险,这些危险到底是什么呢?其实本质还是关于内存的使用问题,在调用strcat,或者strcpy时,总要预先设定目的字符数组的大小,但是数组大小的计算容易出错,这就会造成内存使用的错误。利用strncat,strncpy,这样的函数会保证复制或者连接指定数目的字符。
11 动态数组
动态数组的初始化:如果是内置类型的数组,则不会初始化数组元素,如果是类类型的就会调用默认构造函数进行数组元素的初始化。
int *pia = new int[10] //数组元素没有进行初始化
int *psa = new string[10]//数组元素被初始化为""的空字符串。
如果想要初始化元素类型为内置类型的,只需要这么做
int *pia = new int[10]();//数组元素被初始化为0
const int *pp = new int[100]();//这里必须带(),因为const类型元素必须进行初始化。
12 多维数组
关于数组要明白的就是地址和指针的东西。
int arr[2][3],int aa[3];数组名表示的数组中首元素的地址,所以对于arr[2][3]来讲,arr = &arr[0],它的类型是int *[3],即指向数组的指针。
typedef的东西
typedef int int_arr[3];//这种数组的宏定义还真是奇特。
它表示的意思是int_arr是 int [3]类型的。所以此时int_arr *p;p表示的就是指向整个数组的指针,而不是指向数组的首个元素。



你可能感兴趣的:(C++ primer读书笔记1(常见错误))