运算
进行加法运算的 + 和进行乘法运算的 * 等符号,称为运算符。
运算符和操作数
读取两个整数值,然后显示它们的和、差、积、商和余数。
代码清单2-1
1 | /*程序名:list0201.c*/ |
反汇编(debug模式)
1 | 00665090 push ebp |
反汇编(release)
1 | 00141080 push ebp |
硬编码
1 | 55 push ebp |
取商:
1 | mov eax,被除数 |
取余数:
1 | mov eax,被除数 |
乘法运算符和加减运算符

除法运算的商和余数


使用 printf 函数输出 %

获取整数的最后一位数字
显示读取出的整数的最后一位数字。
代码清单2-2
1 | /*程序名:list0202.c*/ |
反汇编
1 | //printf("最后一位是%d。\n", x % 10); |
被除数/除数=商…余;余数=被除数-除数*商,至于 eax 赋值 66666667h,暂时还不理解。

多个转换说明
读取两个整数,并显示它们的商和余数。
代码清单2-3
1 | /*程序名:list0203.c*/ |
反汇编
1 | //printf("a除以b得%d余%d。\n", a / b, a % b); |
做了两次除法运算。
单目运算

对读取的整数值进行符号取反操作,并输出结果。
代码清单2-4
1 | /*程序名:list0204.c*/ |
反汇编
1 | //printf("符号取反之后的值是%d。\n", -num); |
使用 neg 指令实现取反操作
赋值运算符: =
表达式: a+b
赋值表达式: c=a+b
数据类型
求平均值
读取两个整数,求出它们的平均值
代码清单2-5
1 | /*程序名:list0205.c*/ |
反汇编
1 | //printf("它们的平均值是%d。\n", (a + b) / 2); |
SAR 右移 1 位,实现平均值。
整数和浮点数
C语言中以浮点数的形式来表示实数,double 类型是双精度浮点数类型。
代清单2-6
1 | /*程序名:list0206.c*/ |
另外需要注意的是,在使用 printf 函数输出 double 型值的时候,转换说明本那个使用%d,而要使用 %f。
反汇编
1 | //printf("double型变量x的值:%f\n", x); |
double占8字节大小。这里使用 movsd 指令将浮点值 x 存储到 FPU 寄存器栈中,使用 divsd 指令做浮点数的除法运算。
整型常量和浮点型常量
直接在程序中指定数值的常量也有类型的区别。像 5 和 37 这样的常量,它们都是整数类型的,所以称为 整型常量。像 3.14 这样包含小数的常量,称为 浮点型常量。
Double类型的运算
编写一段程序,读取两个实数值,显示它们的和、差、积、商。
代码清单2-7
1 | /*程序名:list0207.c*/ |
反汇编
1 | //printf("vx + vy = %f\n", vx + vy); |

浮点数算术指令:addsd、subsd、mulsd、divsd
数据类型和运算
进行整数/整数运算的时候,商的小时部分会被舍弃,但是浮点数之间的运算,就不会进行舍弃处理。

这个涉及到数据类型的转换,下面演示的是两个不同类型的变量类型转换的案例。
案例演示
1 | /*程序名:list0207_1.c*/ |
反汇编
1 | //printf("vx + x = %f\n", vx + x); |
操作数的类型不同时,脚下的数据类型的操作数会转换为较大的数据类型,然后再进行运算。反汇编中使用 SEE2 指令集中的 cvtsi2sd 指令:取出最低位的 64 位整型,并将其转换位一个浮点值。内部指令:_mm_cvtsi64_sd
SEE 指令集
第一个支持SSE的CPU是Pentium III,在FPU与SSE之间共享执行支持。当编译出来的软件能够交叉的同时以FPU与SSE运作,Pentium III并无法在同一个周期中同时执行FPU与SSE。这个限制降低了指令流水线的有效性,不过XMM寄存器能够让SIMD与标量浮点运算混合执行,而不会因为切换MMX/浮点模式而产生性能的折损。
SSE的全称是 Sreaming SIMD Extensions, 它是一组Intel CPU指令,用于像信号处理、科学计算或者3D图形计算一样的应用。其优势包括:更高分辨率的图像浏览和处理、高质量音频、MPEG2视频、同时MPEG2加解密;语音识别占用更少CPU资源;更高精度和更快响应速度。使用SSE指令集,主要是通过8个128-bit的寄存器:xmm0到xmm7 来完成的。在Linux下可以使用cat /proc/cpuinfo来查看CPU支持哪些指令集。 SSE的指令集是X86架构CPU特有的,对于ARM架构、MIPS架构等CPU是不支持的,所以使用了SSE指令集的程序,是不具备可移植标准的。
代码清单2-8
1 | /*程序名:list0208.c*/ |
反汇编
1 | //n1 = 5 / 2; |
上面的是书中的例子(2-8),该例子演示:操作数的类型不同时,较小的数据类型操作数会转换成较大的数据类型然后再进行运算。查看 2-8 的反汇编,发现这些运算编译器在编译过程中就算好了结果,程序运行时,其类型并没有转换。因此 2-8 的例子还有所欠缺。

这一节的知识点主要是解释:实数常量存储到 int 类型中无法保留小数点后面的值,但是 double 类型是可以存储小数和整数。主要原因就是 double 类型占的字节大于 int 类型占的字节。
数据类型的转换
修改代码清单2-5,尝试输出小数。
代码清单2-9
1 | /*程序名:list0209.c*/ |
反汇编
1 | //printf("它们的平均值是%f。\n", (a + b) / 2.0); |
printf函数类型转换和之前演示的数据类型转换的案例一样,使用 cvtsi2sd 指令,将 eax 寄存器的整数值,扩展为浮点值并存储到 xmm0 寄存器中。
代码清单2-10
1 | /*程序名:list0210.c*/ |
反汇编
1 | //printf("它们的平均值是%f。\n", (double)(a + b) / 2); |
“(double)(a + b) / 2” 和 “(a + b) / 2.0” 两种写法汇编表现形式都是一样的。
转换说明
读取三个整数,并显示它们的和以及平均值的的程序。
代码清单2-11
1 | /*程序名:list0211.c*/ |
反汇编
1 | //sum = a + b + c; |
另外,如果设定了 “-”,数据会左对齐显示,未设定则会右对齐显示。
代码清单2-12
1 | /*程序名:list0212.c*/ |
反汇编
1 | //printf("[%d]\n", 123); |
总结
1 | /*程序名:summary.c*/ |
反汇编
1 | //printf("(a + b)/2 = %d\n", (a + b) / 2); |
课后练习
练习2-1
1 | /*程序名:lx2-1.c*/ |
练习2-2
1 | /*程序名:lx2-2.c*/ |
练习2-3
1 | /*程序名:lx2-3.c*/ |
练习2-4
1 | /*程序名:lx2-4.c*/ |
练习2-5
1 | /*程序名:lx2-5.c*/ |
练习2-6
1 | /*程序名:lx2-6.c*/ |
- 本文标题:C语言语法入门-运算和数据类型
- 本文作者:9unk
- 创建时间:2023-05-07 11:00:00
- 本文链接:https://9unkk.github.io/2023/05/07/c-yu-yan-yu-fa-ru-men-yun-suan-he-shu-ju-lei-xing/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!