7-变量与格式化输出
我们已经用 C 语言进行过一些输出,这一节,我们将核心讨论变量与格式化输出。
其实printf函数名由两个部分组成,第一个部分是表示“打印”,而第二部分就是这个——它表示了英文中的“格式(format)”的缩写,说明这个函数在打印信息的时候是可以带有格式的。
printf函数 printf--->打印(print) 格式(format)
而这里说的格式,就是我们在前面课程中学到过的printf函数的第一个参数。如果我们不嵌入任何的变量,那么这个格式就是纯粹的一个字符串;如果我们需要在字符串中嵌入一些变量,那么我们嵌入占位符后printf函数的第一个参数就会被视为我们所需要的“格式”。
在 C 语言中,我们常用的基础变量数据类型有:
数据类型 |
关键字 |
说明 |
整数型 |
int |
反映机器中整数的自然长度 |
字符型 |
char |
占1字节,可存一个字符 |
单精度浮点数型 |
float |
用于存储精度的实数 |
双精度浮点数型 |
double |
用于存储双精度的实数,平时更常用 |
除了基础变量数据类型,我们还可以在基本数据类型的前面加一些限定符,比方说short和long可以加在int、float、double前用于修饰对应的数据类型。比方说,被short修饰的整数型变量的存储位数会更小,相应的可存整数范围也就比较小,而如果被long修饰则会用更多的存储位数,能存的整数范围也不小于int。
我们还可以用unsigned来修饰int,说明数据类型是无符号的整数。当你写下unsigned int的时候,你其实在告诉计算机“这个整数不需要留出一位来存储符号,所有的位数都可以拿来存数字”。相应的,这种类型的变量就可以用于表示更大的正整数。
你可能注意到了,老师一直在强调不同数据的类型。因为,不同类型的变量只能和符合它类型的值相对应。如果我们试图将一个10000000000 存到一个int类型的变量d中的时候,由于存储的位数不够,这个变量d就无法正确地存储这个数或将它用于计算了(如果数据比较大,其实可以学习用一下long long int的数据类型,具体做法可以自己在互联网上找一找喔)。
接下来,让我们来回顾一下我们之前用到过的格式化输出。
printf("%c is %dst letter", alpha, number);
在这里,第 1 个占位符%c用于给char类型的第1 个变量alpha做占位符,而第2 个占位符%d用于给int类型的第2 个变量number做占位符。
针对其它数据类型,占位符应该是什么呢?
数据类型及显示 |
占位符 |
占位符来源单词首字母 |
示例数据 |
有符号整数型 |
%d或%i |
digit和integer |
-392 |
字符型 |
%c |
character |
A |
无符号整数型 |
%u |
unsigned |
392 |
浮点型 |
%f或%F |
float |
39.2 |
双精度浮点数 |
%lf |
double |
39.200000 |
科学计数法浮点数 |
%e或%E |
exponent |
3.9256e+2 |
实数(不显示无意义0) |
%g或%G |
|
39.2和1.2e+02 |
内存地址 |
%p |
pointer |
b8000000 |
字符串 |
%s |
string |
sample |
8-1在程序中学会换行
不知道你有没有在读前面的内容和写代码的过程中发现一些规律?我们是不是经常在程序的时候按回车键换行?
我们为什么在程序中换行呢?哪些地方需要换行呢?哪些地方被建议换行呢?
在回答列出的这些问题之前,先让我们来看一个同学的代码:
1#include <stdio.h>
2 int main() {
3 int number;
4 char alpha;
5 // 在上面声明了两个变量,请在下面给他们赋值
6 number = 1;alpha = 'A';printf("%c is %dst letter", alpha, number);
7
8 return 0;
9 }
是不是觉得这个代码很眼熟?没错,这就是你之前也学习过的课程中的代码。
可是,你有没有觉得这个代码写的很难看呢?这个同学把两个变量的赋值和格式化输出全都写在了一行里面了!这么长的内容读起来实在很难受。
那么,让老师带你一起看看,在写程序的时候,有哪些地方必须要换行呢?
·定义函数之前
·除了小括号内的分号后
·左大括号后
·右大括号前(除列表形式进行初始化的时候)
·右大括号后(除 struct,enum 的定义体后,else 前、do-while 的 while 前、初始化列表)
·单行超过 80 字符的最后一个 token 前
·不换行导致不符合语法(宏定义最后)
如果在你弄懂所有上面描述的情况后,遇到上述情况时你都能正确地进行换行,你会获得一个看起来舒服很多的程序。
比如对于之前那份代码,如果我们按照上一个要求进行一下修改,我们将得到:
1 #include <stdio.h>
2 int main() {
3 int number;
4 char alpha;
5 // 在上面声明了两个变量,请在下面给他们赋值
6 number = 1;
7 alpha = 'A';
8 printf("%c is %dst letter", alpha, number);
9
10 return 0;
11 }
除了必须换行的地方,还有一些地方,你可以选择性的换行:
·单行的代码块之前
·左大括号前
·列表形式进行初始化时的右大括号前
·部分右大括号后(仅 struct,enum 的定义后,else 前、do-while 的 while 前)
·switch 的 case 和 default 的冒号后
·enum 定义的逗号后
·两块逻辑不相关的代码段之间
也有一些地方,老师会建议你不要换行,比方说:
·连续的两个空行如果出现可以减少一个换行
·正常的两个关键词间未被建议换行的地方
对于我们说的可以换行和不建议换行的情况,我们可以在变量赋值后加上一个空行,然后把return 0;前的两行空行变成一个空行。
1#include <stdio.h>
2int main() {
3 int number;
4 char alpha;
5 // 在上面声明了两个变量,请在下面给他们赋值
6 number = 1;
7 alpha = 'A';
8
9 printf("%c is %dst letter", alpha, number);
10
11 return 0;
12}
这样我们的代码就会更清晰一些,也看得更舒服一些啦。这里说的一些需要换行的场景你可能还没有学到,在之后遇到时,如果不确定要不要换行,记得回来看一看。
1-重新认识基本运算
在 C 语言中,也有我们很熟悉的四则运算的 运算符(operator):
·加法:使用加号+作为运算符,例如a + b。
·减法:使用减号-作为运算符,例如a - b。
·乘法:区别于我们平时手写的乘号“×”,C 语言中我们使用*作为乘号,例如a * b表示a与b相乘。
·除法:区别于我们小时候写的除号“÷”和写分式时的“—”,在 C 语言中我们使用/作为除号,例如a / b表示a被b除。
除了我们熟悉的加、减、乘、除,在我们的 C 语言中,我们还有一种运算—— 求余(complementation) 运算(也称 模运算(modulo operation) )。顾名思义,求余运算,就是求两个整数相除以后的余数。
如果我们要求 21 除以 4 的余数,我们会得到 1。
在 C 语言中,我们用%作为求余运算的运算符,可以写成例如a % b的形式。对于上面的例子,我们可以直接写21 % 4——这个运算式的值将会是 1。
不知道你发现了没有,我们在这里提到的所有的算术运算符,无论是加、减、乘、除还是求余,在运算符的前、后各有一个被用于运算过程的值,我们称这种运算符为双目运算符(binary operator)。
我们之前学习赋值的时候学到的=赋值运算符,也是一个双目运算符。
我们可以将赋值运算符和这一块的算术运算符结合,得到一些有意思的表达:
·number = 1 + king;表示数值 1 加上变量king内的值作为一个运算式,这个运算式的值被赋值给变量number。
·number = king * queen;表示将变量king内的值乘以变量queen内的作为一个运算式,这个运算式的值被赋值给变量number。
·number = number + 1;表示将变量number内的值加上数值 1 作为一个运算式,这个运算式的值被赋值给number。
通过变量、数值和运算符构成的结果是可以连写的,我们写a = 1 + 2 + 3这样的形式,可以被理解成1 + 2的运算结果通过加法运算符再次和 3 进行了加法,整体被看作一个运算式,这个运算式的值被赋值给了a。但是,如果我们写b = 1 + 2 * 3,则表示 1 通过加法运算符和2 * 3的运算结果进行了加法,整体被看作一个运算式,这个运算式的值被赋值给了b。
为什么会有这样的差异呢?学过数学的你一定不会觉得很奇怪,如数学中的运算一样,C 语言的基本运算符也是有优先级的,乘法、除法、求余(模运算)的优先级比加法、减法要更高。
如果我们想改变这种优先级,C 语言中也设计了一种和数学中教的一模一样的工具——小括号。如果你希望1 + 2的运算结果通过乘法运算符和3 进行乘法,并把结果赋值给b,那么我们需要把运算过程写成b = (1 + 2) * 3。
2-做一下简单的运算
#include <stdio.h>
int main() {
int a;
a = 12;
printf("%d",a+19);
return 0;
}
3-a的n次方
在这里,我们给出来了一小段初始化代码,里面声明了两个变量a和n,并且给他们进行了赋值。
我们今天的任务是求a的n次方。我们会用到一个叫pow的函数(这里的函数名称来自于英文中次方的单词“power”)。当我说“用”的时候,你是不是很开心?因为明显它是一个库里已经实现的函数,你可以拿来“用”!所以呢,你需要在main函数开始之前引入一下包含了pow函数的数学库,它对应的头文件为math.h。
#include <stdio.h>
#include <math.h>
int main() {
double a;
double n;
a = 2.0;
n = 4.0;
// 在下面打出来 a 的 n 次方的结果
return 0;
}
引入了数学库以后,我们就可以使用pow函数了。
pow函数接受两个参数,第一个参数需要传入的是我们的底数a,第二个参数传入的是我们的幂数n。
请注意,我们通过pow得到的结果将是一个浮点数,所以我们讲pow(a,n)的结果通过printf输出出来的时候,占位符要用%lf(注意是字母:l和f,另外别多在第一个参数中写其他东西哦),你自己写一下?
printf("%lf",pow(a,n));
你可以试试改改a和n的值,然后再点击运行,看看结果会不会有什么不同呢?
红色是自己加入到代码!!!
#include <stdio.h>
#include <math.h>
int main() {
double a;
double n;
a = 2.0;
n = 4.0;
// 在下面打出来 a 的 n 次方的结果
printf("%lf",pow(a,n));
return 0;
}
结果:16.0
4-数学函数
在 C 语言的数学(math)函数库中,有很多不同的数学函数,我们会在这里对其中一部分进行介绍。
之前已经我们已经学习了幂函数 pow,今天让我们一起来再学习一下绝对值函数、三角函数、对数函数、取整函数、平方根函数这些数学函数在 C 语言中的使用。
绝对值函数(absolute value function)
在 C 语言中有两个常用的绝对值函数,分别是abs函数和fabs函数(这里的函数名称来自于英文中单词 “absolute”)。
其中abs函数传入的参数需要是一个整数,返回的结果则会是这个传入的整数求完绝对值以后的结果。例如,abs(-4)的返回值会是 4。
而fabs函数,就明显是一个起名很友好的函数,其中f表示了浮点数。所以呢,它很自然的就是一个类似于abs函数,但是传入的参数是浮点数,返回结果是浮点数求完绝对值以后结果的函数。例如,fabs(-3.14)的返回值会是3.14,fabs(2.7)的返回值会是 2.7。
不同于其他大多数数学函数,abs使用前需要引入stdlib.h,引入后abs就可以使用了。
三角函数(trigonometric function)
在 C 语言的数学库中,包含了我们常用的三角函数:正弦sin、余弦cos、正切tan、反正弦asin、反余弦acos、反正切atan。它们都接受一个(双精度)浮点数值作为传入的参数,返回的则是对应的数学定义上的三角函数被应用在传入的浮点数值后的结果,类型也是(双精度)浮点数。
例如,tan(3.1415926535/4)的返回值是 1.000000,acos(0.32696)的返回值是1.237711。
对数函数(logarithmic function)
对于以 e 为底的对数函数lnx,C 语言的数学库中提供了名为log的函数;对于以10 为底的对数函数,数学库中则提供了一个名为log10的函数。
与三角函数类似,它们都接受一个(双精度)浮点数值作为传入的参数,返回的也都是对应的数学定义上的相应对数函数被应用在传入的浮点数值后的结果,类型也是(双精度)浮点数。
例如,log(2.71828)的返回值是 0.999999,而log10(100)的返回值是 2.000000。
取整函数
在数学库中还有 四舍五入函数(rounding function)、上取整函数(或天花板函数,ceiling function)和 下取整函数(或地板函数,floor function)。它们接受一个(双精度)浮点数值作为传入的参数,返回的也都是对应的数学定义上的相应上、下取整后的浮点数结果。例如,floor(2.7)返回的是小于或等于2.7 的整数,也就是 2.000000,ceil(-3.2)返回的是大于或等于−3.2 的整数,也就是−3.000000。
平方根函数
另外,数学库中还有一个 平方根函数(square root function) 。它接受一个(双精度)浮点数值作为传入的参数,返回的也都是对应的数学定义上的浮点数结果。例如,sqrt(42.25)的结果会是6.500000。
接下来的课程中,我们可就要试着使用这些数学函数了喔!
5-让C语言编程计算器
接下来让我一起把y的结果值输出出来。你应该知道怎么做吧?在这老师和你约定,用”%g”(实数格式占位符)作为格式化输出的第一个参数值。
#include <stdio.h>
#include <math.h>
int main() {
double y;
y = sqrt((1-cos(0.5))/2);
printf("%g",y);
return 0;
}
输出结果:0.247404
计算球的体积
#include <stdio.h>
#include <math.h>
#define PI 3.14
int main() {
double radius;
radius = 12.0;
printf("%g",4.0/3*PI*pow(radius,3));
return 0;
}
输出结果:7234.56
6-在程序中添加注释
在前面的课程中,我们看到一些给出的代码中一些//以开头的内容,这些以开头的内容是一种用于解释说明的信息,我们称之为 注释(comment) 。
在代码比较长、或者程序设计的逻辑比较复杂的时候,我们就需要为程序写一些注释了。在 C 语言中,一共有两种注释的方式——第一种是我们已经看到的在某一行中插入的形式,这行中,后的所有内容将在程序编译时被忽略,成为仅被程序员所关注的内容。我们将这种形式的注释称为 行注释(line comment) 。
在 C 语言中,还有另一种注释——以/*作为开头,以*/作为结尾。在一对与之间的所有内容都会被作为注释的内容,只让程序员作为参考,而在程序编译时不被关注。我们将这种形式的注释称为 块注释(block comment) 。
行注释和块注释的目的其实都是一样的,老师在课程代码中写一些注释,可以帮助你了解如何更好的学会程序。你写一些注释可以让自己在之后回顾时不至忘记自己的想法,也可以让和你一起写程序的其它程序员更容易的看懂你的程序。
你可能会好奇:既然有两种不同的注释,什么时候用块注释,什么时候用行注释呢?
一般来说,对于以下两种情况下,我们会使用块注释:
·需要把一大段代码暂时性注释掉的时候
·使用一些会读取代码中块注释来生成文档的工具的时候
除了以上的两种情况,我们都建议大家使用行注释,哪怕是连续的几行文字,我们都依然建议你分成多个连续行注释来进行说明,而不是使用一个跨多行的块注释。
你是不是有些怀疑老师的说法?来让我带你来看一个多行注释写法可能造成的问题。
1 int main() {
2 printf("Hello") ;/* 说你好 */
3 printf("World");
4 return 0;
5 }
对于上面给出的情况,如果我们需要暂时性注释掉main函数内的前两行代码,我们将得到一个这样的代码片段:
1 /*
2 printf("Hello") /* 说你好 */
3 printf("World");
4 */
这种情况下,第一个printf开始前的/*和“说你好”后的*/将会被匹配。而编译器将无法正常的对之后的一个*/进行理解。这种问题是不是我们不希望出现的呢?如果我们平时都用单行注释,是不是就不会有这样的问题了呢?
我是第一次写博客,如果有什么缺失之处,还希望广大朋友们,多多指正。