1. 关于scanf
1.1 读入数字
scanf 的返回值表示成功输入的变量个数,当输入结束时,scanf将无法再次读取数据,返回0
# include <stdio.h>
# include <math.h>
# include <time.h>
# define M 1000000;
// compute the max, min, average value of a given list
int main(){
int x, min = M, max = -M;
int n = 0;
int sum = 0;
printf("%d", min);
// scanf 读取结束后,则完成最后一次while循环。
// windows的退出方法:回车-ctrl+z-回车
while(scanf("%d", &x) == 1){
sum += x;
n += 1;
if (min > x){
min = x;
}
if (max < x){
max = x;
}
}
printf("the max number in the list : %d\n", max);
printf("the min number in the list : %d\n", min);
printf("the average number in the list : %f\n", sum*1.0/n);
return 0;
}
1.2 读入字符串
scanf(“%s”, s)
- 不需要&
- 读入的结果:一个不含空格、TAB、回车符的字符串,存入一个字符数组s
1.3 输出数字的小技巧:printf函数中,使用变量指定小数位数
printf("%.*f", str_len, number);// str_len表示小数位数,number为浮点数变量
printf函数中,输入确定位数的浮点数
printf("%5d", n); // (不超过五位时,)按照五位来打印,不足五位则补空格
printf("%05d", n); // (不超过五位时,)按照五位来打印,不足五位则补零
printf("%11.3f", f);// 打印数字占11位,保留小数点后3位,不足则补空格
printf("%011.3f", f);// 打印数字占11位,保留小数点后3位,不足则补零
2. 琐碎规范
2.1 一个在分母位置上平方溢出的解决方法
待解决问题:输入多组n, m,求1/n2+1/(n+1)2+ … +1/m2的结果。当输入n,m为0时,程序终止
输入范例1: 2 4
输入范例2: 65536 655360
范例2中,在分母部分,若采用1/(n*n),存放分母的int将会溢出;转而采取1/n/n的方法则可以使用浮点数避开溢出的可能
# include <stdio.h>
int main(){
int n, m, kase = 0;
double sum = 0.0;
while(scanf("%d %d", &n, &m) ){
// time to end
if (n==0 && m==0) break;
// exchange the value if n is larger
if (n>m){
int t = m;
m = n;
n = t;
}
while(n <= m){
// sum += 1.0/(n*n);
sum += 1.0/n/n; // 防止溢出
n += 1;
}
kase += 1;
printf("Case %d: %.5f\n", kase, sum);
}
}
2.2 floor() 关于浮点数的精度问题和四舍五入问题
在写函数的过程中,由于浮点数的计算精度问题,类似将1.0表示为1.00001或0.9999998,单纯的数值运算上,这种误差的问题不大。但涉及到取整数时,如使用 floor() 函数,就可能会将floor(1)的结果判为0。
常见的一个解决方法是写为 floor(a+0.5) 的格式。
初看是将分段函数的跃迁点从整数点移动到刻度为0.5的位置,有了类似四舍五入的功能,如[0.5, 1.5) 范围的浮点数将被floor() 判为1。
实际上,这是一个修正方法,并没有解决上述提出的浮点数在末位上的微小误差,而是将该误差由影响较大的1.0前后转移到了无甚影响的1.5附近,毕竟浮点数误差再大也不能由0.5大,对吧?
2.3 慎用全局变量
在函数内部定义的变量称为局部变量,反之为全局变量
全局变量定义、使用方便,但其并不会虽函数的结束而释放或重置。
这意味着,若有两处依次调用同一全局变量并修改其值,即便是相同的语句,其结果也可能不同
# include <stdio.h>
int t = 0;
int g(){ return ++t; }
int main(){
int a, b;
a = g();
b = g();
printf("%d %d", a, b);
return 0;
}
输出结果为
1 2
3 使用文件完成输入输出
3.1 使用文件输入输出——重定向(freopen)
# define LOCAL
# include <stdio.h>
# include <math.h>
# include <time.h>
# define M 1000000
// compute the max, min, average value of a given list
int main(){
# ifdef LOCAL // 若定义了LOCAL变量
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
# endif //编译结束
int x, min = M, max = -M;
int n = 0;
int sum = 0;
// scanf 读取结束后,则完成最后一次while循环。
// windows的退出方法:回车-ctrl+z-回车
while(scanf("%d", &x) == 1){
sum += x;
n += 1;
if (min > x){
min = x;
}
if (max < x){
max = x;
}
}
printf("the max number in the list : %d\n", max);
printf("the min number in the list : %d\n", min);
printf("the average number in the list : %f\n", sum*1.0/n);
return 0;
}
3.2 使用文件输入输出——fopen
# include <stdio.h>
# define M 1000000
// compute the max, min, average value of a given list
int main(){
FILE *fin, *fout;
// 声明变量
fin = fopen("data.in", "rb");
fout= fopen("data.out", "wb");
int x, min = M, max = -M;
int n = 0;
int sum = 0;
// fcanf三个参数:从哪读, 什么类型,赋给哪个参数
while(fscanf(fin, "%d", &x) == 1){
sum += x;
n += 1;
if (min > x){
min = x;
}
if (max < x){
max = x;
}
}
// 同样说明输出的位置
fprintf(fout, "the max number in the list : %d\n", max);
fprintf(fout, "the min number in the list : %d\n", min);
fprintf(fout, "the average number in the list : %f\n", sum*1.0/n);
// 关闭文件
fclose(fin);
fclose(fout);
return 0;
}
在fopen条件下,灵活性较大,可以反复打开并读写文件
当我们想要再次修改为标准输入输出,如下修改fin,fout,fclose即可
# include <stdio.h>
# define M 1000000
// compute the max, min, average value of a given list
int main(){
FILE *fin, *fout;
// 声明变量,直接定位到标准输入输出
// fin = fopen("data.in", "rb");
// fout= fopen("data.out", "wb");
fin = stdin;
fout = stdout;
int x, min = M, max = -M;
int n = 0;
int sum = 0;
// fcanf三个参数:从哪读, 什么类型,赋给哪个参数
while(fscanf(fin, "%d", &x) == 1){
sum += x;
n += 1;
if (min > x){
min = x;
}
if (max < x){
max = x;
}
}
// 同样说明输出的位置
fprintf(fout, "the max number in the list : %d\n", max);
fprintf(fout, "the min number in the list : %d\n", min);
fprintf(fout, "the average number in the list : %f\n", sum*1.0/n);
// 无需关闭文件
// fclose(fin);
// fclose(fout);
return 0;
}
4. 数组和字符串
4.1 关于较大数组的定义位置
一般只有当定义在main函数外面时,数组才能开得很大
4.2 复制数组中部分元素到另一个数组memcpy && 数组归零memset
# include <stdio.h>
# include <string.h>
int main(){
double a[10];
double b[10];
// 将数组 a 归零
memset(a, 0, sizeof(a));
for (int i =0; i<10; ++i){
a[i] = i;
}
for (int i =0; i<10; ++i){
b[i] = i + 10;
}
// // 复制所有 a 中的元素到 b
// memcpy(b, a, sizeof(a));
// 复制 3个 a中的元素到 b
memcpy(b, a, sizeof(double)*3);
for (int i=0; i<10; ++i){
printf("%f ", b[i]);
}
return 0;
}
关于memset,其对数组做初始化的取值,仅限于0和-1。
这是由于,memset是在内存上逐字节进行的赋值,4字节的int经过memset赋值为1后,取到的二进制为00000001 00000001 00000001 00000001,转化为十进制并非为1,而这个值在long long 等其他类型的数据上就又有不同了。能够初始化为0和-1纯属巧合
4.3 strlen, sprintf, strchr, strcpy, strcmp, strcat
- 字符串(字符数组)s标记的长度可能有很长,其中有些实际存有数据,而另一些位置的内容是不确定的,而函数strlen()可以获取字符串s的实际长度。
- 类似于printf在屏幕上打印、fprintf在文件上打印,sprintf可以将内容输出到字符串
- strchr(s, tmp[i])可以在一个字符串s中查找单个字符tmp[i]。若未找到,则返回值为NULL;若找到该字符,则返回该字符及之后的字符串
- char *strcpy(char *dest, const char *src):将src所指向的字符串复制到dest
- int strcmp(const char *str1, const char *str2):将str1所指向的字符串与str2所指向的字符串进行比较——大于0则str1大于str2(不一定是1);小于0则小于;等于0则等于
- char *strcat(char *dest, const char *src):将src所指向的字符串追加到dest所指向的字符串的末尾,返回值为一个指向最终目标字符串dest的指针
4.4 getchar()逐个读入字符
getchar()可以从标准输入读取到一个字符时,将返回一个int类型。
这是因为,当文件结束时,将返回一个特殊标记 EOF ,因为EOF并非一个char,如果返回一个char,将无法吧特殊的EOF和普通字符区分开。
其实,getchar()等价于将 fgetc(fin) 中的fin替换为stdin
# include <stdio.h>
// replace the ' " ' with ' `` ' and ' !! ' in turn.
int main(){
int c, q = 1;// getchar的返回值为int类型,故用int c记录
while ((c = getchar()) != EOF ){
if (c == '"'){
// 因为双引号的出现,必然是前后相邻两个成对出现,这与括号的使用过程中可层层嵌套的用法不同。
// 所以可以通过改变布尔值来交替输出两种符号
printf("%s", q ? "``" : "!!");
q = !q;
}
else printf("%c", c);
}
return 0;
}
4.5 isalpha、 isdigit、isprint
isalpha() : 判断字符是否为字母
isdigit() :判断字符是否为数字
isprint():检查所传的字符是否是可打印的,可打印的比如’k’、空格等,返回true;不可打印的比如\t,返回false
toupper()
tolower()
上述函数都在ctype.h中声明