C++ 学习笔记——二、复合类型

目录:点我

一、数组

数组采用大括号进行初始化,可以省略等号,但是禁止缩窄转换(例如浮点类型转整型、取值超限等)。

二、字符串

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为指针所分配的空间,并不销毁指针变量本身,特别注意 newdelete 要配对使用,否则会引发未知后果,对空指针使用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] 的后一个地址,也就是说两者的变量类型是不同的。tellshort 型指针,而 &tellshort[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;
    
  • 动态存储:即使用newdelete来管理内存,在运行阶段采用动态联编,程序员对其的控制权更大,不受编译器限制。

2. 内存泄漏

  • 如果使用new运算符在自由存储空间(或堆)中创建变量后,没有调用delete,则很可能出现以下情况:包含指针的内存由于作用域规则和对象生命周期的原因被释放,在自由存储空间上动态分配的变量或结构也将继续存在。 这将因为指向这些内存的指针无效,进而导致无法访问自由存储空间中的结构。简单来说,就是记录指针信息的内存被释放了,但是指针原本指向(占用)的内存未被释放。 这部分内存在整个程序运行过程中将无法被发现、使用和释放,这就导致了内存泄漏。因此养成newdelete关键字配对使用的好习惯十分重要。

附录:C++转义序列的编码

字符名称ASCII符号C++代码十进制ASCII码十六进制ASCII码
换行符NL(LF)\n100xA
水平制表符HT\t90x9
垂直制表符VT\v110xB
退格BS\b80x8
回车CR\r130xD
振铃BEL\a70x7
反斜杠|\|920x5C
问号?\?630x3F
单引号\’390x27
双引号"\"340x22
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BeZer0

打赏一杯奶茶支持一下作者吧~~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值