C语言答疑-02
C 语言答疑-02
By Von Brank | 2020/11/07 | Audience: 120L0213
目录
- 集成开发环境(IDE), 在线评测系统的(Online Judge)的推荐与使用
- 近期知识梳理
- 几道题
- 答疑
集成开发环境(IDE), 在线评测系统的(Online Judge)的推荐与使用
- IDE
- Dev-Cpp
- Visual Studio Code
- OJ
- 算法可视化
知识点梳理
数组
数组能够解决的基本问题
- 给出 个数,计算它们的平均数
1 |
|
注意到不需要记录每一个输入的数
- 给出 个数(),输出所有大于平均数的数
1 |
|
要记录很多数字,需要使用数组存储
数组的定义
1 | /* |
- 元素数量必须是整数
- C99 标准之前,定义数组时元素数量应是常量或用宏定义实现
- C99 标准之后,在函数定义数组时元素的数量可以是变量,但全局变量数组定义的元素数量仍只能使用宏定义
数组的集成初始化
-
一维数组
1
2
3
4
5int 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,}; -
二维数组
1
2
3
4
5
6
7
8
9
10int 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 语言中可以通过编译
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
int main()
{
int a[maxn] = {0, 1, 2, 3,};
return 0;
}
------------------------------
int main()
{
int a[100] = {0, 1, 2, 3,};
return 0;
}
------------------------------
int main()
{
const int maxn = 100;
int a[maxn] = {0, 1, 2, 3,};
return 0;
}
------------------------------
int main()
{
int maxn = 100;
int a[maxn] = {0, 1, 2, 3,};
return 0;
}以下定义方式在 C 语言中无法通过编译,但是在 C++中可以
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const int maxn = 100;
int main()
{
int a[100] = {0, 1, 2, 3,};
return 0;
}
-------------------------------
int main()
{
const int maxn = 100;
int a[100] = {0, 1, 2, 3,};
return 0;
}
数组的赋值
-
错误示范
1
2
3int a[1050] = {0, 1, 2, 3, 4, 5,};
int b[1050];
b = a; -
正确示范
1
2
3
4int a[1050] = {0, 1, 2, 3, 4, 5,};
int b[1050];
for(int i=0; i<=5; i++)
b[i] = a[i];
数组的遍历
-
将数组从头至尾验视一次
-
一般使用
for
循环进行数组遍历 -
一些遍历数组的例子
-
数组的遍历赋值
1
2for(int i=1; i<=n; i++)
b[i] = a[i]; -
数组的遍历初始化
1
2for(int i=1; i<=n; i++)
a[i] = 0; -
遍历数组查找元素
1
2
3
4
5
6
7
8for(int i=1; i<=n; i++)
{
if(key == a[i])
{
ans = i;
break;
}
} -
输出比平均值大的数
1
2
3
4
5
6
7for(int i=1; i<=n; i++)
{
if(a[i] > avg)
{
printf("%d ", a[i]);
}
} -
输出数组中所有值
1
2
3
4for(int i=1; i<=n; i++)
{
printf("%d ", a[i]);
}
-
数组作为函数的参数
-
对于一维数组,不能在
[]
中给出数组的大小 -
无法再利用
sizeof()
函数给出数组的大小 -
往往需要再传入一个变量来描述数组大小
1
2
3
4
5
6
7
8
9
10
11
12
13int 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 码表
-
char
既是整数也是字符,取决于你怎么看它以下两个
printf
语句输出都是1
1
2printf("%c\n", 49);
printf("%d\n", 1);以下两个
printf
语句输出都是49
1
2printf("%c\n", '1');
printf("%d\n", 49);以"
char
的方式"看待49
这个整数,它是字符1
以"
int
的方式"看待1
这个字符,他是整数49
-
'\n'
,'\t'
与空格
字符串的定义与初始化
1 | char s[100]; |
字符串的输入与输出
%s
会将当前位置与下一个空格或回车之间的字符串赋值给字符串s
1 | char s[100]; |
- 上述写法中,
scanf
会将输入字符串的首位赋值给s[0]
- 每个字符串都以
'\0'
结尾
常用字符串函数
- 需要
#include <string.h>
getchar()
单字符输入,putchar()
单字符输出strlen()
计算字符串长度strcmp()
比较两个字符串是否相同strcpy()
将一个字符串赋值给另一个字符串strcat()
将一个字符串拷贝到另一个字符串的后面,连成一个长的字符串
数组其他注意事项
-
数组是一种容器,有如下特点:
- 一个数组中所有元素类型相同
- 数组一旦定义,不能改变大小
- 任意维度的数组在内存中的排列是连续的
-
数组中的每一个单元是数组类型的一个变量,使用数组时
[]
中的数字称为下标或索引,下标从开始计数 -
一个拥有个元素的数组,下标的范围是,以大于等于的下标访问数组可能会造成数组越界导致程序崩溃,但是编译器在编译时并不会检查数组是否越界,因此务必保证程序只使用有效下标访问数组
-
创建数组时,应给予冗余的数组元素个数,如
1
2
3
4
5
6
7
8
9
10
11
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】闰年展示
输入 ,输出$[x,y] $区间中闰年个数,并在下一行输出所有闰年年份数字,使用空格隔开。
-
不使用数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;
} -
使用数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 数楼梯
楼梯有阶,上楼可以一步上一阶,也可以一步上二阶。
编一个程序,计算共有多少种不同的走法。
-
不使用数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
}运行结果:
-
使用数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
对于一个递归函数
- 如果就返回值
- 如果就返回
- 如果并且$b<c w(a,b,c-1)+w(a,b-1,c-1)-w(a,b-1,c)$
- 其它的情况就返回
这是个简单的递归函数,但实现起来可能会有些问题。当均为时,调用的次数将非常的多。你要想个办法才行.
对于一些特殊情况,比如 既满足条件又满足条件,这种时候我们就按最上面的条件来算,所以答案为
-
不使用数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
}运行结果:
-
使用数组(记忆化)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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 |
浮点数
-
令人头疼的精度问题
1
2
3
4
5
6
7
8
9
10
int main()
{
if(1.1 + 0.1 == 1.2)
printf("YES!!!");
else
printf("NO!!!");
return 0;
}以上程序输出的是
NO
-
1.1 + 0.1
为什么不等于1.2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
260.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
相比较
二者不同 -
解决方案
1 |
|
C 语言程序设计其他注意事项
- 不要把SSE当成IDE
- C 语言上机测试提交前一定要在本地编译通过并用样例数据测试
- 循环变量使用
1 | int a, tmp; |
-
遵循 C 语言标准代码规范
-
运算符两端空格
1
2
3int a=b+1; //错误示范
int a = b + 1; //正确示范当然,嫌for
循环内空格太多也可以不遵循本条 -
采用缩进风格
1
2
3
4
5
6
7
8
9
10
11if(n == 0) //错误示范
{
a = b + 1;
b ++;
}
if(n == 0) //正确示范
{
a = b + 1;
b ++;
} -
避免把多行语句写在同一行
1
2
3
4int a = b + 1; b++; //错误示范
int a = b + 1; //正确示范
b++;
-
几道题
洛谷 P1177 【模板】快速排序
给出个数,将其从小到大排序.
对于的数据,
相亲数
输入一个数,输出范围内每一对相亲数
注:相亲数指两个正整数中,彼此的全部约数之和(本身除外)与另一方相等
洛谷 P3383 【模板】线性筛素数
给定一个范围 ,有 个询问,每次输出区间范围内第 小的素数。
对于的数据,,,保证输入合法
洛谷 P1706 全排列问题
输入一个数输出自然数到所有不重复的排列,即的全排列,要求所产生的任一数字序列中不允许出现重复的数字。
输出的所有排列按字典序排序
对于的数据,