C 语言答疑-02
By Von Brank | 2020/11/07 | Audience: 120L0213
目录
- 集成开发环境(IDE), 在线评测系统的(Online Judge)的推荐与使用
- 近期知识梳理
- 几道题
- 答疑
集成开发环境(IDE), 在线评测系统的(Online Judge)的推荐与使用
- IDE
- Dev-Cpp
- Visual Studio Code
- OJ
- www.luogu.com.cn
- leetcode-cn.com
- 算法可视化
- visualgo.net/zh
知识点梳理
数组
数组能够解决的基本问题
- 给出 个数,计算它们的平均数
#include <stdio.h>
int main()
{
int n, sum = 0, x;
scanf("%d", &n);
for(int i=1; i<=n; i++)
{
scanf("%d", &x);
sum += x;
}
printf("%lf", (double)sum / n);
return 0;
}
注意到不需要记录每一个输入的数
- 给出 个数(),输出所有大于平均数的数
#include <stdio.h>
const int maxn = 105;
int main()
{
int n, sum = 0;
int a[maxn]; //定义数组
double avg;
scanf("%d", &n);
for(int i=1; i<=n; i++)
{
scanf("%d", &a[i]); //读入数组
sum += a[i];
}
avg = (double)sum / n;
for(int i=1; i<=n; i++) //遍历数组
{
if(a[i] > avg) printf("%d ", a[i]); //输出数组元素
}
return 0;
}
要记录很多数字,需要使用数组存储
数组的定义
/*
* 一维数组
*/
<变量类型> <变量名称>[元素数量];
int numbers[100];
double numberss[100];
for(int i=1; i<=n; i++)
scanf("%d", numbers[i]);
/*
* 二维数组
*/
<变量类型> <变量名称>[第一维长度][第二维长度];
int numbers[100][100];
- 元素数量必须是整数
- C99 标准之前,定义数组时元素数量应是常量或用宏定义实现
- C99 标准之后,在函数定义数组时元素的数量可以是变量,但全局变量数组定义的元素数量仍只能使用宏定义
数组的集成初始化
一维数组
int a[1050] = {0, 1, 1, 2, 3, 5, 8, 13,}; int a[] = {0, 1, 1, 2, 3, 5, 8, 13,}; int a[1050] = { [1] = 1, 1, 2, 3, 5, 8, 13,};
二维数组
int a[105][105] = { {0, 1, 1, 2, 3, 5, 8, 13,}, {0, 1, 2, 4, 8, 16, 32, 64,}, }; int b[][4] = { {0, 1, 2, 3,}, {0, 2, 4, 6,}, };
二维数组第二维长度必须被定义
将变量作为数组定义的元素数量并作集成初始化的注意事项
以下定义方式在 C 语言中可以通过编译
#include <stdio.h> #define maxn 100 int main() { int a[maxn] = {0, 1, 2, 3,}; return 0; } ------------------------------ #include <stdio.h> int main() { int a[100] = {0, 1, 2, 3,}; return 0; } ------------------------------ #include <stdio.h> int main() { const int maxn = 100; int a[maxn] = {0, 1, 2, 3,}; return 0; } ------------------------------ #include <stdio.h> int main() { int maxn = 100; int a[maxn] = {0, 1, 2, 3,}; return 0; }
以下定义方式在 C 语言中无法通过编译,但是在 C++中可以
#include <stdio.h> const int maxn = 100; int main() { int a[100] = {0, 1, 2, 3,}; return 0; } ------------------------------- #include <stdio.h> int main() { const int maxn = 100; int a[100] = {0, 1, 2, 3,}; return 0; }
数组的赋值
错误示范
int a[1050] = {0, 1, 2, 3, 4, 5,}; int b[1050]; b = a;
正确示范
int a[1050] = {0, 1, 2, 3, 4, 5,}; int b[1050]; for(int i=0; i<=5; i++) b[i] = a[i];
数组的遍历
将数组从头至尾验视一次
一般使用
for
循环进行数组遍历一些遍历数组的例子
数组的遍历赋值
for(int i=1; i<=n; i++) b[i] = a[i];
数组的遍历初始化
for(int i=1; i<=n; i++) a[i] = 0;
遍历数组查找元素
for(int i=1; i<=n; i++) { if(key == a[i]) { ans = i; break; } }
输出比平均值大的数
for(int i=1; i<=n; i++) { if(a[i] > avg) { printf("%d ", a[i]); } }
输出数组中所有值
for(int i=1; i<=n; i++) { printf("%d ", a[i]); }
数组作为函数的参数
对于一维数组,不能在
[]
中给出数组的大小无法再利用
sizeof()
函数给出数组的大小往往需要再传入一个变量来描述数组大小
int search(int key, int a[], int len) { int ans = -1; for(int i=1; i<=len; i++) { if(a[i] == key) { ans = i; break; } } return ans; }
字符串简介
ASCII 码表
tool.ip138.com/ascii_code/
char
既是整数也是字符,取决于你怎么看它以下两个
printf
语句输出都是1
printf("%c\n", 49); printf("%d\n", 1);
以下两个
printf
语句输出都是49
printf("%c\n", '1'); printf("%d\n", 49);
以”
char
的方式”看待49
这个整数,它是字符1
以”
int
的方式”看待1
这个字符,他是整数49
'\n'
,'\t'
与空格
字符串的定义与初始化
char s[100];
char str[100] = "Hello World!!!";
字符串的输入与输出
%s
会将当前位置与下一个空格或回车之间的字符串赋值给字符串s
char s[100];
scanf("%s", s);
printf("%s", s);
- 上述写法中,
scanf
会将输入字符串的首位赋值给s[0]
- 每个字符串都以
'\0'
结尾
常用字符串函数
- 需要
#include <string.h>
getchar()
单字符输入,putchar()
单字符输出strlen()
计算字符串长度strcmp()
比较两个字符串是否相同strcpy()
将一个字符串赋值给另一个字符串strcat()
将一个字符串拷贝到另一个字符串的后面,连成一个长的字符串
数组其他注意事项
数组是一种容器,有如下特点:
- 一个数组中所有元素类型相同
- 数组一旦定义,不能改变大小
- 任意维度的数组在内存中的排列是连续的
数组中的每一个单元是数组类型的一个变量,使用数组时
[]
中的数字称为下标或索引,下标从开始计数一个拥有个元素的数组,下标的范围是,以大于等于的下标访问数组可能会造成数组越界导致程序崩溃,但是编译器在编译时并不会检查数组是否越界,因此务必保证程序只使用有效下标访问数组
创建数组时,应给予冗余的数组元素个数,如
#include <cstdio> const int maxn = 1050; //对于1e3的数据范围,给予额外50个元素 int main() { int n; int a[maxn]; scanf("%d", &n); for(int i=1; i<=n; i++) scanf("%d", &a[i]); return 0; }
因数组越界而扣分所造成的损失比计算机内部多几个int
所造成的内存消耗严重得多(
数组的应用
洛谷 P5737 【深基 7.例 3】闰年展示
输入 ,输出区间中闰年个数,并在下一行输出所有闰年年份数字,使用空格隔开。
不使用数组
#include <stdio.h> int main() { int x, y, cnt = 0; scanf("%d %d", &x, &y); for(int i=x; i<=y; i++) { if(i % 4 == 0 && !(i % 100 == 0 && i % 400 != 0)) { cnt++; } } printf("%d\n", cnt); for(int i=x; i<=y; i++) //需要做两遍计算 { if(i % 4 == 0 && !(i % 100 == 0 && i % 400 != 0)) { printf("%d ", i); } } return 0; }
使用数组
#include <stdio.h> int main() { int x, y, cnt = 0; int leapYear[1050]; scanf("%d %d", &x, &y); for(int i=x; i<=y; i++) { if(i % 4 == 0 && !(i % 100 == 0 && i % 400 != 0)) { leapYear[++cnt] = i; } } printf("%d\n", cnt); for(int i=1; i<=cnt; i++) { printf("%d", leapYear[i]); } return 0; }
洛谷 P1255 数楼梯
楼梯有阶,上楼可以一步上一阶,也可以一步上二阶。
编一个程序,计算共有多少种不同的走法。
不使用数组
#include <stdio.h> long long Fib(int n) { if(n == 0 || n == 1) return 1; return Fib(n-1) + Fib(n-2); } int main() { int n; scanf("%d", &n); printf("%d", Fib(n)); return 0; }
运行结果:
使用数组
#include <stdio.h> const int maxn = 10050; int main() { int n; long long a[maxn]; scanf("%d", &n); a[0] = 0; a[1] = 1; for(int i=2; i<=n; i++) a[i] = a[i-1] + a[i-2]; printf("%lld", a[n]); return 0; }
*运行结果:AC!!! :)
洛谷 P1464 Function
对于一个递归函数
- 如果就返回值
- 如果就返回
- 如果并且就返回
- 其它的情况就返回
这是个简单的递归函数,但实现起来可能会有些问题。当均为时,调用的次数将非常的多。你要想个办法才行.
对于一些特殊情况,比如 既满足条件又满足条件,这种时候我们就按最上面的条件来算,所以答案为
不使用数组
#include <stdio.h> int w(int a, int b, int c) { if(a <= 0 || b <= 0 || c <= 0) return 1; else if(a > 20 || b > 20 || c > 20) return w(20, 20, 20); else if(a < b && b < c) return w(a, b, c-1) + w(a, b -1, c - 1) - w(a, b, c - 1); else return w(a-1, b, c)+w(a-1, b-1, c)+w(a-1, b, c-1)-w(a-1, b-1, c-1); } int main() { int a, b, c; while(1) { scanf("%d %d %d", &a, &b, &c); if(a == -1 && b == -1 && c == -1) { break; } printf("w(%d, %d, %d) = %d\n", a, b, c, w(a, b, c)); } return 0; }
运行结果:
使用数组(记忆化)
#include <stdio.h> int vis[25][25][25]; void init() { for(int i=0; i<=20; i++) { for(int j=0; j<=20; j++) { for(int k=0; k<=20; k++) { vis[i][j][k] = -1; } } } } int w(int a, int b, int c) { if(a <= 0 || b <= 0 || c <= 0) a = b = c = 0; if(a > 20 || b > 20 || c > 20) a = b = c = 20; if(vis[a][b][c] != -1) return vis[a][b][c]; else if(a <= 0 || b <= 0 || c <= 0) return vis[0][0][0] = 1; else if(a < b && b < c) return vis[a][b][c] = w(a, b, c - 1) + w(a, b -1, c - 1) - w(a, b, c - 1); else return vis[a][b][c] = w(a - 1, b, c) + w(a - 1, b- 1, c) + w(a - 1, b, c - 1) - w(a - 1, b - 1, c - 1); } int main() { int a, b, c; while(1) { init(); //计算每组数据前要初始化数组 scanf("%d %d %d", &a, &b, &c); if(a == -1 && b == -1 && c == -1) { break; } printf("w(%d, %d, %d) = %d\n", a, b, c, w(a, b, c)); } return 0; }
运行结果:AC!!! :)
C 语言庶事
整数是怎么表达的——原码,反码,补码
- 没什么目的,只是为了方便计算
各数据类型的表达范围与自然溢出
int
类型有 32 位,表达范围为unsigned int
数据范围是
各数据类型的输入输出
数据类型 | |
---|---|
%d | int |
%u | unsigned int |
%lld | long long |
%llu | unsigned long long |
浮点数
令人头疼的精度问题
#include <stdio.h> int main() { if(1.1 + 0.1 == 1.2) printf("YES!!!"); else printf("NO!!!"); return 0; }
以上程序输出的是
NO
1.1 + 0.1
为什么不等于1.2
0.1转化为二进制的结果是一个循环小数: 0.00011001100110011001100110011001100110011001100110011010 double类型在二进制下的精度是53位有效数字,超出部分四舍五入 同理将1.1转化为二进制: 1.0001100110011001100110011001100110011001100110011010 二者相加 0.00011001100110011001100110011001100110011001100110011010 + 1.0001100110011001100110011001100110011001100110011010 --------------------------------------------------------------- 1.0011001100110011001100110011001100110011001100110100 将结果转化为十进制: 1.20000000000000018 将1.2转化为二进制: 1.0011001100110011001100110011001100110011001100110011 与1.1+0.1的二进制结果: 1.0011001100110011001100110011001100110011001100110100 相比较 二者不同
解决方案
#include <stdio.h>
const double eps = 1e-6;
double eps(double x)
{
return x < 0 ? -x : x;
}
int main()
{
if((abs(1.1 + 0.1 - 1.2) < eps)
printf("YES!!!");
else
printf("NO!!!");
return 0;
}
C 语言程序设计其他注意事项
- 不要把SSE当成IDE
- C 语言上机测试提交前一定要在本地编译通过并用样例数据测试
- 循环变量使用
int a, tmp;
for(a = 1, tmp = 0; a <= n; a++) //错误示范
{
...
...
}
int i;
int tmp = 0;
for(i = 1; i <= n; i++) //正确示范
{
...
...
}
遵循 C 语言标准代码规范
运算符两端空格
int a=b+1; //错误示范 int a = b + 1; //正确示范
当然,嫌for
循环内空格太多也可以不遵循本条采用缩进风格
if(n == 0) //错误示范 { a = b + 1; b ++; } if(n == 0) //正确示范 { a = b + 1; b ++; }
避免把多行语句写在同一行
int a = b + 1; b++; //错误示范 int a = b + 1; //正确示范 b++;
几道题
洛谷 P1177 【模板】快速排序
给出个数,将其从小到大排序.
对于的数据,
相亲数
输入一个数,输出范围内每一对相亲数
注:相亲数指两个正整数中,彼此的全部约数之和(本身除外)与另一方相等
洛谷 P3383 【模板】线性筛素数
给定一个范围 ,有 个询问,每次输出区间范围内第 小的素数。
对于的数据,,,保证输入合法
洛谷 P1706 全排列问题
输入一个数输出自然数到所有不重复的排列,即的全排列,要求所产生的任一数字序列中不允许出现重复的数字。
输出的所有排列按字典序排序
对于的数据,