已阅读5页,还剩136页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第3章C 基本语法 主要内容 3 1数据类型 程序中的各种加工和处理都是针对某些数据进行的 这些数据由数据类型描述 数据类型规定了数据的存储结构 在内存中占据的大小和布局 可以进行的运算 取值范围 C 中的数据类型大致分为三类 C 预定义的一组内置的基本数据类型 表示常见的简单数据 如整数 浮点数等 复合类型 由基本数据类型组合而成的更复杂的数据类型 如数组 结构体 联合 枚举等 类类型 即用户自己定义的抽象数据类型 为了方便程序员 C 标准库中还提供的一些常用的抽象数据类型 3 1 1内置数据类型 字符类型字符型char 通常用来表示单个字符和小整数 整数类型整型int 短整型short 长整型long 分别代表不同长度的整数值char short int和long类型都可以用signed和unsigned修饰浮点类型浮点型float 双精度浮点型double和长双精度longdouble布尔类型bool类型只有两个值 true和false bool类型 char类型 各种整数类型通称为整值类型 整值类型和浮点类型一起被称为算术类型 内置数据类型的空间大小 标准C 对内置数据类型的空间大小并没有做出严格规定 但为了编写可移植的代码 应该了解你所使用的编译器和机器 避免对这些特性做出某种假定 如下代码可以测试C 编译器为每种数据类型分配的空间大小 includeusingnamespacestd intmain cout sizeof char endl cout sizeof int endl cout sizeof float endl cout sizeof double endl cout sizeof bool endl return0 VisualC 6 0和DEV C 结果 14481 3 1 2指针类型 指针持有一个对象的地址 通过指针可以间接操作这个对象 指针的典型用法 构建链式的数据结构 如链表和树管理程序执行时动态分配的对象作为函数的参数每个指针都有相关的类型 需要在定义指针时指出 不同类型的指针的表示方法和保存的地址值并没有分别 区别只是在于指针指向的对象类型不同 指针的类型指出了如何解释该内存地址保存的内容 以及该内存区域应该有多大 例如 int pi 指向整型的指针 如指向内存地址是1000 则跨越的地址空间是1000 1003char pc 指向字符型的指针 如指向内存地址是1000 则只占据1000这个字节的区域 空指针 指针值为0时表示它不指向任何对象 即空指针 指针不能保存非地址值 也不能被赋值或初始化为不同类型的地址值 intival 100 int pi 编译错误 通用指针 C 提供了一种通用指针 即void 指针 它可以持有任何类型的地址值 不能操纵void指针指向的对象 只能传送该地址值或和其他地址进行比较 C 也不允许void指针到其他类型指针的直接赋值 inta 10 charch k void pv 错误 应该使用pi int pv 通过解引用操作 可以间接访问指针指向的对象 intx 100 y 20 int pi x y 指针的算术运算 指针可以进行加或减整数值的算术运算 这时地址值增加数目取决于指针的类型 指针只有在指向数组元素时 其算术运算才有意义 inta 10 charch k int pi 在原地址值上加1 因为char占1个字节 3 1 3引用类型 引用又称为别名 它可以作为对象的另一个名字 通过引用我们可以间接操纵对象 使用方式类似于指针 但是不需要指针的语法 在程序中 引用主要用作函数的形参 引用由类型标识符和一个取地址符 来定义 引用必须被初始化 intival 100 OK refVal是指向ival的引用int Error 不能用没有内存地址的数值来初始化引用 引用的注意事项 1 引用一旦定义 就不能再指向其他的对象 对引用的所有操作都会被应用在它所指向的对象上 例如 intx 100 y 20 int r不是y的引用 而是x y 引用的注意事项 2 注意引用的初始化和赋值极为不同 初始化时引用 指向 一个对象 赋值时 引用被作为所指对象的别名 虽然C 标准没有规定引用的实现方式 但是在很多C 编译器中 引用被实现为与所指对象占据同一地址空间 例如下面的代码 includeusingnamespacestd intmain inta 10 int 在GNUC 编译环境的输出结果如下 a 10 a 0 x22ff34ra 10 ra 0 x22ff34a 20 a 0 x22ff34ra 20 ra 0 x22ff34 引用与指针的差别 1 定义与初始化指针的定义形式 类型 指针变量 指针的初始化 intx 10 y 20 int pi 可以不初始化pi 引用与指针的差别 2 使用方式指针通过解引用 运算间接访问指向的对象 引用作为对象的别名 可以直接访问对象 例如 pi a 40 引用与指针的差别 3 指针可以不指向任何对象 引用必须指向一个对象 pi 0 pi是空指针 不指向任何对象ri 0 a 0 引用与指针的差别 4 指针之间的相互赋值会改变指向关系 引用之间的相互赋值是它们指向的对象之间的赋值 引用关系本身并不改变 intx 100 y 20 int p1 x y r1仍是x的引用 3 1 4数组 数组是一个单一数据类型对象的集合 其中的单个对象没有命名 但可以通过它在数组中的位置进行访问 即下标访问 例如 ia是包含10个int对象的数组intia 10 数组中下标为3的元素被赋值为7ia 3 7 定义数组 定义数组时需要指定类型名 数组名标识符和数组大小 数组大小是一个不小于1的常量表达式 数组元素的下标从0开始 到数组大小 1 但是 C 并不提供对数组下标范围的检查 需要程序员自己保证正确性 定义数组的同时可以对数组进行初始化 初始值放在花括号中 用逗号隔开 intia 3 1 2 3 intib 4 5 6 7 intic 10 0 1 2 数组的注意事项 一个数组不能被另一个数组初始化 也不能被直接赋值给另一数组 C 也不允许声明一个引用数组 例如 intia 3 1 2 3 intib ia Errorint OK C风格字符串 C 保留了C语言中用字符数组表示字符串的方式 称为C风格字符串 可以通过库函数对这样的字符串进行操作 需要包含标准库头文件 多维数组 定义多维数组 每一维的大小由一对方括号指定 例如 intia 4 3 访问多维数组的元素要指定每一维的下标 例如 ia 1 2 5 多维数组也可以被初始化 intia 3 2 0 1 2 3 4 5 数组与指针 数组名字代表数组第一个元素的地址 它的类型是数组元素类型的指针 例如对下面的数组定义 intia 5 ia是一个int 类型 ia和 3 1 5结构体 结构体 struct 把一组来自不同类型的数据组合在一起构成复合类型 其中的每个数据都是结构体的成员 在内存中依次存放 结构体的成员不能单独使用 必须由结构体类型的变量通过成员选择运算符 来选择 或者由结构体类型的指针通过 运算符选择 定义结构体类型之后 可以创建该类型的许多实例 例如 结构体变量内存中的大小 1 结构体变量的成员在内存中依次存放 因而 原则上 结构体变量在内存中的大小是其所有成员的大小之和 但是 为了提高访问效率 大多数编译器实际上都使用了边界对齐技术 structY charc inti Yst 上面的变量st在内存中占据几个字节呢 是5吗 结构体变量内存中的大小 2 在GCC编译器下 sizeof Y 的结果是8 3 1 6联合 共用体 union和struct的语法类似 只是数据成员的存储方式不同 union的每个成员都是从联合变量的首地址开始存储 所以每次只能使用一个成员 使用union可以节省空间 但是容易出错 unionPacked charc inti floatf doubled 联合的大小是其中double的大小 因为double是占据空间最大的元素Packedx x c a 其余成员现在不可使用x d 3 14 覆盖了成员c的内容 联合的内存布局 3 1 7枚举 枚举定义了一组命名的整数常量 从而提高代码的可读性 例如 enumShapeType circle square rectangle ShapeType枚举类型定义了3个常量 0 1 2分别和名字circle square以及rectangle关联 ShapeType是一个枚举类型 可以用它来定义枚举变量 变量的值只能是枚举成员 也可以自己指定枚举成员的值 enumShapeType circle 10 square 20 rectangle 未指定值的枚举成员 编译器会赋给它相邻的下一个整数值 所以rectangle成员的值是21 枚举类型在必要时 如参与算术运算 会被自动提升为算术类型 枚举的成员名字是不可打印的 输出的是它所表示的整数值 另外 不能使用枚举成员进行迭代 C 不支持枚举成员之间的前后移动 3 1 8类类型 除了内置类型和复合类型之外 C 还允许用户自己定义类类型 并且在标准库中预定义了一些常用的类型 这里先简单介绍几种常用的标准类型 输入输出流 iostream字符串类标准数组 向量类 输入输出流 iostream 输入输出操作是由iostream类提供的 使用iostream需包含标准库头文件 includeiostream库中有两个预定义的对象 cin istream类型的对象 用于从用户终端读入数据 cin的常用方式为 cin 变量名 cin 变量名1 变量名2 cin 变量名1 变量名2 变量名n cout ostream类型的对象 用于向用户终端写数据 cout的常用方式为 cout 表达式 cout 表达式 endl cout 表达式1 表达式2 表达式n 输入输出流 iostream includeusingnamespacestd intmain intival floatfval cin ival cin fval cout ival fval cout ival n fval endl 字符串类 string 除了保留C风格字符串char 之外 C 标准库还定义了字符串类string string类支持字符串对象的各种初始化方式 支持字符串之间的拷贝 比较和连接等操作 还支持对字符串长度的查询和是否为空的判断 并且也可以访问字符串中的单个字符 使用string类 需要包含相应的头文件 string include 字符串类 string include includeusingnamespacestd intmain strings1 s2 创建两个空字符串对象strings3 Hello World 初始化s3strings4 Iam s2 Today 赋值s1 s3 s4 字符串连接s1 5 末尾追加cout s1 s2 endl 输出字符串内容cout Lengthofs1is s1 size endl 输出字符串长度for inti 0 I s1 size i cout s1 i 逐个输出s1中的字符 字符串类 string 字符串比较使用运算符 获得字符串长度使用size 判断字符串是否为空使用empty 可以将一个C风格的字符串赋给string对象 但反之不可 要将string对象转换为C风格字符串 使用c str 操作 其返回结果为constchar 即转换得到的C风格字符串首地址 例如 strings1 Thisisabook intx strlen s1 Errorx strlen s1 c str OK 标准数组 向量类 C 标准库中的vector类为内置数组类提供了一种替代表示 为了使用vector 我们必须包含相关的头文件 vector vector可以像数组一样使用 还可以以STL方式使用 数组习惯 includeusingnamespacestd intmain vectoriv 10 intia 10 for intid 0 id 10 id ia id iv id 向量类 STL使用习惯 STL习惯 include includeusingnamespacestd intmain vectoriv for intid 0 id 10 id iv push back id for intid 0 id iv size id cout iv id 3 2 1内置数据类型的文字常量 当一个数值 例如5 出现在程序中时 它被称为文字常量 literalconstant 文字 是因为只能以它的值的形式指代它 常量 是因为它的值不能被改变 文字常量是不可寻址的 每个文字常量都有相应的类型 其类型由其形式 取值和后缀决定 整数文字常量 整数文字常量可以表示为十进制 八进制或十六进制的形式 例如23可以写成 23 十进制027 八进制0 x17 十六进制十进制整数文字常量默认类型是int 如果数值超出int能够表示的范围 那么其类型是long 八进制和十六进制整数文字常量的类型是int unsignedint long unsignedlong中第一个范围足够表示该数值的类型 可以在文字常量后面加 L 或 l 后缀将其指定为long类型 可以加 U 或 u 将其指定为无符号数 如 116u120UL2L 浮点型文字常量 浮点型文字常量可以写成普通的十进制形式或科学计数法形式 例如浮点数235 8可以表示为 235 82 358E2浮点型文字常量默认为double类型 也可以加后缀改变其类型 float类型的浮点文字常量可以在后面加 F 或 f 标示 后缀 L 或 l 则为longdouble类型 3 14F1 24f2 5E3L 布尔型文字常量 bool型的文字常量只有true和false 字符型文字常量 字符型文字常量是用单引号括起来的单个字符或以斜线开头的转义字符 如 a n 一些常用的转义字符如 字符串文字常量 在程序中经常出现的还有用双引号括起来的字符序列 即字符串文字常量 例如 helloworld 其默认类型是constchar 3 2 2变量和标识符 变量 变量为我们提供了一个有名字的存储区 可以通过代码对其进行读 写处理 变量有时也被称为对象 每个变量都有特定的数据类型 这个类型决定了相关内存的大小 布局 能够存储在该存储区的值的范围以及可以应用在其上的操作 例如 intcount doublesalary 3 2 2变量和标识符 标识符 引用变量要通过其名字 而变量由标识符命名 标识符可以由字母 数字以及下划线组成 但必须由字母或下划线开头 并且区分大小写字母 上面代码中count和salary都是标识符 C 保留了一些词用作关键字 关键字不能作为程序的标识符使用 C 关键字 变量的命名规则 1 变量名字要完全 准确地描述出该变量所代表的事物 变量名不能过短 太短的名字无法传达足够的信息 也不能过长 太长的名字很难写且不实用 研究表明 变量名的最佳长度是9到15个字符 使用i j k这些名字作为循环变量是约定俗成的 但不要在其他场合使用 给布尔变量赋予隐含 真 假 含义的名字 如done error found success或ok等 变量的命名规则 2 常量 typedef和预处理宏全部大写 类和其他类型的名字混合大小写 变量名和函数名中第一个单词小写 后续每个单词首字母大写 如variableOrRoutineName 除了在特定前缀中 不要用下划线作为名字中的分隔符 如student name 用studentName更好 应该避免使用这些名字 令人误解的名字或缩写 具有相似含义的名字 带有数字的名字 拼写错误的单词 仅靠大小写区分的名字 混用多种自然语言 如汉语拼音和英语 标准类型和函数的名字 避免在名字中包含易混淆的字符 例如 数字一 1 小写字母L l 和大写字母i I 数字0和大小写的字母O o 数字2和字母z 变量的右值和左值 变量和文字常量的区别在于变量是可寻址的 对于每个变量 都有两个值与之关联 它的数据值 存储在某个内存地址中 有时也被称为右值 文字常量和变量都可以被用作右值 它的地址值 即存储数据值的那块内存的地址 有时被称为变量的左值 在下面的赋值表达式中 count count 5变量count同时出现在赋值操作的左边和右边 右边的变量被读取 读出其关联的内存中的数据值 而左边的count用作写入 原来的值会被加法操作的结果覆盖 在这个表达式的赋值号右边 count和 5 用作右值 而左边的count用作左值 变量的定义与声明 定义变量 变量的定义会引起相关内存的分配 因为一个变量只能在内存中占据一个位置 所以程序中的每个变量只能定义一次 变量定义的一般格式为 类型名字 也可以一次定义多个同类型变量 如下 类型名字1 名字2 例如 intival ival 5 定义ival后就可以使用ivalinta b c 定义三个int变量a b c 变量的定义与声明 声明变量 如果在一个文件中定义了一个变量 其他的文件还想使用这个变量 就必须声明该变量 变量声明 declaration 的作用是使程序知道该变量的类型和名字 声明不会引起内存分配 程序中可以包含对同一变量的多个声明 声明全局变量格式 extern类型变量名 变量的定义与声明 声明变量 module0 cppintk 2 定义变量k module1 cpp include 声明变量k 说明了在module1 cpp之外的某处有k的定义externintk intmain intt 3 定义变量t 同时也是声明k t 2 使用变量k 变量的初始化 定义变量的同时可以为变量提供初始值 否则对于全局变量 系统会自动提供初始值0 而局部变量是未初始化的 因为引用未初始化变量是程序中常见的错误 且不易被发现 因此建议为每个定义的变量提供一个初始值 C 支持两种形式的初始化 使用赋值操作符的显式语法形式 也叫赋值初始化 intvalue 1024 charch t 将初始值放在括号中的隐式形式 也叫直接初始化 intvalue 1024 charch t 3 2 3const限定词 由const限定的对象不可改变 const在C 中用法非常多 主要有 限定一个对象 变量 限定指针限定引用类的成员函数参数 返回值 限定一个对象 程序中经常会使用某些常数值 如数组的大小 可是程序代码中如果充斥着这些数值 会降低程序的可读性和可维护性 C语言中我们使用预处理指令 define定义符号常量 但是这种常量的缺点在于它只是作简单的字符串替换 没有类型检查 C 中引入了const限定符 它将一个对象限定为常量 如 constintbufSize 1024 企图修改这个值会导致编译错误 因为常量在定义后不能修改 所以必须进行初始化 限定指针 首先回顾一下指针 includeusingnamespacestd intmain intival 1024 int pi DEV C 结果如下 sizeof pi 4sizeof ival 4 pi 0 x22ff70pi 0 x22ff74 ival 0 x22ff74 pi 1024ival 1024 限定指针 从程序结果可以看出 指针在内存中占4个字节 首地址是0 x22ff70 其中存放的是它所指向内容的首地址 int型变量ival在内存中占4个字节 首地址是0 x2274 这块内存存放的是ival的值1024 问题 在这个例子中 如果我们不允许修改ival的值 即1024 怎么办呢 显然可以将ival定义成常量 constintival 1024 那么能否通过间接方式修改一个常量呢 例如 constintival 1024 int pi 可以修改吗 指向常量的指针 1 这段代码会导致编译错误 理由并不是试图通过指针间接修改const 而是 试图将一个const地址赋值给一个非const指针 要保存ival的地址 只能将pi定义成一个指向constint的指针 成为指向常量的指针 constintival 1024 constint pi Error 指向常量的指针 2 在上面的代码中 pi是一个指向常量的指针 它所指向的内存中的内容不可以改变 指向常量的指针 3 指向常量的指针 4 注意 pi是一个指向常量的指针 但pi本身的值可以改变 指向另一个constint 例如 includeusingnamespacestd intmain intargc char argv constintival 1024 constint pi 运行结果 pi 0 x22ff70pi 0 x22ff74 ival 0 x22ff74 pi 0 x22ff70pi 0 x22ff6c ival 0 x22ff6c 指向常量的指针 6 程序运行中的指针变化情况 指向常量的指针 7 C 允许将一个非const地址赋值给const指针 例如 intival 1024 constint pi 我们也可以定义指向非const对象的指针常量 该指针所在内存的值不允许改变 即该指针一旦用某个单元的地址值初始化 那么指针值就不能再改变 但它所指向单元的值可以改变 语法如下 intival 1024 int constpi Error不能改变pi的值 指向非const对象的const指针 1 指向非const对象的const指针 2 指向const对象的const指针 constintival 5 constint constpi pi是一个指向const对象的const指针在pi的定义中 第一个const限定int 表示指针指向的单元是常量 第二个const限定pi 表示指针的值也是一个常量 因此该指针所在内存的值不允许改变 它所指向内存的值也不能改变 限定引用 const也可以限定引用 const引用可以用不可寻址的值初始化 如 int OK 编译器生成一个值为10的临时对象 rc指向这个对象除了限定对象 引用和指针 const的另一个用途是限定函数的形式参数 由const限定的参数在函数体中不能被修改 3 2 3volatile限定词 const和volatile一起被称为CV限定词 volatile的使用语法和const相似 当一个对象的值可能在编译器的控制或检测之外被改变时 那么该对象应该声明为volatile 例如一个被系统时钟更新的对象 编译器执行的某些例行优化行为不能应用在volatile对象上 volatile一般用在多线程或中断处理的程序设计中 3 3运算符和表达式 操作数和应用在这些操作数上的运算符构成了表达式 表达式指定了一个计算 会产生一个结果 即表达式的值 当一个表达式中包含两个或两个以上的运算符时 它被称为复合表达式 复合表达式的计算次序由运算符的优先级和结合性决定 C 语言提供了丰富的运算符 根据操作数的个数 可以分为一元运算符 二元运算符 C 中还有一个三元运算符 也可以根据运算符的特点进行分类 例如算术运算符 位运算符 赋值运算符等 所有的运算符都会从操作数中产生一个值 大多数运算符都不会改变操作数 但是有的运算符会修改操作数的值 这称为运算符的副作用 3 3 1算术运算符 算术运算符包括 注意 和 运算的右操作数不能为0 两个整数的 运算结果还是整数 即整除 运算符只能应用于整值类型操作数 两个非负整数进行 运算 余数为非负值 否则余数的符号取决于实现 和 也可以作用于指针类型的变量 指针的加法一般形式是 pointer number 指针减法除了可以像上面的加法形式之外 还可以进行两个指针的减法 一般要求这两个指针指向同一数组的元素 其结果是它们指向的数组元素的下标之差 例 韩信点兵不足百人 三人一行多一个 七人一行少两个 五人一行正好 问有多少人 例 韩信点兵不足百人 三人一行多一个 七人一行少两个 五人一行正好 问有多少人 3 3 2关系和逻辑运算符 关系运算符有 逻辑运算符有 逻辑运算和关系运算的计算结果是bool类型的 即true或false 在需要整值类型的环境中 它们的结果会被自动提升为1 true 或者0 false 逻辑与和逻辑或运算的计算次序是从左至右 只要能够得到表达式的值 运算就会结束 这种现象被称为逻辑运算的短路 即 对于表达式expr1 expr2 如果expr1的计算结果为false true 则整个逻辑与 逻辑或 表达式的值为false true 不会再计算expr2 判断某年是否为闰年 如果是闰年 它应能被4整除 但不能被100整除 或被100整除 也能被400整除 boolisLeapYear intyear booln1 year 4 0 booln2 year 100 0 booln3 year 400 0 if n1 true 3 3 3赋值运算符 赋值运算符包括 和复合赋值运算符 赋值运算符的左操作数必须是可修改的左值 右操作数的类型必须与左操作数的类型完全匹配 否则编译器会自动将右操作数的类型转换为左操作数的类型 如果不能进行转换 会引起编译错误 赋值运算是有副作用的 它改变了左操作数 赋值表达式本身的值是实际赋给左操作数的值 例如 intival ival 1 结果是1ival 2 5 结果是2 复合赋值运算符 C 还提供了一组复合赋值运算符 一般的语法格式为 aop b等价于 a aop b 这里的op 可以是下面的运算符之一 赋值和初始化的不同 赋值和初始化有时会被混淆 因为都使用同一个运算符 二者的不同之处在于 一个对象只能在它被定义的时候初始化一次 但是可以多次被赋值 intival 12 初始化 等价于intival 12 ival 15 赋值intival2 定义变量 没有初始化ival2 5 赋值 虽然是第一次赋值 但不是初始化 常被误用 在编写程序时 赋值运算符 经常被误用为关系运算 引起程序语义错误 例如 判断变量x的值如果等于1就执行某个操作的代码如果将 误写为 这个条件将永远为true 因为其结果是赋值表达式的值1if x 1 doSomething 3 3 4自增和自减 自增 和自减 运算符为对象加1和减1提供了方便简短的表示 它们最常用于对数组下标 迭代器或指针进行加1或减1操作 自增和自减是一元运算符 其操作数是可修改的左值 自增和自减都有前缀和后缀两种形式 inta 5 b 5 intival ival a ival的值是5 而a的值是6ival b b的值是6 ival的值是6自增 自减运算也常用于指针 这时指针通常指向的是一个数组中的元素 3 3 5位运算符 1 位运算符将操作数解释为有序位的集合 每个位是0或1 位运算符允许程序员设置或测试独立的位或一组位 通常使用无符号的整值数据类型进行位操作 按位非运算 按位非运算符 对操作数的每一位取反 原来的1置为0 0置为1 移位运算 是二元运算 形式 E1 E2 对无符号数或者非负的有符号数 右移一位相当于除2 如果是有符号数的负数 右移运算在左边空位或者插入符号位 或者插入0 这由具体的实现定义 3 3 5位运算符 2 按位 位运算表达式 例子 加密解密 intmain chara1 f a2 i a3 r a4 e charsecret 8 a1 char a1 secret a2 char a2 secret a3 char a3 secret a4 char a4 secret cout 密文 a1 a2 a3 a4 endl a1 char a1 secret a2 char a2 secret a3 char a3 secret a4 char a4 secret cout 原文 a1 a2 a3 a4 endl 3 3 6sizeof运算符 sizeof运算符的作用是计算一个对象或类型名的字节数 返回类型是size t size t是一种与实现相关的typedef定义 在标准库头文件中定义 sizeof有以下三种形式 sizeof typename sizeof object sizeofobject 3 3 6sizeof应用在内置类型 在所有C 实现中 sizeof应用在char unsignedchar signedchar类型上的结果都是1 对其他内置类型应用sizeof运算 其结果由实现决定 sizeof应用在枚举类型上的结果是表示枚举类型数值的低层整值类型的字节数 3 3 6sizeof应用在数组 sizeof运算符应用在数组上时 返回的是整个数组的字节个数 即数组长度乘以每个元素的字节数 sizeof应用在指针上时返回的是指针的字节长度 即使指针是指向数组的 例如 intia 0 1 2 size tarraysize siezeofia 32位机器上 arraysize的值是12int pa ia size tpointersize siezeof pa pointersize的值是4 3 3 6sizeof应用在引用类型 应用在引用类型上的sizeof运算符返回的是被引用对象的字节数 charch a char sizeof pc 4sizeof运算符是在编译时计算 因此被作为常量表达式 它可以用在任何需要常量表达式的地方 如数组的大小等 3 3 7new和delete 对象可以静态分配 即编译器在处理程序源代码时分配 也可以动态分配 即程序运行时调用运行时刻库函数来分配 这两种内存分配方法的主要区别是效率和灵活性 静态内存分配效率比较高 但是缺少灵活性 而存储未知类型或未知数目的元素却需要动态内存分配的灵活性 C 语言则通过new和delete两个运算符来完成动态存储空间的管理静态和动态内存分配的主要区别 静态对象是有名字的变量 我们直接对其进行操作 而动态对象是没有名字的变量 我们通过指针间接地对它进行操作 静态对象的分配与释放由编译器自动处理 动态对象的分配与是否必须由程序员显式地管理 new new表达式的第一种形式 new运算符返回新分配的对象的指针 new表达式有三种形式 第一种用于分配特定类型的单个对象 并返回其地址 语法形式为 new类型或者new类型 初始值 例如 int ip1 newint ip1 512 int ip2 newint 100 new new表达式的第二种形式 new表达式的第二种形式可以在堆上分配特定类型和大小的数组 并返回数组首地址 但是不能对数组进行显式的初始化 语法形式为 new类型 数组大小 例如 int ipa newint 100 注意 这里的数组大小是指数组元素的个数 用new分配的数组其大小不必是常量 可以在程序执行期间指定 比较 int ip newint 100 int ip newint 100 new new表达式的第三种形式 new表达式的第三种形式允许程序员将对象创建在已经分配好的内存中 这种形式被称为定位new表达式 其形式如下 new place address 类型 place address必须是指针 程序员可以预先分配大量的内存 以后通过定位new表达式在这段内存中创建对象 使用定位new 必须包含标准库头文件 new new表达式的第三种形式 定位new表达式示例 include includeusingnamespacestd 预分配一段空间 首地址在buf中保存char buf newchar 1000 intmain int pi new buf int 在buf中创建一个int对象 此时不再重新从堆上分配空间 delete 堆上的空间在使用之后必须释放 否则会造成内存泄漏 new表达式分配的空间用delete运算符释放 delete释放new分配的单个对象 释放new分配的单个对象 使用 delete指针 表达式 例如 int ip newint ip 512 不再使用这个对象时 释放内存 释放指针指向的int对象 将空间归还给动态存储区deleteip 空悬指针 执行delete运算后 指针ip指向的空间被释放 不能再使用ip指向的内存 delete后的ip不是空指针 而是 空悬指针 即指向不确定的单元 在大多C 实现中 delete之后 ip中保存的仍然是delete之前的地址值 但是这个地址单元的使用权已经通过delete操作归还给动态存储区了 再继续通过ip间接使用这个单元是非法的 会引起不可预料的运行错误 int ip newint ip 512 OKdeleteip 释放了空间 ip成为空悬指针 ip 100 危险 不能再使用ip指向的单元intx ip OK 仍然可以使用ip这个指针 delete释放new分配的数组 new表达式分配的数组使用 delete 指针 形式释放 例如 int pa newint 100 不再使用这个数组时 释放内存 释放指针pa指向的数组 将空间归还给动态存储区delete pa 由于定位new并不实际分配空间 所以没有对应的delete表达式 3 3 8条件运算符 C 中唯一的一个三元运算符 条件运算符的语法格式如下 expr1 expr2 expr3 intmin intia intib return ia ib ia ib 这段代码是如下代码的简写形式 intmin intia intib if ia ib returnia elsereturnib 3 3 9逗号运算符 逗号表达式是一系列由逗号分开的表达式 这些表达式从左向右计算 逗号表达式的结果是最右边表达式的值 例如 a 1 b 2 a b表达式的值是3 3 3 10运算符的优先级 运算符具有优先级和结合性 优先级和结合性是指复合表达式中运算符计算的顺序 在复合表达式中 优先级高的子表达式先被计算 相同优先级的运算按照结合性从左向右或从右向左计算 大部分二元运算符都是左结合的 即从左向右计算 赋值运算符是右结合的 从右向左计算 括号可以改变优先级 在计算复合表达式时先计算所有括号中的子表达式 再由计算的结果继续参与运算 3 3 10运算符的优先级 运算符优先级表 1 运算符优先级表 2 运算符优先级表 3 运算符优先级表 4 3 3 11类型转换 问题 表达式中的操作数经常可能具有不同的类型 那么在计算表达式时如何处理呢 例如 intival ival 3 54 2 如何计算 最终ival的结果是5 3 3 11类型转换 C 并不是真的将double和int值加在一起 而是提供了一组算术转换 以便在执行算术运算之前 将两个操作数转换成相同的类型 基本的转换规则是小类型被提升为大类型 以防止精度损失 因此加法计算的结果是5 54 double类型 下一步将结果赋给ival 赋值运算符两个操作数不同类型时 会尝试将右边的操作数转换为左操作数的类型 这个例子中 double值被截取 变成5 赋给ival 因为从double到int的转换会损失精度 编译器一般会给出警告 3 3 11类型转换 上述的转换过程由编译器自动完成 因此被称为隐式类型转换 我们也可以指定显式类型转换来禁止这种自动转换 例如 将3 54显式转换为int类型再计算ival static cast 3 54 2 隐式类型转换 C 定义了一组内置类型对象之间的标准转换 在必要时编译器自动应用这些转换 隐式类型转换发生在下列情况下 混合类型的算术表达式中 最宽的数据类型成为转换的目标类型 算术转换 用一种类型的表达式赋值给另一种类型的对象 目标转换类型是被赋值对象的类型 参数传递的表达式类型与函数形参类型不同 目标转换类型是形参的类型 从函数返回的表达式类型与返回类型不同 目标转换类型是函数的返回类型 隐式类型转换 注意 算术转换有两个指导原则 第一 为防止精度损失 必要时类型总是被提升为较宽的类型 第二 所有含有小于整型的整值类型的算术表达式在计算之前其类型都会被转换为整型 称为整值提升 进行整值提升时 类型char signedchar unsignedchar shortint都被提升为int类型 如果机器上的int类型足够表示unsignedshort类型的值 则unsignedshort被转换为int 否则提升为unsignedint 枚举类型被提升为能够表示其底层类型所有值的最小整值类型 显式类型转换 有些情况下我们需要强制类型转换 如 void 指针被称为通用指针 它可以指向任何类型的数据 但是void 指针不能直接解引用 因为没有类型信息 所以 void 的指针必须先转换为特定类型的指针 C 不允许从void 指针到其他类型指针的自动转换 所以这时需要强制类型转换 另外 我们有时希望改变通常的标准转换 或者避免出现多种转换可能的二义性情况 在这些情况下 都需要显式强制类型转换 显式类型转换 显式转换也被称为强制类型转换 包括四个强制类型转换运算符 static castdynamic castconst castreinterpret cast显式转换运算符的一般形式如下 将expr显式转换为type类型castname expr const cast const cast将转换掉表达式的const限定和volatile限定 例如 intmain constinti 0 int j 用const cast执行一般的类型转换 或者用其他三种形式来转换掉常量性都会引起编译错误 static cast static cast可以 显式进行编译器隐式进行的任何类型转换 窄化 转换具有潜在危险的类型转换 如void 指针的强制类型转换算术值到枚举型的强制转换基类对象 或指针 引用 到其派生类对象 或指针 引用 的强制转换 如 enummumble first 1 second third externintival mumblee static cast ival 有潜在危险 因为正确性取决于ival的值是否为1 2 3 reinterpret cast reinterpret cast通常对于操作数的位模式执行一个比较低层次的重新解释 它的正确性很大程度依赖程序员的主动管理 usingnamespacestd structstudent charname 16 longid intscore intmain studentwangli strcpy wangli name WangLi wangli id 2009123 wangli score 87 cout 执行结果 WangLi2009123871735287127690076822935604253206200912387 dynamic cast dynamic cast支持在运行时刻识别由指针或引用指向的类对象 对dynamic cast的详细讨论在多态性和虚函数一章 旧式强制转换 上面的四种显式转换是标准C 新引入的 在此之前 显式类型转换由通用的强制类型转换语法实现 现称为旧式强制转换 标准C 仍然支持旧式强制转换 但建议使用新的转换语法 旧式强制转换有下面两种形式 type expr C 强制转换 type expr C语言强制转换旧式转换可以用来代替标准C 中的static cast const cast和reinterpret cast 3 4语句 程序最小的独立单元是语句 语句由分号结束 C 程序包含有简单语句和复合语句 缺省情况下语句以出现的顺序执行 控制语句则可以根据条件改变程序中语句的执行顺序 3 4 1简单语句和复合语句 1 空语句 即仅有一个分号 例如 while string inBuf 空语句表达式语句 在表达式末尾加上分号 就构成表达式语句 例如 inta 5 b 10 a 值5被丢弃 副作用是a递增b 3 值被丢弃 副作用是b变为3a b 没有保留计算结果9 无副作用简单语句由单个语句构成 上面的表达式语句都是简单语句 3 4 1简单语句和复合语句 2 有些语句在语法上只允许执行一条指定的语句 如条件和循环 但在逻辑上却要多条语句序列才能完成相关功能 在这种情况下 可以用复合语句来代替单个语句 复合语句是由一对花括号 括起来的语句序列 复合语句被视为一个独立的单元 它可以出现在程序中任何需要单个语句的地方 复合语句也称为块 3 4 2声明语句 在C中 总是要在一个程序块的最开始就定义所有的变量 C 不再沿用C的这种习惯 C 中对象的声明被作为一条语句 可以放在程序中任何允许语句出现的地方 可以在需要的时候再定义变量 同时进行初始化 并且使声明具有局部性 即声明语句出现在被定义对象首次使用的局部域内 声明语句将一个或多个新的标识符引入当前块中 说明它们的属性 类型 存储类别等 声明是向程序中引入一个标识符 它告诉编译器 这个变量在某个地方可以找到 它是这样的类型 而定义是 在这里建立这种类型的变量 并为这个它存储分配空间 定义同时也是声明 3 4 2声明语句 除了下面几种声明之外 其他的声明也是定义 只是声明了一个函数 但没有指定函数体 包含extern说明符或链接指示符 且不包含初始化部分或函数体部分 类声明中的静态数据成员声明 类名字声明 typedef声明 using声明或using指令 3 4 2声明语句 下面是一组定义 inta 定义aexternconstintc 1 定义cintf intx returnx a 定义fstructS inta intb 定义SintX y 1 定义X yenum up down 定义枚举常量下面的都是声明语句 externinta 声明aexternconstintc 声明cintf int 声明fstructS 声明StypedefintInt 声明IntexternXanotherX 声明anotherX 3 4 3if语句 if语句测试指定表达式的值是true或false 有条件地选择执行一条语句或一个语句块 if语句有两种语法形式 if condition statement或 if condition statement1elsestatement2 3 4 3if语句 condition可以是条件表达式或是一条具有初始化功能的声明语句 在condition中定义的对象 只在if控制的语句或语句块中可见 包括else部分 在if语句外访问该对象会导致编译错误 例如 if intival x y ival在这个语句块中可见 else ival在这个语句块中可见 if ival Error ival不可见 注意 较早的某些编译器对这种变量声明方式可能会有不同的实现 如VC 6 0 3 4 3if语句 if语句可以嵌套 如果if子句的数目多于else子句的数目 会出现空悬else问题 引起二义性 C 规定 else子句总是与最近的未匹配的if语句相匹配 从而解决二义性问题 有些编码风格建议在if和else语句中应该总是使用复合语句括号 来避免修改代码可能带来的混淆和错误 分支语句 举例 if x 6 if y 6 cout 设备正常 endl elsecout 设备出错 endl 等价 if x 6 if y 6 cout 设备正常 endl elsecout
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026新疆昆东经济技术开发区管委会招聘19人备考题库及完整答案详解
- 2025浙江LT2025122901ZP0000宁波文旅会展集团有限公司招聘9人备考题库含答案详解
- 2026中闻印务投资集团有限公司财务经理招聘1人备考题库含答案详解
- 2025湖南衡阳市衡阳县湘南船山高级技工学校招聘专业技术人员6人备考题库及答案详解(易错题)
- 2026年六安皖西中学公开招聘2026届应届公费师范毕业生备考题库及答案详解(易错题)
- 2025年镇赉县事业单位公开招聘工作人员(含专项招聘高校毕业生)及基层治理专干备考题库(47人)及答案详解(夺冠系列)
- 2025河北秦皇岛市外事旅游职业学校教师招聘备考题库及完整答案详解一套
- 2025云南康旅酒店管理有限公司社会招聘5人备考题库含答案详解
- 2026江苏南京江北新区退役军人服务中心招聘编外人员6人备考题库有答案详解
- 2026年1月福建厦门市集美区灌口医院补充编外人员招聘2人备考题库及参考答案详解一套
- 风电项目数据采集与监控方案
- 人教版(PEP)六年级英语上册复习知识点大全
- 咨询服务风险管理策略-洞察及研究
- 涉水人员健康知识培训课件
- 物业维修工安全培训课件
- 户外电源技术讲解
- 一年级体育课题申报书
- 墙面夹芯板安装施工方案
- 六年级语文阅读理解之托物言志(知识梳理技法点拨例文分析)(含答案)
- 钣金供应商管理办法
- 煤矿自救器使用课件
评论
0/150
提交评论