详解C语言中的符号常量、变量与算术表达式

C语言中的符号常量
在结束讨论温度转换程序前,我们再来看一下符号常量。在程序中使用 300、20 等类似的“幻数”并不是一个好习惯,它们几乎无法向以后阅读该程序的人提供什么信息,而且使程序的修改变得更加困难。处理这种幻数的一种方法是赋予它们有意义的名字。#define 指令可以把符号名(或称为符号常量)定义为一个特定的字符串:

#define 名字 替换文本

在该定义之后,程序中出现的所有在 #define 中定义的名字(既没有用引号引起来,也不是其它名字的一部分)都将用相应的替换文本替换。其中,名字与普通变量名的形式相同:它们都是以字母打头的字母和数字序列;替换文本可以是任何字符序列,而不仅限于数字。
在该定义之后,程序中出现的所有在 #define 中定义的名字(既没有用引号引起来,也不是其它名字的一部分)都将用相应的替换文本替换。其中,名字与普通变量名的形式相同:它们都是以字母打头的字母和数字序列;替换文本可以是任何字符序列,而不仅限于数字。

#include <stdio.h>

#define LOWER 0 /* lower limit of table */
#define UPPER 300 /* upper limit */
#define STEP 20 /* step size */

/* print Fahrenheit-Celsius table */
main()
{
 int fahr;
 for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP)
 printf("%3d %6.1f\n", fahr, (5.0/9.0)*(fahr-32));
}

其中,LOWER、UPPER 与 STEP 都是符号常量,而非变量,因此不需要出现在声明中。符号常量名通常用大写字母拼写,这样可以很容易与用小写字母拼写的变量名相区别。注意,#define 指令行的末尾没有分号。

变量与算术表达式
我们来看下一个程序,使用公式℃=(5/9)(℉-32)打印下列华氏温度与摄氏温度对照表:

0 -17
20 -6
40 4
60 15
80 26
100 37
120 48
140 60
160 71
180 82
200 93
220 104
240 115
260 126
280 137
300 148

此程序中仍然只包括一个名为 main 的函数定义。它比前面打印“hello, world”的程序长一些,但并不复杂。这个程序中引入了一些新的概念,包括注释、声明、变量、算术表达式、循环以及格式化输出。该程序如下所示:

#include <stdio.h>
/* 当 fahr=0,20,… ,300 时,分别打印华氏温度与摄氏温度对照表 */
main()
{
 int i;
 int fahr, celsius;
 int lower, upper, step;
 lower = 0; /* 温度表的下限 */
 upper = 300; /* 温度表的上限 */
 step = 20; /* 步长 */

 fahr = lower;
 while (fahr <= upper) {
 celsius = 5 * (fahr-32) / 9;
 printf("%d\t%d\n", fahr, celsius);
 fahr = fahr + step;
 }
 scanf("%s", &i);
}

其中的一行:

/*当 fahr=0,20,… ,300 时,分别打印华氏温度与摄氏温度对照表 */

称为注释,此处,它简单地解释,该程序是做什么用的。包含在/*与*/之间的字符序列将被编译器忽略。注释可以自由地运用在程序中,使得程序更易于理解。程序中允许出现空格、制表符或换行符之处,都可以使用注释。

在 C 语言中,所有变量都必须先声明后使用。声明通常放在函数起始处,在任何可执行语句之前。声明用于说明变量的属性,它由一个类型名和一个变量表组成,例如:

int fahr, celsius;
int lower, upper, step;

其中,类型 int 表示其后所列变量为整数,与之相对应的,float 表示所列变量为浮点数(即可以带有小数部分的数)。int 与 float 类型的取值范围取决于具体的机器。对于 int 类型,通常为 16 位,其取值范围在-32768~32767 之间,也有用 32 位表示的 int 类型。float 类型通常是 32 位,它至少有 6 位有效数字,取值范围一般在 10-38~1038 之间。

除 int 与 float 类型之外,C 语高还提供了其它一些基本数据类型,例如:

  • char:字符,一个字节
  • short:短整型
  • long:长整型
  • double:双精度浮点型

这些数据类型对象的大小也取决于具体的机器。另外,还存在这些基本数据类型的数组、结构、联合,指向这些类型的指针以及返回这些类型值的函教。

在上面的温度转换程序中,最开始执行的计算是下列 4 个赋值语句:

lower = 0;
upper = 300;
step = 20;
fahr = lower;

它们为变量设置初值。各条语句均以分号结束。

温度转换表中的各行计算方式相同,因此可以用循环语句重复输出各行。这是 while 循环语句的用途:

while (fahr <= upper) {
 ...
}

while循环语句的执行方式是这样的:首先测试圆括号中的条件;如果条件为真(fahr<=upper),则执行循环体(括在花括号中的 3 条语句);然后再重新测试圆括号中的条件,如果为真,则再次执行循环体;当圆括号中的条件测试结果为假(fahr>upper)时,循环结束,并继续执行跟在 while 循环语句之后的下一条语句。在本程序中,循环语句后没有其它语句,因此整个程序的执行终止。

while 语句的循环体可以是用花括号括起来的一条或多条语句(如上面的温度转换程序),也可以是不用花括号包括的单条语句,例如:

while (i < j)
 i = 2 * i;

在这两种情况下,我们总是把由 while 控制的语句缩进一个制表位,这样就可以很容易地看出循环语句中包含哪些语句。这种缩进方式突出了程序的逻辑结构。尽管 C 编译器并不关心程序的外观形式,但正确的缩进以及保留适当空格的程序设计风格对程序的易读性非常重要。我们建议每行只书写一条语句,并在运算符两边各加上一个空格字符,这样可以使得运算的结合关系更清楚明了。相比而言,花括号的位置就不那么重要了。我们从比较流行的一些风格中选择了一种,读者可以选择适合自己的一种风格,并养成一直使用这种风格的好习惯。

在该程序中,绝大部分工作都是在循环体中完成的。循环体中的赋值语句:

printf(" %d\t%d\n", fahr, celsius);

用于打印两个整数 fahr 与 celsius 的值,并在两者之间留一个制表符的空间(\t)。

printf 函数的第一个参数中的各个%分别对应于第二个、第三个、……参数,它们在数目和类型上都必须匹配,否则将出现错误的结果。

顺便指出,printf 函数并不是 C 语言本身的一部分,C 语言本身并没有定义输入/输出功能。printf 仅仅是标准库函数中一个有用的函数而已,这些标准序函数在 C 语言程序中通常都可以使用。但是,ANSI 标准定义了 printf 函数的行为,因此,对每个符合该标准的编译器和库来说,该函数的属性都是相同的。

上述的温度转换程序存在两个问题。比较简单的问题是,由于输出的数不是右对齐的,所以输出的结果不是很美观。这个问题比较容易解决:如果在 printf 语句的第一个参数的%d 中指明打印宽度,则打印的数字会在打印区域内右对齐。例如,可以用语句

printf(" %3d %6d\n", fahr, celsius);

打印 fahr 与 celsius 的值,这样,fahr 的值占 3 个数字宽,celsius 的值占 6 个数字宽,输出的结果如下所示:

 0 -17
 20  -6
 40  4
 60  15
 80  26
100  37
...

另一个较为严重的问题是,由于我们使用的是整型算术运算,因此经计算得到的摄氏温度值不太精确,例如,与 0℉对应的精确的摄氏温度应该为-17.8℃,而不是-17℃。为了得到更精确的结果,应该用浮点算术运算代替上面的整型算术运算。这就需要对程序做适当修改。下面是该程序的又一种版本

#include <stdio.h>
/* print Fahrenheit-Celsius table
for fahr = 0, 20, ..., 300; floating-point version */
main()
{
 float fahr, celsius;
 float lower, upper, step;
 lower = 0;
 upper = 300;
 step = 20;
 /* lower limit of temperatuire scale */
 /* upper limit */
 /* step size */
 fahr = lower;
 while (fahr <= upper) {
 celsius = (5.0/9.0) * (fahr-32.0);
 printf("%3.0f %6.1f\n", fahr, celsius);
 fahr = fahr + step;
 }
}

这个程序与前一个程序基本相同,不同的是,它把 fahr 与 celsius 声明为 float 类型,转换公式的表述方式也更自然一些。在前一个程序中,之所以不能使用 5 / 9 的形式,是因为按整型除法的计算规则,它们相除并舍位后得到的结果为 0。但是,常数中的小数点表明该常数是一个浮点数,因此,5.0 / 9.0 是两个浮点数相除,结果将不被舍位。

如果某个算术运算符的所有操作数均为整型,则执行整型运算。但是,如果某个算术运算符有一个浮点型操作数和一个整型操作数,则在开始运算之前整型操作数将会被转换为浮点型。例如,在表达式 fahr – 32 中,32 在运算过程中将被自动转换为浮点数再参与运算。不过,即使浮点常量取的是整型值,在书写时最好还是为它加上一个显式的小数点,这样可以强调其浮点性质,便于阅读。
在这里需要注意,赋值语句 fahr = lower; 与条件测试语句 while (fahr <= upper) 也都是按照这种方式执行的,即在运算之前先把 int 类型的操作数转换为 float 类型的操作数。

printf 中的转换说明%3.0f 表明待打印的浮点数(即 fahr)至少占 3 个字符宽,且不带小数点和小数部分;%6.1f 表明另一个待打印的数(celsius)至少占 6 个字符宽,且小数点后面有 1 位数字。其输出如下所示:

 0 -17.8
20  -6.7
40  4.4
...

格式说明可以省略宽度与精度,例如,%6f 表示待打印的浮点数至少有 6 个字符宽;%.2f指定待打印的浮点数的小数点后有两位小数,但宽度没有限制;%f 则仅仅要求按照浮点数打印该数。

  • %d, 按照十进制整型数打印
  • %6d, 按照十进制整型数打印,至少 6 个字符宽
  • %f, 按照浮点数打印
  • %6f, 按照浮点数打印,至少 6 个字符宽
  • %.2f, 按照浮点数打印,至少 6 个字符宽
  • %6.2f, 按照浮点数打印,至少 6 个字符宽,小数点后有两位小数

此外,printf 函数还支持下列格式说明:%o 表示八进制数;%x 表示十六进制数;%c表示字符;%s 表示字符串;%%表示百分号(%)本身。

(0)

相关推荐

  • C语言 常量,变量及数据详细介绍

    一.数据 图片文字等都是数据,在计算机中以0和1存储. (一)分类 数据分为静态数据和动态数据. ①. 静态数据:一些永久性的的数据,一般存储在硬盘中,只要硬盘没坏数据都是存在的.一般以文件的形式存储在硬盘上,电脑关机重启后依然存在. ②. 动态数据:程序运行过程中,动态产生的的临时数据,一般存储在内存中,内存的存储空间一般较小,计算机关闭后这些数据就会被清除.软件或者电脑关闭则这些临时数据会被清除. ③. 静态数据和动态数据可以转换. ④. 注意:为什么不把动态数据存放到硬盘?因为直接访问内存

  • C语言菜鸟基础教程之常量和变量

    先动手编写程序: #include <stdio.h> int main() { int a = 1; printf("a = %d\n", a); a = 2; printf("a = %d\n", a); return 0; } 运行结果: a = 1 a = 2 程序分析: int a = 1; 定义了一个整型变量a,把1赋值给a.注意,C语言中的等号表示赋值,作用是把一个常量赋值给一个变量,这样变量就获得了一个临时的固定值. 为什么说是临时的呢?

  • 详解C语言中的符号常量、变量与算术表达式

    C语言中的符号常量 在结束讨论温度转换程序前,我们再来看一下符号常量.在程序中使用 300.20 等类似的"幻数"并不是一个好习惯,它们几乎无法向以后阅读该程序的人提供什么信息,而且使程序的修改变得更加困难.处理这种幻数的一种方法是赋予它们有意义的名字.#define 指令可以把符号名(或称为符号常量)定义为一个特定的字符串: #define 名字 替换文本 在该定义之后,程序中出现的所有在 #define 中定义的名字(既没有用引号引起来,也不是其它名字的一部分)都将用相应的替换文本

  • 详解Go语言中的作用域和变量隐藏

    目录 前言 包隐藏 全局变量 类型强制 闭包 := 的情况 总结 前言 变量隐藏在 Go 中可能会令人困惑,让我们尝试弄清楚. package main import ( "fmt" "io/ioutil" "log" ) func main() { f, err := ioutil.TempFile("", "") if err != nil { log.Fatal(err) } defer f.Clos

  • 详解Go语言中关于包导入必学的 8 个知识点

    1. 单行导入与多行导入 在 Go 语言中,一个包可包含多个 .go 文件(这些文件必须得在同一级文件夹中),只要这些 .go 文件的头部都使用 package 关键字声明了同一个包. 导入包主要可分为两种方式: 单行导入 import "fmt" import "sync" 多行导入 import( "fmt" "sync" ) 如你所见,Go 语言中 导入的包,必须得用双引号包含,在这里吐槽一下. 2. 使用别名 在一些场

  • 详解R语言中的表达式、数学公式、特殊符号

      在R语言的绘图函数中,如果文本参数是合法的R语言表达式,那么这个表达式就被用Tex类似的规则进行文本格式化. y <- function(x) (exp(-(x^2)/2))/sqrt(2*pi) plot(y, -5, 5, main = expression(f(x) == frac(1,sqrt(2*pi))*e^(-frac(x^2,2))), lwd = 3, col = "blue") library(ggplot2) x <- seq(0, 2*pi, b

  • 详解Go语言中的数据类型及类型转换

    目录 1.基本数据类型 2.基础数据类型转换 3.基本数据类型转为字符串 4.strconv的使用 5.字符串转为基础类型 1.基本数据类型 数据类型有很多,先研究一下基础的,例如:布尔型.数字类型.字符串类型. 数字类型有uint8.uint16.uint32.uint64.int8.int16.int32.int64(uint和int区别在于uint为无符号整数,即只支持正数,不支持负数形式) 数字浮点型有fload32.float64.complex64.complex126(后面两个均为

  • 详解C语言中结构体的使用

    目录 结构体的声明 结构体成员的类型 结构体成员的访问 结构体的声明 结构体的定义:结构体是一些值的集合,这些值称为成员变量,结构体的每个成员可以是不同类型的变量. 举例: //定义结构体类型 struct tag//struct结构体关键字 tag结构体标签 struct tag结构体类型 { //成员变量 char name[20]; short age; char telphone[12]; char sex[5]; }s1,s2,s3;//s1,s2,s3是三个全局结构体变量 int m

  • 详解C语言中二级指针与链表的应用

    目录 前言 二级指针讲解 链表的应用 定义双链表的结构体 创建双链表 前言 这篇文章即将解决你看不懂或者不会写链表的基本操作的问题,对于初学者而言,有很多地方肯定是费解的.比如函数的参数列表的多样化,动态分配内存空间函数malloc等,其实这些知识和指针联系紧密,尤其是二级指针.那么开始好好的学习这篇文章吧! 二级指针讲解 简述:其实就是一个指针指向另一个指针的地址. 我们都知道指针指向地址,但是指针自身也是一个变量,当然也可以被二级指针所指向. 语法:形如 int x = 10; int *q

  • 详解在JavaScript中如何判断变量类型

    JavaScript是一个动态类型语言,在运行时获取变量类型是常用操作,由于JavaScript设计的问题,看似简单的问题,在JavaScript中可能并不简单,比如在社区中流传的下图,仔细看一下这些坑,即便是JavaScript老司机也经常翻车. 上图中typeof NaN会返回number,这可能和你想的不一样,在JavaScript准确的获取变量类型,并不简单,正因为如此,这个问题经常被用来考察面试者,由于程序=数据+算法,而基本数据是数据的基础,所以面试中考察类型也是合理的. 如果面试中

  • 详解C语言中动态内存管理及柔性数组的使用

    目录 一.malloc 二.free 三.calloc 四.realloc 1.realloc在扩容时的情况 2.realloc也能实现malloc功能 五.使用动态内存的常见错误 1.free空指针 2.对动态开辟的空间越界访问 3.对非动态开辟内容free 4.只free动态开辟空间的一部分 5.对同一块内存多次free 6.动态内存空间忘记释放(内存泄漏) 六.柔性数组 1.柔性数组的概念 2.柔性数组的特点 3.柔性数组的使用场景 4.柔性数组的优点 一.malloc 这个函数向堆区申请

  • 一文详解C语言中文件相关函数的使用

    目录 一.文件和流 1.程序文件 2.数据文件 3.流 二.文件组成 三.文件的打开和关闭 1.文件的打开fopen 2.文件关闭fclose 四.文件的顺序读写 1.使用fputc和fgetc写入/读取单个字符 2.使用fputs和fgets写入/读取一串字符 3.使用fprintf和fscanf按照指定的格式写入/读取 4.使用fwrite和fread按照二进制的方式写入/读取 5.使用sprintf和sscanf将格式化数据和字符串互相转换(文件无关) 五.文件的随机读写 1.fseek(

随机推荐