搜索引擎优化网站排名,wordpress postfix,wordpress标签使用方法,网站集群建设ppt系列文章
C 系列 前篇 为什么学习C 及学习计划-CSDN博客
C 系列 第一篇 开发环境搭建#xff08;WSL 方向#xff09;-CSDN博客
C 系列 第二篇 你真的了解C吗#xff1f;本篇带你走进C的世界-CSDN博客
C 系列 第三篇 C程序的基本结构-CSDN博客
C 系列 第四篇 C 数据类型…系列文章
C 系列 前篇 为什么学习C 及学习计划-CSDN博客
C 系列 第一篇 开发环境搭建WSL 方向-CSDN博客
C 系列 第二篇 你真的了解C吗本篇带你走进C的世界-CSDN博客
C 系列 第三篇 C程序的基本结构-CSDN博客
C 系列 第四篇 C 数据类型上篇—基本类型-CSDN博客
前言 我们上一篇总结了C的基本类型涉及整形和浮点型总的来说都是数值类型在实际编程中一定会碰到数值相关的算术运行。所以这一篇我们总结下C的算术运算最主要的是不同类型 的整形或浮点型进行算术运算时候的一些隐式类型转换虽然是一些基本得知识但真的是容易被遗忘或者编程时被忽视的点相信有足够编程经历的程序猿们多少都被类型隐式转换坑过。
C运算符 C使用运算符来运算。提供了几种运算符来完成5种基本的算术计算分别是 加法、减法-、乘法*、除法、以及求模% 每种运算符都使用两个值操作数来计算结果运算符及操作数构成了表达式变量和常量都可以用作操作数。C中这些算术运算符的使用方式及限制和C 语言是一致的。 运算符对操作数执行加法运算。例如420 等于 24。 - 运算符从第一个数中减去第二个数。例如12-3 等于 9。 * 运算符将操作数相乘。例如28 * 4 等于 112。 / 运算符用第一个数除以第二个数。例如1000 / 5等于200。如果两个操作数都是整数则结果为商的整数部分。例如17 / 3 等于 5小数部分被丢弃。 % 运算符求模。也就是说它生成第一个数除以第二个数后的余数。例如19 % 6 为 1因为 19是 6 的 3 倍余 1。两个操作数必须都是整型将该运算符用于浮点数将导致编译错误。 运算符的优先级
示例1不同优先级操作符 int value3 4 * 5; 结果是35还是23呢 当多个运算符可用于同一个操作数时C使用优先级规则来决定首先使用哪个运算符。算术运算符遵循通常的代数优先级先乘除后加减。因此 34*5 指的是 3(4*5)而不是(34)*5结果为 23而不是 35。当然可以使用括号来执行自己定义的优先级。 乘法*、除法/和 取模%优先级相同。同样加和减的优先级也相同但比乘除低。
示例2同优先级操作符 float value 120 / 4 *5; 结果是150 还是6呢 运算符 / 和 * 的优先级相同因此优先级本身并不能指出程序究竟是先计算120除以 4.还是先计算4乘以5。因为第一种选择得到的结果是150而第二种选择的结果是6因此选择十分重要。 当两个运算符的优先级相同时C将看操作数的结合性是从左到右还是从右到左。从左到右的结合性意味着如果两个优先级相同的运算符被同时用于同一个操作数则首先应用左侧的运算符。从右到左的结合性则首先应用右侧的运算符。C 及C 语言中乘除都是从左到右结合的。这说明应当先对4使用左侧的运算符。也就是说用120 除以 4得到的结果为 30然后再乘以 5结果为 150。注意仅当两个运算符被用于同一个操作数时优先级和结合性规则才有效。
示例3有优先级和结合性是否就能确保结果 int value 20 * 5 24 * 6 运算符优先级表明了两点:程序必须在做加法之前计算20 * 5必须在做加法之前计算24 * 6。但优先级和结合性都没有指出应先计算哪个乘法。读者可能认为结合性表明应先做左侧的乘法但是在这种情况下两个*运算符并没有用于同一个操作数所以该规则不适用。事实上C把这个问题留给了实现让编译器来决定在系统中的最佳顺序。对于这个例子来说两种顺序的结果是一样的。 但是也有两种顺序结果不同的情况比如后边章节会总结的递增运算符如下例子中vlaue4 就是一个极端的例子。 类型转换 C及C 语言丰富的类型允许根据需求选择不同的类型这也使计算机的操作更复杂。例如将两个 short 值相加涉及到的硬件编译指令可能会与将两个 long 值相加不同。由于有 11 种整型和 3 种浮点类型因此计算机需要处理大量不同的情况尤其是对不同的类型进行运算时。为处理这种潜在的混乱C及C语言在以下情况中会自动执行类型转换: 1、将一种算术类型的值赋给另一种算术类型的变量时将对值进行转换; 2、表达式中包含不同的类型时将对值进行转换; 3、将参数传递给函数时将对值进行转换。 4、使用 和 函数返回类型不同的类型变量接受返回值将对值进行转换。这个也是实际编程中经常会碰到的bug
初始化及赋值时的类型转换 C允许将一种类型的值赋给另一种类型的变量。这样做时值将被转换为接受变量的类型如下示例long 类型的值赋值给int 型时转换为了ini 型double赋值给int 型时也转换为了int型。 将一个值赋给值取值范围更大的类型通常不会导致什么问题。例如将 short 值赋给long 变量并不会改变这个值只是占用的字节更多而已。然而将一个很大的long值(如2111222333)赋给float变量将降低精度。因为 float 只有6 位有效数字因此这个值将被四舍五入为 2.11122E9。因此有些转换是安全的有些则会带来麻烦。如下表列出了可能出现的转换问题。 以{}初始化时进行的转换 C11 将使用大括号的初始化称为列表初始化因为这种初始化常用于给复杂的数据类型提供值列表。它对类型转换的要求更严格。具体地说列表初始化不允许缩窄(narrowing)即变量的类型可能无法表示赋给它的值。例如不允许将浮点型转换为整型。将整型转换为浮点型被允许条件是编译器知道目标变量能够正确地存储赋给它的值。例如可将 long 变量初始化为 int 值因为 long 总是至少与 int 一样长;相反方向的转换也可能被允许只要 int 变量能够存储赋给它的 long 常。 表达式中的转换 当同一个表达式中包含两种不同的类型时C将执行两种自动转换首先一些类型在出现时便会自动转换其次有些类型在与其他类型同时出现在表达式中时将被转换。 先来看看自动转换。在计算表达式时C将bool、char、unsigned charsigned char 和 short 值转换为int。这些转换被称为整型提升(integral promotion)。 我们还是写一个小用例来说明问题如下我们声明了两个short类型的变量a和b并打印它们的大小以字节为单位。然后我们将a和b相加并打印相加后的结果的大小。 当运行这个程序时会发现a和b的大小都是 2 字节。然而当我们将它们相加后实际相加后结果并不需要int来存储结果的大小却是 4 字节这就证明了short类型在表达式计算中被自动提升为int类型。 同样wchar_t 被提升成为下列类型中第一个宽度足够存储wchar_t取值范围的类型int、unsigned int、long 或unsigned long. 再来看下不同类型进行算术运算时进行的一些转换。 当运算涉及两种类型时较小的类型将被转换为较大的类型。例如用 9.0 除以 5。由于9.0 的类型为 double因此程序在用5除之前将 5 转换为 double 类型。运算时的转换符合以下规律 1、如果有一个操作数的类型是 long double则将另一个操作数转换为 long double。 2、否则如果有一个操作数的类型是double则将另一个操作数转换为double。 3、否则如果有一个操作数的类型是 float则将另一个操作数转换为float。 4、否则说明操作数都是整型因此执行整型提升。 5、在这种情况下如果两个操作数都是有符号或无符号的且其中一个操作数的级别比另一个低则转换为级别高的类型。这里所说的级别就是 比如long 级别比int 高。 6、如果一个操作数为有符号的另一个操作数为无符号的且无符号操作数的级别比有符号操作数高则将有符号操作数转换为无符号操作数所属的类型。 7、否则如果有符号类型可表示无符号类型的所有可能取值则将无符号操作数转换为有符号操作数所属的类型。 8、否则将两个操作数都转换为有符号类型的无符号版本。
函数涉及的转换 传递参数时的类型转换通常由C函数原型控制。函数返回值则由 接受返回值的变量类型进行确认在日常代码工程中经常能看到 有人 封装的一个函数 返回 -1 int型 但是 使用的人没注意看用的是无符号int 定义的变量 进行接受的就会导致 整个逻辑判断出错一定要当心。
强制类型转换 C还允许通过强制类型转换机制显式地进行类型转换。强制类型转换的格式有两种。我们还是用实例说明问题如下示例中 a 是short 型 可以 使用(typename) variable 或者 typename (variable) 的方式进行强制类型转换同时强制类型转换不会修改原有变量的类型而是创建一个新的、指定类型的值可以在表达式中使用这个值。 (typename) variable 或者 typename (variable) 第一种格式来自C语言第二种格式是纯粹的 C。新格式的想法是要让强制类型转换就像是函数调用。 当然C 认为C语言的强制转换太危险所以还增加了其他的约束力更强的强制类型转换有4中分别如下我们这里只做简单 陈述后边涉及到再展开讨论 static_cast用于非多态类型之间的转换如基本数据类型之间的转换以及具有继承关系的类型之间的转换。但它不能用于将 const 或 volatile 限定符添加或删除。 dynamic_cast用于具有继承关系的类型之间的转换它在运行时进行类型检查只能用于具有虚函数的类多态类型。它可以用于将指向基类的指针或引用转换为指向派生类的指针或引用或者将指向派生类的指针或引用转换为指向基类的指针或引用。如果转换失败dynamic_cast 运算符将返回一个空指针对于指针类型或引发一个 std::bad_cast 异常对于引用类型。 const_cast用于添加或删除 const 或 volatile 限定符。它可以用于将常量对象转换为非常量对象或者将非常量对象转换为常量对象。但是const_cast 并不能用于修改本来就是常量的对象或者用于修改指向常量对象的指针。 reinterpret_cast用于不同类型之间的强制类型转换它可以将任意类型的指针或引用转换为其他类型的指针或引用甚至可以将指针转换为整数类型或者将整数类型转换为指针。但是使用 reinterpret_cast 进行类型转换时需要非常小心因为它会绕过编译器的类型检查可能导致未定义的行为。
总结 C使用运算符来提供对数字类型的算术运算加、减、乘、除和求模。当两个运算符对同一个操作数进行操作时C的优先级和结合性规则可以确定先执行哪种操作。 对变量赋值、在运算中使用不同类型、使用强制类型转换时C将把值从一种类型转换为另一种类型。很多类型转换都是“安全的”即可以在不损失和改变数据的情况下完成转换。例如可以把 int 值转换为 long 值而不会出现任何问题。对于其他一些转换如将浮点类型转换为整型则需要更加小心。