C中字符的频率 - 奇怪的数字

时间:2016-10-05 23:53:54

标签: c

这是我的功能:

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

为什么我在大写矩阵中得到这些奇怪的长数字?好像我没有在大写数组之外编制索引 - 我以与小写数组完全相同的方式处理它。

我在这里做错了什么?

3 个答案:

答案 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,;\"'";

whilestrtok一起使用没有任何问题,但由于您只对循环范围内的token感兴趣,因此使用for循环和{{ 1}}循环声明会导致表达式更紧凑,例如

c99

for (char *tok = strtok (tmp, delim); tok; tok = strtok (NULL, delim)) count++; ,(printStatistics下方)如果prnstats(我的currents为空,会发生什么? ?由于您无法对其中任何一个的频率进行分类,因此在函数开头进行简单检查可以防止未定义的行为。

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)

你对strcpy的调用:

strcpy(tempToken,current);

写过缓冲区的末尾。这会产生未定义的行为,在这种情况下,会有效地破坏您在2个阵列中收集的数据。 考虑使用strnlen_s来查找您尝试制作临时副本的缓冲区的长度。和strcpy_s以确保您不会覆盖缓冲区。

此外,乍一看,'&amp;'的用法让我倾斜了我的头。如果这不是故意的,要非常小心。它在这里工作,因为&gt; =和&lt; =运算符产生一个布尔值,但一般来说,最好不要试图找到两个值是否为“true”。