PS:因为个人之前学习过C/C++,本章里一些自己已经掌握的东西就不再记录,例如变量类型、注释、引用。 本篇文章内容只包含自己还不太了解、或是已经遗忘的东西。因此等于是刷题时自用的小笔记,不建议看到的朋友拿来当原书的概述哈~
1、define定义语句
define除了可以定义常量外,其实可以定义任何语句或片段。其格式如下:
#define 标识符 任何语句或片段
可以这样用ADD(A,B)代替a+b的功能:
#define ADD(a,b) ((a)+(b))
注意:在使用define定义语句时,要对变量加好括号。因为宏定义是直接将对应的部分替换,才进行编译和运行。下面是不加括号导致错误发生的例子:
#include <stdio.h>
#define CAL(x) (x*2+1)
int main()
{
int a=1;
printf( "%d/n", CAL(a + 1));
return 0;
}
运行结果:4
而显然,4是错误的,正确结果是5。原因是CAL(a+1)被替换为a+1*2+1,而不是(a+1)*2+1。
2、常用math函数
C语言提供了很多实用的数学函数,如果要使用,需要在程序开头加上math.h头文件。
-
fabs(double x) 该函数用于对double型变量取绝对值。 -
floor(double x)和ceil(double x) 这两个函数分别用于double型变量的向下取整和向上取整,返回类型为double型。 -
pow(double r,double p) 该函数用于返回r的p次方。 -
sqrt(double x) 返回double型变量的算术平方根。 -
log(double x) 返回以自然对数为底的对数。 -
sin(double x)、cos(double x)、tan(double x) -
asin(double x)、acos(double x)、atan(double x) 分别返回反正弦值、反余弦值、反正切值。 -
round(double x) 用于将double型变量四舍五入。
3、sscanf与sprintf
使用sscanf将char数组str中的内容以"%d"的格式写到n中。
int n;
char str[100]="200";
sscanf(str,"%d",&n);
打印n,运行结果:
200
sscanf更复杂的应用:将str中的内容按"%d:%lf,%s"的格式写到int型变量n、double型变量db、char型数组str2中。
int n;
double db;
char str[100]="20:3.14,hello";
char str2[100];
sscanf(str,"%d:%lf,%s",&n,&db,str2);
打印d,db,str2,运行结果:
d=20, db=3.14, str2="hello"
sprintf按"%d:%lf,%s"的格式将int型变量n、double型变量db、char型数组str2写入字符数组str中。
int n=20;
double db=3.14;
char str2[100]="hello";
char str[100];
sprintf(str,"%d:%lf,%s",n,db,str2);
打印str,运行结果:
str2="20:3.14,hello"
4、cin与cout
当想读入一整行时,需要使用getline函数。
char str[100];
cin.getline(str,100);
如果是string容器:
string str;
getline(cin,str);
(PS:cout控制格式输出太复杂了。。。不想写了,直接用printf省事又安全)
事实上,对考试而言,并不推荐读者使用cin跟cout来进行输入和输出,因为它们在输入/输出大量数据的情况下表现得非常糟糕,有时候题目的数据还没有输入完毕就已经超时。因此还是推荐读者使用C语言的scanf与printf函数进行输入/输出,只有在十分必要的时候才使用cin与cout(例如第6章会介绍的string)。
5、浮点数的比较
极小数eps
由于计算机中采用有限位的二进制编码,因此浮点数在计算机中的存储并不总是精确的。
例如在经过大量计算后,一个浮点型的数 3.14在计算机中就可能存储成3.1400000000001,也有可能存储成3.1399999999999,这种情况下会对比较操作带来极大的干扰(因为C/C++中的“==”操作是完全相同才能判定为 true)。于是需要引入一个极小数 eps来对这种误差进行修正。
如果一个数a落在了[b-eps, b+eps]的区间中时,就应当判断为a-b成立。那么eps应当取多少呢?经验表明,eps取10^-8是一个合适的数字——对大多数的情况既不会漏判,也不会误判。因此可以将eps定义为常量1e-8。
const double eps = 1e-8;
圆周率π
因为cos(π)=-1,可知π=arccos(-1)。因此只需要把π写成常量 acos(-1.0) 即可。
const double Pi = acos(-1.0);
还需注意的几点
- 由于精度问题,在经过大量运算后,可能一个变量中存储的0是个很小的负数,这时如果对其开根号sqrt,就会因不在定义域内而出错。同样的问题还出现在asin(x)当x存放+1、acos(x)当x存放-1时。这种情况需要用eps使变量保证在定义域内。
- 在某些由编译环境产生的原因下,本应为0.00的变量在输出时会变成-0.00。这个问题是编译环境本身的 bug,只能把结果存放到字符串中,然后与-0.00进行比较,如果比对成功,则加上eps来修正为0.00。
6、memset函数
- memset函数用于给数组初始化。
- 需要在开头加上string.h头文件。
- 建议使用memset函数赋0或-1。原因:memset使用的是按字节赋值,即对每个字节赋同样的值,这样组成int型的4个字节就会被赋成相同的值。而由于0的二进制补码为全0,-1的二进制补码为全1,不容易弄错。
- 如果要对数组赋其他数字(例如1),那么请使用fll函数(但memset执行速度更快)
- 格式:memset(数组名,值,sizeof(数组名));
int a[5];
memset(a,0,sizeof(a));
7、输入结束
scanf函数的返回值为其成功读入的参数的个数。 这就是说,如果语句scanf("%d",&n)成功读入了一个整数 n,那么、scanf 的返回值就是1;如果语句scanf("%d%d" , &n, &m)成功读入了两个整数n、m,那么scanf的返回值就是2。
当题目没有说明有多少数据需要读入时,就可以利用scanf 的返回值是否为EOF来判断输入是否结束。于是就有了下面这种写法:
while(scanf("%d",&n)!=EOF{
...
}
另外,当在黑框里输入数据时,并不会触发EOF状态。因此如果想要在黑框里面手动触发EOF,可以按<Ctrl+Z>组合键,这时就会显示一个^Z,按< Enter>键就可以结束while了。
还需要指出,如果读入字符串,则有scanf("%s" , str)与 gets(str)两种方式可用,其对应的输入写法如下所示:
while(scanf("%s",str) != EOF{
...
}
while(get(str) != NULL{
...
}
|