遍历机器代码时遇到问题

Having issues iterating through machine code

我试图在 c 中重新创建 wc 命令,但在任何包含机器代码的文件(核心文件或编译的 c)中获取正确数量的单词时遇到问题。记录的字数总是比 wc 返回的字数少 90% 左右。

项目信息供参考

编译语句

gcc -ggdb wordCount.c -o wordCount -std=c99

wordCount.c

/*
 *  Author(s)   - Colin McGrath
 *  Description - Lab 3 - WC LINUX
 *  Date        - January 28, 2015
 */

#include<stdio.h>
#include<string.h>
#include<dirent.h>
#include<sys/stat.h>
#include<ctype.h>

struct counterStruct {
    int newlines;
    int words;
    int bt;
};

typedef struct counterStruct ct;

ct totals = {0};

struct stat st;

void wc(ct counter, char *arg)
{
    printf("%6lu %6lu %6lu %s\n", counter.newlines, counter.words, counter.bt, arg);
}

void process(char *arg)
{
    lstat(arg, &st);
    if (S_ISDIR(st.st_mode))
    {
        char message[4056] = "wc: ";
        strcat(message, arg);
        strcat(message, ": Is a directory\n");
        printf(message);
        ct counter = {0};
        wc(counter, arg);
    }
    else if (S_ISREG(st.st_mode))
    {
        FILE *file;
        file = fopen(arg, "r");
        ct currentCount = {0};
        if (file != NULL)
        {
            char holder[65536];
            while (fgets(holder, 65536, file) != NULL)
            {
                totals.newlines++;
                currentCount.newlines++;
                int c = 0;
                for (int i=0; i<strlen(holder); i++)
                {
                    if (isspace(holder[i]))
                    {
                        if (c != 0)
                        {
                            totals.words++;
                            currentCount.words++;
                            c = 0;
                        }
                    }
                    else
                        c = 1;
                }
            }
        }
        currentCount.bt = st.st_size;
        totals.bt = totals.bt + st.st_size;
        wc(currentCount, arg);
    }
}

int main(int argc, char *argv[])
{
    if (argc > 1)
    {
        for (int i=1; i<argc; i++)
        {
            //printf("%s\n", argv[i]);
            process(argv[i]);
        }
    }

    wc(totals, "total");

    return 0;
}

示例 wc 输出:

    135     742  360448 /home/cpmcgrat/53/labs/lab-2/core.22321
    231    1189  192512 /home/cpmcgrat/53/labs/lab-2/core.26554
   5372   40960  365441 /home/cpmcgrat/53/labs/lab-2/file
     24     224   12494 /home/cpmcgrat/53/labs/lab-2/frequency
     45     116     869 /home/cpmcgrat/53/labs/lab-2/frequency.c
   5372   40960  365441 /home/cpmcgrat/53/labs/lab-2/lineIn
     12      50    1013 /home/cpmcgrat/53/labs/lab-2/lineIn2
      0       0       0 /home/cpmcgrat/53/labs/lab-2/lineOut
     39     247   11225 /home/cpmcgrat/53/labs/lab-2/parseURL
    138     318    2151 /home/cpmcgrat/53/labs/lab-2/parseURL.c
     41     230   10942 /home/cpmcgrat/53/labs/lab-2/roman
     66     162    1164 /home/cpmcgrat/53/labs/lab-2/roman.c
     13      13      83 /home/cpmcgrat/53/labs/lab-2/romanIn
     13      39     169 /home/cpmcgrat/53/labs/lab-2/romanOut
      7       6     287 /home/cpmcgrat/53/labs/lab-2/URLs
  11508   85256 1324239 total

示例重建输出 (./wordCount):

   139     76 360448 /home/cpmcgrat/53/labs/lab-2/core.22321
   233    493 192512 /home/cpmcgrat/53/labs/lab-2/core.26554
  5372  40960 365441 /home/cpmcgrat/53/labs/lab-2/file
    25      3  12494 /home/cpmcgrat/53/labs/lab-2/frequency
    45    116    869 /home/cpmcgrat/53/labs/lab-2/frequency.c
  5372  40960 365441 /home/cpmcgrat/53/labs/lab-2/lineIn
    12     50   1013 /home/cpmcgrat/53/labs/lab-2/lineIn2
     0      0      0 /home/cpmcgrat/53/labs/lab-2/lineOut
    40      6  11225 /home/cpmcgrat/53/labs/lab-2/parseURL
   138    318   2151 /home/cpmcgrat/53/labs/lab-2/parseURL.c
    42      3  10942 /home/cpmcgrat/53/labs/lab-2/roman
    66    162   1164 /home/cpmcgrat/53/labs/lab-2/roman.c
    13     13     83 /home/cpmcgrat/53/labs/lab-2/romanIn
    13     39    169 /home/cpmcgrat/53/labs/lab-2/romanOut
     7      6    287 /home/cpmcgrat/53/labs/lab-2/URLs
 11517  83205 1324239 total

注意前两个文件(核心文件)以及 roman 文件和 parseURL 文件(机器代码,无扩展名)在字数统计(第二个整数)上的差异。

C 字符串不存储它们的长度。它们以单个 NUL (0) 字节终止。

因此,strlen 需要一个字符一个字符地扫描整个字符串,直到到达 NUL。这使得:

for (int i=0; i<strlen(holder); i++)

极度低效:对于holder中的每个字符,它需要统计holder中的所有字符以测试i是否仍在范围内。将简单的线性 Θ(N) 算法转换为 Θ(N<sup>2</sup>) cycle-burner.

但在这种情况下,它也会产生错误的结果,因为二进制文件通常包含很多 NUL 个字符。由于 strlen 实际上会告诉您第一个 NUL 的位置,而不是 "line" 的长度,因此您最终会跳过文件中的很多字节。 (从好的方面来说,这使得扫描速度成二次方更快,但是更快地计算出错误的结果并不是真正的胜利。)

您不能使用 fgets 读取二进制文件,因为 fgets 界面不会告诉您它读取了多少。您可以改用 Posix 2008 getline 接口,或者您可以使用 fread 进行二进制输入,这样效率更高,但会迫使您自己计算换行符。 (这不是世界上最糟糕的事情;你似乎也算错了。)

或者,当然,您可以使用 fgetc 一次读取文件一个字符。对于学校练习,这不是一个糟糕的解决方案;生成的代码易于编写和理解,fgetc 的典型实现比 FUD 表明的更有效。