这是我的功能:
void printStatistics(const char *current) {
int count = 0, i = 0, length = strlen(current);
int lowercaseLetters[26] = {0};
int uppercaseLetters[26] = {0};
char *token;
for (i = 0; i < length; i++) {
if (current[i] >= 'a' & current[i] <= 'z') {
lowercaseLetters[current[i] - 'a']++;
}
}
for (i = 0; i < length; i++) {
if (current[i] >= 'A' & current[i] <= 'Z') {
uppercaseLetters[current[i] - 'A']++;
}
}
char tempToken[10] = "";
strcpy(tempToken, current);
token = strtok(tempToken, " ");
while (token != NULL) {
token = strtok(NULL, " ");
count++;
}
printf("Statistics:\n"
"\tlength:\t\t%d\n"
"\tword:\t\t%d\n"
"Frequency:\n", length, count);
printf("Printing Uppercase matrix...\n");
for (i = 0; i < 26; i++) {
printf("\tfrequency of %c:\t%d\n", 'a' + i, uppercaseLetters[i]);
}
printf("Printing Lowercase matrix...\n");
for (i = 0; i < 26; i++) {
printf("\tfrequency of %c:\t%d\n", 'a' + i, lowercaseLetters[i]);
}
}
以下是我尝试检查字符串gggggggggggggggggggg BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
Statistics:
length: 74
word: 2
Frequency:
Printing Uppercase matrix...
frequency of a: 1734829927
frequency of b: 1734829927
frequency of c: 1107322727
frequency of d: 1111638594
frequency of e: 1111638594
frequency of f: 1111638594
frequency of g: 1111638594
frequency of h: 1111638594
frequency of i: 1111638594
frequency of j: 1111638594
frequency of k: 1111638594
frequency of l: 1111638594
frequency of m: 1111638594
frequency of n: 1111638594
frequency of o: 1111638594
frequency of p: 1111638594
frequency of q: 0
frequency of r: 0
frequency of s: 0
frequency of t: 0
frequency of u: 0
frequency of v: 0
frequency of w: 0
frequency of x: 0
frequency of y: 0
frequency of z: 0
Printing Lowercase matrix...
frequency of a: 0
frequency of b: 0
frequency of c: 0
frequency of d: 0
frequency of e: 0
frequency of f: 0
frequency of g: 20
frequency of h: 0
frequency of i: 0
frequency of j: 0
frequency of k: 0
frequency of l: 0
frequency of m: 0
frequency of n: 0
frequency of o: 0
frequency of p: 0
frequency of q: 0
frequency of r: 0
frequency of s: 0
frequency of t: 0
frequency of u: 0
frequency of v: 0
frequency of w: 0
frequency of x: 0
frequency of y: 0
frequency of z: 0
为什么我在大写矩阵中得到这些奇怪的长数字?好像我没有在大写数组之外编制索引 - 我以与小写数组完全相同的方式处理它。
我在这里做错了什么?
答案 0 :(得分:3)
您通过写过缓冲区的结尾来导致 undefined behaviour 。主要问题在于:
char tempToken[10] = "";
strcpy(tempToken, current);
由于在将current
复制到tempToken
之前未检查字符串的长度,因此您可能会超过9个字符的限制(允许一个额外的字符用于终止{ {1}}字节)并破坏分配给其他数据的内存。
在您的情况下,这是程序调用{{1}}时堆栈的样子:(但请参阅下面的注释)
'\0'
将字符串printStatistics()
复制到+--------------------+--------------------------+--------------------------+--------------
| char tempToken[10] | int uppercaseLetters[26] | int lowercaseLetters[26] | token, etc...
+--------------------+--------------------------+--------------------------+--------------
时,前十个字符将完全填充此数组,其余字符将写入数组gggggggggggggggggggg BBBBBBBBBBBBBB...
。因此,当您从此数组中获取数据时,实际上是在读回这些ASCII字符(1734829927 == 0x67676767 ==“gggg”; 1111638594 == 0x42424242 ==“BBBB”)。
如果你复制一个较长的字符串,你也会覆盖tempToken
,然后是其他变量(uppercaseLetters
等)。
strncpy()
功能旨在避免此类问题。你也应该使用它。
此外,正如其他人所指出的那样,您使用的是按位“和”运算符lowercaseLetters
,其中需要逻辑“和”token
。
- 注意:其他系统和其他编译器会以不同方式存储内容,并会以其他方式行为不当。在我的计算机上编译时,您的代码就会崩溃。
答案 1 :(得分:2)
已经适当涵盖了大问题,但还有一些额外的意见,评论时间过长。首先,尽量避免在代码中使用幻数。如果您需要26
,#define
一个常量或使用enum
,例如
#define ABET 26
或
enum { ABET = 26 };
然后你可以在整个代码中使用常量(如果你需要稍后调整它,你只需要在一个位置更改它)。然后你可以做类似以下的事情(注意:我不喜欢打字,所以你的lowercaseletters
只是被下面的lc
取代等等......
int count = 0, i = 0, len = (int)strlen (s),
lc[ABET] = { 0 },
uc[ABET] = { 0 };
...
for (i = 0; i < ABET; i++)
接下来,不需要两个单独的环来填充上/下频率阵列,例如, (并将current
替换为s
)
/* no need for 2 separate loops */
for (i = 0; i < len; i++) {
if ('a' <= s[i] && s[i] <= 'z')
lc[s[i] - 'a']++;
if ('A' <= s[i] && s[i] <= 'Z')
uc[s[i] - 'A']++;
}
(注意:将您的比较写为if ('a' <= s[i] && s[i] <= 'z')
可能会使每个集合中的值更加明显,这只是一种品味问题,无论哪种方式都是正确的)
您可能还会发现在变量中声明一组delimiters
(例如delim
)提供了一种方便的方法来防止在每个strtok
调用中对分隔符进行硬编码,从而增加可维护性,例如
char *delim = " \t,;\"'";
将while
与strtok
一起使用没有任何问题,但由于您只对循环范围内的token
感兴趣,因此使用for
循环和{{ 1}}循环声明会导致表达式更紧凑,例如
c99
在for (char *tok = strtok (tmp, delim); tok; tok = strtok (NULL, delim))
count++;
,(printStatistics
下方)如果prnstats
(我的current
)s
或为空,会发生什么? ?由于您无法对其中任何一个的频率进行分类,因此在函数开头进行简单检查可以防止未定义的行为。
NULL
如果您愿意,可以包含错误消息,例如
if (!s || !*s) return;
将所有这些结合在一起,以及传统上消除C中的if (!s || !*s) {
fprintf (stderr, "prnstats() error: invalid parameter.\n");
return;
}
或MixedCase
变量,并消除更多的输入,您可以执行类似于以下操作:
camelCase
仔细看看,请考虑下面的#include <stdio.h>
#include <string.h>
#define ABET 26
void prnstats (const char *s);
int main (int argc, char **argv) {
char *s = argc > 1 ? argv[1] :
"a quick brown fox jumps over the lazy dog."
"A QUICK BROWN FOX JUMPS OVER THE LAZY DOG.";
prnstats (s);
return 0;
}
void prnstats (const char *s)
{
if (!s || !*s) return;
int count = 0, i = 0, len = (int)strlen (s),
lc[ABET] = { 0 },
uc[ABET] = { 0 };
char *delim = " \t,;\"'";
/* no need for 2 separate loops */
for (i = 0; i < len; i++) {
if ('a' <= s[i] && s[i] <= 'z')
lc[s[i] - 'a']++;
if ('A' <= s[i] && s[i] <= 'Z')
uc[s[i] - 'A']++;
}
char tmp[len + 1]; /* a vla is fine here */
strcpy (tmp, s);
for (char *tok = strtok (tmp, delim); tok; tok = strtok (NULL, delim))
count++;
printf ("Statistics:\n"
" length : %3d\n"
" words : %3d\n\n", len, count);
/* no need for 2 separate loops for dual-columns */
printf ("Frequency:\n Uppercase Lowercase\n\n");
for (i = 0; i < ABET; i++)
printf (" %c : %2d %c : %2d\n",
'A' + i, uc[i], 'a' + i, lc[i]);
}
字样,如果您有任何问题请与我联系。
示例使用/输出
17
答案 2 :(得分:1)