今日学习笔记:
(思路):
1. 定义变量与初始化计数数组
- 定义字符变量 c 用于临时存储读取到的字符。
-
定义数组 arr[123] 并初始化为 {0}:
- 数组大小为 123,是因为小写字母 'a' 到 'z' 的 ASCII 值范围是 97~122,数组索引刚好可以覆盖这个范围(arr['a'] 对应索引 97,arr['z'] 对应索引 122)。
- 初始化 {0} 确保所有元素初始值为 0,避免随机值干扰计数结果。
2. 循环读取输入并统计字母
-
使用 while ((c = getchar()) != EOF) 循环读取所有输入字符,直到遇到 “文件结束符”(EOF,通常通过 Ctrl+Z 或 Ctrl+D 输入):
- getchar() 每次读取一个字符(包括空格、回车、标点等)。
- 仅当读取的字符 c 是小写字母(c >= 'a' && c <= 'z')时,才执行计数:arr[c]++(例如读到 'a' 时,arr[97] 的值加 1)。
- 非小写字母字符会被直接跳过,不参与计数。
3. 输入结束后输出统计结果
-
当输入结束(循环退出),使用 for (c = 'a'; c <= 'z'; c++) 遍历所有小写字母:
- 对每个字母 c,若其计数 arr[c] > 0(即该字母出现过),则通过 printf("%c:%d\n", c, arr[c]) 输出 “字母:次数” 的格式(例如 a:3 表示 'a' 出现 3 次)。
- 未出现过的字母(计数为 0)不输出,减少冗余信息。
一些迷惑的点:
计数操作:
arr 是一个数组,其索引对应字符的 ASCII 码(例如 arr[97] 对应 'a',arr[98] 对应 'b',以此类推)。
当 c 是小写字母时(比如 c = 'k'),arr[c] 等价于 arr['k'],而 'k' 的 ASCII 码是 107,所以实际操作的是 arr[107]。
++ 是自增运算符,arr[c]++ 表示将当前字母对应的计数加 1(例如第一次读到 'k' 时,arr['k'] 从 0 变成 1;第二次读到 'k' 时,从 1 变成 2,以此类推)。
(原来的代码中出现的问题):
char i,j;
int arr[10000];
while (scanf("%c",&i)!=EOF) {
if (i>='a' && i<='z') {
arr[i]++;
}
for (j='a';j<='z';j++) {
if (arr[j]>0) {
printf("%c:%d\n",j,arr[j]);
}
}
}
- 数组未初始化:arr[10000] 定义后未初始化,其元素初始值是随机的,导致首次统计结果不正确。
- 输入包含非字母字符:scanf("%c", &i) 会读取所有字符(包括空格、回车等),非小写字母字符会被跳过,但循环仍会执行后续的输出逻辑。
- 输出时机不当:每次读取一个字符后就输出所有字母的计数,会导致输出过于频繁(例如输入一个字母就输出 26 行可能的结果)。
- 数组下标范围:小写字母 'a'-'z' 的 ASCII 值是 97-122,arr 数组只需索引到 122 即可,定义为 arr[123] 更合理(避免空间浪费)。
#include<stdio.h>
int main()
{
char i,j;
int arr[123]={0};
while ((i=getchar())!=EOF) {
//判断当前读取的字符是否为小写字母,如果是,则对应字母的计数加 1 if (i>='a' && i<='z') {
arr[i]++;
}
}
for (j='a';j<='z';j++) {
if (arr[j]>0) {
printf("%c:%d\n",j,arr[j]);
}
}
return 0;
}