程序设计 第四章 复合类型知识总结

本文总结了程序设计课程中关于复合类型、指针、字符串、函数等内容。介绍了constexpr、auto、decltype等特性,探讨了指针的基本概念及运算,并对字符串操作、函数定义与调用进行了详细说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 程序设计 第四章 复合类型知识总结

 

 

 

新的学期,我们的学习重心,不再像上学期一样,学习如何敲代码的技术,现在开始逐渐深入,开始学习面向对象程序设计,新学期的程序设计变得更为的复杂,初步表现在数据的类型上,我们一上来就学习了第四章复合类型,下面是第四章的知识总结。

 一开始讲了一个constexpr对象,constexpr对象如果认定某个对象是常量表达式,可以把它声明为constexpr类型,由编译器验证对象的值是否是常量表达式 ,大多数情况下constexpr和const在使用上没有区别如果b是全局作用域中的名字编译器一定会为b分配空间对于a,如果没有代码明确要使用a的地址,编译器可以选择不为a分配存储空间,而仅将其当作编译时期的值,类似于字面值常量。

 

constexpr int a = length();//必须在编译时能计算出length()返回值

const int b = length();//b的值可以在运行时才获得,之后不改变

 

之后了解到对位置类型数的处理:在编程时需要把一个表达式的值赋给变量,此时必须知道表达式的类型才能声明该变量,要清楚地知道这个表达式的类型比较困难或不可能。auto类型说明符用auto声明变量的类型,由编译器去自动分析表达式的类型,推断出变量的实际类型定义auto变量必须有初始值。

decltype类型指示符作用是选择并返回操作数的类型编译器会分析表达式的类型,并不真正计算表达式的值

decltype(sizeof(int)) size;//size的类型是sizeof(int)结果的类型

auto和decltype可以让我们不用去记忆和书写复杂的类型名字例如,某些复合类型、函数指针类型、标准库中的某些类型希望从初始值表达式推断出要定义的变量的类型时,用auto希望从表达式推断出要定义的变量的类型,但是又不想用这个表达式的值初始化该变量时,用decltype。

 

range-for是一种简化的for语句从头至尾对容器或序列的所有元素逐个执行某种操作range-for语句的语法形式:

for( declaration : expression )

{statement;}

学习了上面的零碎知识后,又更加系统的学习了指针。

内存地址程序运行时,代码和需要的数据都被存储在内存中内存是有序的字节序列,每个字节都有唯一的地址,使用该地址可以确定字节的位置,用以存储和获取数据直接访问和间接访问通过变量的名字直接访问为程序中定义的变量分配的内存单元,存取变量的值使用变量的内存地址找到存放数据的单元,间接访问其中的内容

指针的特点:指针持有一个对象的地址,称为指针“指向”该对象。通过指针可以间接操纵它指向的对象。

定义指针变量的语法:每个指针都有相关的类型,要在定义指针时指出

类型 *指针变量;

指针不能保存非地址值,也不能被赋值或初始化为不同类型的地址值

操作时常用到空指针:空指针指针值为0时是一个空指针,即不指向任何对象的指针

生成空指针的3种方法:

int *p1 = nullptr;

int *p2 = 0;     

int *p3 = NULL;

 

定义指针时,应该对指针进行初始化指针的运算同类型的指针可以进行相等(==)或不相等(!=)的比较操作,比较的结果是布尔类型指针可以进行加或减整数值的算术运算自增、自减运算适用于指向数组元素的指针。因为他不是具体的数值,不可用< >来比较。

 

通用指针:void*指针可以持有任何类型的地址值,即通用指针相关的值是个地址,但是该地址保存的对象类型不知道就不能操纵void指针指向的对象,只能传送该地址值或者和其他地址值进行比较不允许void指针到其他类型指针的直接赋值。

 

编译器在处理程序分配内存时,可以动态分配空间,new和delete为最基础的操作。new运算符在堆上动态分配空间,创建对象,并返回对象的地址一般将new返回的地址保存在指针变量中,以便间接访问堆上的对象。又因为堆上的空间在使用后必须释放,否则会造成内存泄漏,所以用delete来释放new建立的内存。

 

左值引用:引用的定义和初始化引用由类型标识符和一个取地址符(&)来定义type& refVariable = leftValue;引用必须被初始化,初始值是一个有内存地址的对象

使用左值引用时注意引用并非对象,是为已存在的对象所起的另一个名字引用只能绑定到对象(有内存地址)上,不能与字面值或某个表达式的计算结果绑定在一起引用本身不是对象,所以不能定义引用的引用

引用的定义和初始化类型 &引用名 = 初始值; 引用是一个对象的别名,定义引用时必须用有内存地址的对象初始化引用在初始化之后,一直指向该对象。

空指针:指针可以不指向任何对象,其值为0,表示空指针没有空引用引用必须指向一个对象,而且一直指向该对象,不存在“空”引用引用的值如果为0,表示它指向的单元值为0。

赋值指针之间的相互赋值会改变指向关系引用之间的相互赋值是它们指向的对象之间的赋值,引用关系本身并不改变

数组上的指针运算

指向数组的指针可以执行解引用、自增、自减、加或减一个整数、两个指针相减、比较等运算指针加或减一个整数n,结果仍是指针新指针指向的元素与原来的指针相比前进或后退了n 个位置两个指针相减的结果是它们之间的距离参与运算的两个指针必须指向同一个数组中的元素两个指针如果指向同一个数组的元素,可以利用关系运算符对其进行比较。

 

 

右值引用:右值引用就是必须绑定到右值的引用。定义:右值引用的形式类型 &&右值引用变量 = 右值表达式;右值引用由类型标识符和两个取地址符(&&)定义右值引用必须被初始化,初始值是右值表达式不能将右值引用直接绑定到一个左值上。

 

之后扩展了对const常量的学习,const限定的对象是不可改变,volatile限定的对象不能被编译器例行优化。

volatile限定词当一个对象的值可能在编译器的控制或检测之外被改变时,可以将对象声明为volatil,例如被系统时钟更新的对象volatile一般用在多线程或中断处理的程序设计中const和volatile的使用语法相同

 

1.指向const对象的指针(非const )

const type *cp; 或者type const *cp;cp是指向常量的指针,它所指向的内存中的内容不可以改变,即*cp的值不能改变

2.指向非const对象的const指针

type* const cp = initAddressValue;cp是常量指针,初始化后值不能改变,指向固定的单元

3.指向const对象的const指针

const type* const cp = initAddressValue;

 

4.const限定的左值引用不可修改const引用可以绑定到const对象不能用非const引用指向const对象

 

5.const左值引用可以绑定到生成右值的表达式可以用任意表达式初始化const引用,只要表达式的结果能转换成引用的类型即可

 

6.const限定引用的含义const引用仅对引用自己可参与的操作进行了限定,对所指向的对象本身是不是常量未作限定。因为指向的对象也可能不是const,所以允许通过其他途径改变它的值。

 

结构体

结构体把一组来自不同类型的数据组合在一起构成复合类型,其中的每个数据都是结构体的成员。

结构体由关键字struct定义,语法形式:

struct 结构体类型名{成员声明;};

结构体的成员不能独立使用,必须由结构体类型的变量通过成员选择运算符“.”来选择,或者由结构体类型的指针通过“->”。

 

String是我们处理数据非常实用的操作

string 表示可变长度的字符序列字符串是对象string 类支持字符串对象的各种操作

各种初始化方式字符串之间的复制、比较、连接查询字符串长度和判断字符串是否为空

访问字符串中的单个字符,使用string 类要包含头文件<string>,可以用关系运算符比较两个字符串对象两个string相等意味着它们的长度相同,并且所包含的字符也完全相同字符串的大小关系依照字典顺序定义且区分大小写字母。

字符串字面值不是string类型,而是const char*类型string对象和C风格字符串的转换可以将C风格的字符串直接赋给string对象,反之不可用string对象的c_str()操作可以返回一个表示该string对象内容的C风格字符串,结果为const char*类型,即C风格字符串的首地址

 

vector

动态数组

动态可以改变存储空间的大小,定义的数组可以没有界限。

头文件: #include <vector>

定义:vector <data_type> vector_name;

如:vector <int> v;

操作:

empty()   返回bool型,表示vector是否为空(v.empty() )

size()   返回vector内元素个数 (v.size() )

push_back(data_type a) 将元素a插入最尾端

pop_back() 将最尾端元素删除

 

 

迭代器

迭代器举例:multiset <int> :: iterator pos;for(pos = s.begin(); pos != s.end(); pos++)

迭代器相当于我们学的指针,用来给元素标明位置。指向地址,而不指向值。上面的迭代器可以用来输出set中的元素,其中循环条件不可作<,因为s.end()是地址而不是值。

 

文件读写-输入输出重定向

输入输出重定向

将标准输入和标准输出与命名文件关联起来

c:\> program <inputfile >outputfile

运行名为program.exe的可执行程序,用文件inputfile作为输入,用文件outputfile作为输出

程序4.11

读入以0结束的整数序列,求和后输出

c:\> addNumbers <numbers.txt

 

标准库文件流

C++标准库中的文件流

ifstream类:用来输入(读文件)的文件流

ofstream类:用来输出(写文件)的文件流

fstream类:把文件连接到流对象用来输入和输出

 

 

在之后在第五章我们学习了函数,对于一个程序来说,定义函数的操作可以使得主函数的思路更加清晰。主函数越简洁,则该程序越好,这样就需要多定义全局函数来实现了。

函数是一个命名的代码块,通过调用函数可以执行相应的代码,用来实现各种算法,完成特定的任务。语法形式为:返回类型  函数名(参数列表) { 函数体 }。

函数的参数列表不能省略

如果函数没有任何参数,可以用空参数表或void参数表表示

参数列表由逗号分隔的形参类型列表构成,每个形参类型后跟一个可选的形参名字。

函数声明

函数在使用之前必须声明

一个函数可以在程序中多次声明

函数定义也可以被用作声明,但是函数在程序中只能定义一次

函数声明的形式

返回类型  函数名(参数列表) ;

参数传递是指用函数调用的实参来初始化函数形参存储区的过程

函数的形参是局部对象,仅在函数的作用域内可见

每次调用函数时,会创建形参变量,并用传入的实参初始化形参

形参的类型决定了实参初始化形参的方式

如果形参是引用类型,形参将绑定到对应的实参上

否则,将实参的值复制后赋给形参

传值,按值传递参数

当实参的值被复制给形参时,形参和实参是两个独立的对象,实参被称为按值传递,或传值调用函数

C++的默认参数传递方式就是按值传递

传指针:使用指针参数是传地址值

在前面学习了引用之后,我们也清楚的知道,函数也是可以传引用的。当形参是引用类型时,对应的实参被称为按引用传递,或者传引用调用函数。此时,引用形参绑定到实参,是实参对象的别名。

使用函数得到函数结果最重要的就是返回值:函数的执行结果由return语句返回

return语句结束当前正在执行的函数,将控制权返回给函数的调用者

return语句有两种形式:

(1)return;

用在返回类型为void的函数中,不是必需的

  1. return 表达式;

非void的函数必须返回一个与声明类型匹配的值,否则会引起编译错误  

默认情况下,函数的返回值是按值传递的,得到控制权的函数将接收return语句中指定的表达式值的副本

返回值的方式和初始化变量或形参的方式完全一样,返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果。

函数中一个重要的扩展为重载函数,他使得多函数的操作更为方便。

C++函数重载机制:多个函数可以共享同一个函数名,针对不同的参数类型提供不同的操作。

重载函数:如果同一个作用域内的几个函数名字相同但形参列表不同,则它们是重载函数。例:void print(const int *b, const int *e){...}

void print(const int ia[], size_t size){...}

void print(const char *cp){...}。

当然定义时方便,调用时会更困难,函数参数个数相同且参数类型可以相互转换时,确定调用的函数会困难一些。

在第五章最后学习了作用域和存储类别。

对象的生存期:是指程序执行过程中对象存在的时间,对象的生存期与对象的作用域和存储类别密切相关。

名字的作用域:程序的一段区域,名字的作用域指的是该名字可以在哪些程序文本区使用。

对象的存储类别:创建对象时分配内存空间的方式和内存空间的类型。

全局变量和全局函数:

1.在程序整个执行过程中都存在,可以在整个程序中使用

2.全局的内置类型变量,不指定初始值时被初始化为0值

3.全局变量和非inline 全局函数在程序中只能定义一次,在其他地方使用时需要声明

4.关键字extern 用来声明全局对象,可以在同一文件中或同一程序的不同文件中多次出现

与全局相对的为局部的,局部对象默认为自动存储,可以通过存储类别关键字static 和register 对其进行修改。

一、Static:声明为static的局部对象是静态存储的

static对象在控制流程第一次到达其定义点时被初始化,如果没有提供初始值,就被自动初始化为0值

在函数的后续调用中,初始化语句被跳过

静态对象的值在函数的多次调用之间保持有效,生存期会延续到整个程序结束,但它的作用域仍然是局部的

因此,需要在同一函数的两次调用之间保留某些数据时可以使用局部static对象

  • 函数中频繁使用的自动变量可以用register 关键字声明。如:

int factor(int n)

{

register int i=1;

for(i=1;i<n;i++){ … }

}

最后还学习了存储空间分配,这一部分的内容就和之前学习new和delete时补充的动态存储知识差不多了。

这些差不多就是刚开学第一阶段的学习了,打的虽然多,但是实际学习的知识,这些还远没有那么少。不仅是多,更是越来越难以理解,这样就更不是那么容易的去应用。可想而知这学期的学习会越发之难,更需要花费更多的时间来学习,而且最重要的是来巩固。我的记忆力非常之差,面对枯燥复杂的知识,需要不断的练习、应用,才可使记忆加深。老师说,现在我们才算是开始学习程序设计,往后的学习是一场硬仗。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值