目录:点我
一、数组
数组采用大括号进行初始化,可以省略等号,但是禁止缩窄转换(例如浮点类型转整型、取值超限等)。
二、字符串
1. C 风格
- C 风格字符串必须以
\0
结尾,如果采用双引号初始化则不用显式的指明空字符,程序会自动填补,因此要确定存储字符串所需的最短数组时,需要加上空字符。 'S'
表示的是 ASCII 码值,而"S"
表示该字符串的地址,前者可赋给 char 类型,后者不可。cout
可以自动拼接多个字符串:cout << "a section" " b section" << endl;
。cin
会在遇到空字符时停止并将其留在缓冲区(此时若调用getline
则会导致错误),cin.get(name, size)
和cin.getline(name, size)
会在遇到换行符时停止,前者不会丢弃换行符并将其留在缓冲区,后者则使用空字符替换。strcpy(char1,char2)
:将char2
复制给char1
,要求char1
的空间不能小于char2
的空间。strncpy(char1,char2,max_length)
:第三个参数为最大字符数,用于防止char1
的内存较小时复制出错,也可理解为将char2
中前max_length
个字符复制到char1
中。strcat(char1,char2)
:把char2
的内容扩展到char1
中。strlen(char1)
:获取char1
的长度,不计空字符。
2. C++ 风格
cin
用于string
时每次读取一行而不是一个单词,同时为防止越界可以采用getline(cin, str);
。w_char: L"example"
、char_16: u"example"
、char_32: U"example"
、raw: R"(example)"
、UTF-8: u8"example"
,其中R"tmp()tmp"
表示定界符,tmp 表示自定义的定界符,不能是空格、括号、斜杠和控制字符,多种前缀可复合使用。
三、结构
1. 结构体
-
struct
:允许设定变量表示位数,例如:struct table{ int id : 7;// 限定id字段为7位 string name : 10;// 限定name字段为10位 };
2. 共用体
-
union
:不同变量共享同一空间,因此需分时使用union table{ int id; string name; };
3. 枚举
-
enum
:枚举类型,性质如下:enum spectrum{red, orange, yellow, green, blue, violet, indigo, ultraviolet}; // 声明,允许省略枚举变量名,此时不能定义相应的枚举变量,但可以使用所定义的常量,枚举类型允许多个相同值元素 enum bits{one, two = 2, four = 2, five}; // 允许,此时one=0,five=3,即后者比前者多1,且量值可重复 spectrum band; // 定义 band = blue; // 赋值,只允许赋值为所声明的枚举值,且无算术运算 int color = blue; // 由于enum类型是整形,因此允许给int型变量赋值 color = 3 + red; // 允许,red将转化为整型运算 band = 3; // 非法,整型不能转化为enum型 band = spectrum(3); // 允许,强制类型转换,赋值为green band = spectrum(3005); // 允许,但结果未知
-
枚举在取值范围内即有效,因此如果定义一个枚举:
enum bits{one=1, two=2, four=4, eight=8};
,那么语句bits myflag = bits(6);
是合法的。考虑到内存物理结构,枚举类型的最大值通常是枚举定义中最大值小于最小的 2 的幂指数的值减一(符号占位),例如某个定义中有最大值 101 ,那么该枚举类型最大取值范围为 127 ,最小取值范围同理。
四、指针
对于一个数据,必须追踪其 3 种基本属性:数据存储地址、数据类型、数据的值。对于传统的程序,通常在编译阶段进行决策,即在编译阶段就确定所采用数组的内存大小;而 OOP 则在运行阶段进行决策,即在程序运行时根据需要分配内存大小。这样能够更好的节省空间,使程序更灵活。
1. 声明指针
-
声明格式:
typeName *pointerName;
-
示例:
int *pt; char *pc;
2. 指针赋值
-
将内存地址赋给指针,可以用取地址符
&
获取变量地址,用new
运算符申请未命名内存地址,示例如下:double * pn; double * pa; double * pb; double example = 1.0; pn = &example; pa = new double; pb = new double[10];
3. 对指针解除引用
-
解除引用即将地址变换为实际值,可以使用间接值运算符
*
或数组表示法,示例如下:cout << *pn; // 输出1.0,即example的值 cout << pn[0]; // 同上
4. 区分指针和指针所指向的值
- 如果
pt
是指向int
的指针,则*pt
不是指向int
的指针,而是等同于一个int
型的变量,pt
才是指针。
5. 数组名
- 大多数情况下C++将数组名视为数组的第一个元素的地址。
- 但是
sizeof
运算符用于数组名时,将返回整个数组的长度;用于指针时,将返回指针的长度。单位均为字节。
6. 指针算数
-
指针可以与整数进行运算,其意义为指针对应地址值的带权整数偏移量,示例如下:
int tacos[10] = {5,2,8,4,1,2,2,4,6,8}; int * pt = tacos; // 假设此时指针pt地址为3000 pt = pt +1; // 此时指针pt地址为3004(int型为4字节,即偏移量权值为4) int * pe = &tacos[9]; // 此时指针pe地址为3036 pe = pe - 1; // 此时指针pe地址为3032, 对应数组tacos[8] int diff = pe - pt; // tacos[8] - tacos[1] 的差值
7. 数组的动态联编和静态联编
-
使用数组声明来创建数组时,将采用静态联编,即数组的长度在编译时的设置:
int tacos[10]; // 静态联编,数组大小在编译时已经被限制
-
使用
new[]
运算符创建数组时,将采用动态联编,在运行时分配空间,使用完该数组后需使用delete[]
释放内存:int * pz = new int [size]; ...... delete [] pz;
-
delete
:销毁指针,只是销毁new
为指针所分配的空间,并不销毁指针变量本身,特别注意new
与delete
要配对使用,否则会引发未知后果,对空指针使用delete
是安全的。 -
delete []
:销毁new []
创建的动态数组指针的空间。 -
如果只用
new
而不使用delete
,则会发生内存泄漏,即已分配的内存再也无法使用,造成系统不断寻找更多内存而终止。
8. 数组表示法和指针表示法
-
使用方括号数组表示法等同于对指针解除引用:
tacos[3] 等价于 *(tacos + 3)
-
区别在于,指针是变量,因此可以进行运算与赋值,而数组名是常量。若使用
sizeof
运算符,对于数组将得到数组的长度,对于指针将得到指针的长度。 -
共同点在于,指针和数组名使用括号访问元素时,都执行以下转换:
arrayname[i] becomes *(arrayname + i) pointername[i] becomes *(pointername + i)
-
对于一个数组
short tell[10];
,tell
表示tell[0]
的地址,&tell
表示整个数组的地址,从数字上来看二者相等,但从概念来讲有所区别。tell + 1
表示tell[1]
的地址,而&tell + 1
则表示tell[10]
的后一个地址,也就是说两者的变量类型是不同的。tell
是short
型指针,而&tell
是short[10]
型指针。若要定义一个指向&tell
的指针变量,则使用语句short (*pas)[10] = &tell
,括号表示这是一个指针,而不是指针数组,若省略括号,则pas
先与[20]
结合,导致pas
是一个short
指针数组,包含10
个元素。另外,由于pas
等价于&tell
,因此*pas
等价于tell
,所以(*pas)[0]
为tell
的第一个元素。
9. 指针用于结构
-
由于指针表示地址,地址中并没有成员的名称,因此不能使用
.
运算符直接访问结构中的成员。C++为指针提供了->
运算符来帮助指针访问结构中的成员,或者对指针解除引用可以采用点来访问结构中的成员,但由于优先级的规则,必须采用括号,示例如下:struct things { int good; int bad; }; things example = {1, 2}; things * pt = &example; ------------------------------------------------- 此时: example.good 等价于 pt ->good 等价于 (*pt).good example.bad 等价于 pt ->bad 等价于 (*pt).bad
五、内存问题
1. 自动存储、静态存储和动态存储
- 自动存储:即在函数内部定义的常规变量使用自动存储空间,这意味着它们在所属的函数被调用时自动产生,在函数结束时消亡。简单来讲自动变量就是局部变量,通常存储在栈中。
- 静态存储:整个程序执行期间都存在的存储方式,有两种定义方式,一种是在函数外部定义它,另一种是在声明变量时使用关键字
static
简单来说静态变量就是全局变量:static double example = 1.0;
- 动态存储:即使用
new
、delete
来管理内存,在运行阶段采用动态联编,程序员对其的控制权更大,不受编译器限制。
2. 内存泄漏
- 如果使用
new
运算符在自由存储空间(或堆)中创建变量后,没有调用delete
,则很可能出现以下情况:包含指针的内存由于作用域规则和对象生命周期的原因被释放,在自由存储空间上动态分配的变量或结构也将继续存在。 这将因为指向这些内存的指针无效,进而导致无法访问自由存储空间中的结构。简单来说,就是记录指针信息的内存被释放了,但是指针原本指向(占用)的内存未被释放。 这部分内存在整个程序运行过程中将无法被发现、使用和释放,这就导致了内存泄漏。因此养成new
与delete
关键字配对使用的好习惯十分重要。
附录:C++转义序列的编码
字符名称 | ASCII符号 | C++代码 | 十进制ASCII码 | 十六进制ASCII码 |
---|---|---|---|---|
换行符 | NL(LF) | \n | 10 | 0xA |
水平制表符 | HT | \t | 9 | 0x9 |
垂直制表符 | VT | \v | 11 | 0xB |
退格 | BS | \b | 8 | 0x8 |
回车 | CR | \r | 13 | 0xD |
振铃 | BEL | \a | 7 | 0x7 |
反斜杠 | |\|92 | 0x5C | ||
问号 | ? | \? | 63 | 0x3F |
单引号 | ’ | \’ | 39 | 0x27 |
双引号 | " | \" | 34 | 0x22 |