Что не так с этим примером из K & R? - PullRequest
1 голос
/ 12 сентября 2011

Я пробую пример списка каталогов из раздела 8.6 K & R, но при тестировании кода он говорит, что «не может получить доступ» для каждого файла в каталоге.Это потому, что он предназначен для Unix?Вот то, что я думаю, что код должен выглядеть, когда соединен, что я пропускаю? ДОБАВИТЬ: Как мне исправить этот код, чтобы он работал?

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "dirent.h"
#include <sys/dir.h>
#define NAME_MAX 14

typedef struct {
  long ino;
  char name[NAME_MAX+1];
} Dirent;

void fsize(char *);

/* print file sizes */
main(int argc, char **argv)
{
  if (argc == 1)
    fsize(".");
  else
    while (--argc > 0)
      fsize(*++argv);
  return 0;
}

void dirwalk(char *, void (*fcn)(char *));

/* fsize: print size of file "name" */
void fsize(char *name)
{
  struct stat stbuf;

  if (stat(name, &stbuf) == -1) {
    fprintf(stderr, "fsize: can't access %s\n", name);
    return;
  }
  if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
    dirwalk(name, fsize);
  printf("%81d %s\n", stbuf.st_size, name);
}

#define MAX_PATH 1024

/* dirwalk: apply fn to all files in dir */
void dirwalk(char *dir, void (*fcn)(char *))
{
  char name[MAX_PATH];
  Dirent *dp;
  DIR *dfd;

  if ((dfd = opendir(dir)) == NULL) {
    fprintf(stderr, "dirwalk: can't open %s\n", dir);
    return;
  }

  while ((dp = readdir(dfd)) != NULL) {
    if (strcmp(dp->name, ".") == 0
    || strcmp(dp->name, "..") == 0)
      continue;
    if (strlen(dir)+strlen(dp->name)+2 > sizeof(name))
      fprintf(stderr, "dirwalk: name %s/%s too long\n",
          dir, dp->name);
    else {
      sprintf(name, "%s/%s", dir, dp->name);
      (*fcn)(name);
    }
  }
  closedir(dfd);
}

Ответы [ 4 ]

7 голосов
/ 12 сентября 2011

С чего начать ....

  1. NAME_MAX и PATH_MAX действительно существуют ... вы не должны определять их сами.
  2. Dirent действительно существует ( struct dirent из dirent.h ), поэтому используйте существующий struct dirent.
  3. Вы не используете скобки последовательно (всегда используйте скобки), что делает ваш код нечитаемым.
  4. Вы должны включать , а не "dirent.h" (если у вас нет особых причин для этого).
  5. Вам необходимо заранее объявить свои функции, прежде чем использовать их.

Кроме того, в будущем было бы полезно знать:

  1. Ваш компилятор.
  2. Ваша операционная система.
  3. Ошибки, выдаваемые вашим компилятором.

Как видно из вышесказанного, решение ваших проблем будет намного проще. Что еще более важно, вы упомянули, что изучаете «K & R C». Обратите внимание, что «K & R C» предшествует стандартизации языка программирования C Международной организацией по стандартизации (ISO). Начиная с K & R C, было несколько итераций ISO C: ISO C 1989 (a.k.a. C89), ISO C 1990 (a.k.a. C90) и ISO C 1999 (a.k.a. C99). Я настоятельно рекомендую вам научиться самостоятельно из более современной книги.

Кроме того, если вы ориентируетесь на UNIX, я настоятельно рекомендую вам освоиться со справочными страницами UNIX. Хромовое расширение UNIX Manual Pages позволяет легко получить доступ к официальному стандарту IEEE . 1003.1 Страницы справочника POSIX . Я настоятельно рекомендую вам взглянуть на них, чтобы понять, какие стандартные функции доступны, что ожидается от входов, что гарантировано от выходов и как они могут выходить из строя.

5 голосов
/ 12 сентября 2011

Структура Dirent, определенная в вашем примере, вероятно, не соответствует struct dirent для вашей системы (она не в моей).

В системе Linux следующее решит проблему:

/* dirwalk: apply fn to all files in dir */
void dirwalk(char *dir, void (*fcn)(char *))
{
    char name[MAX_PATH];
    struct dirent *dp;

    DIR *dfd;

    if ((dfd = opendir(dir)) == NULL) {
            fprintf(stderr, "dirwalk: can't open %s\n", dir);
            return;
    }

    while ((dp = readdir(dfd)) != NULL) {
            if (strcmp(dp->d_name, ".") == 0
                            || strcmp(dp->d_name, "..") == 0)
                    continue;
            if (strlen(dir)+strlen(dp->d_name)+2 > sizeof(name))
                    fprintf(stderr, "dirwalk: name %s/%s too long\n",
                                    dir, dp->d_name);
            else {
                    sprintf(name, "%s/%s", dir, dp->d_name);
                    (*fcn)(name);
            }
    }
    closedir(dfd);
}

Функция readdir зависит от системы, поэтому вам следует ознакомиться с документацией.В системе Linux вы можете набрать man 2 readdir в терминале, чтобы увидеть интерактивное руководство по функции readdir.

2 голосов
/ 12 сентября 2011

Проблема очевидна, когда вы смотрите на предупреждения компилятора.По крайней мере, в моей системе GCC выдает:

x.c: In function ‘dirwalk’:
x.c:58:16: warning: assignment from incompatible pointer type

Это строка while ((dp = readdir(dfd)) != NULL) {.Как указал Майкл, struct dirent уже существует, и это то, что возвращается readdir.Вы используете нестандартную несовместимую структуру, поэтому вместо имен файлов / каталогов вы получаете мусор.

Самое быстрое решение - это избавиться от Dirent и использовать вместо него struct dirent.

2 голосов
/ 12 сентября 2011

Ваша конкретная проблема - ваш доморощенный Dirent.readdir на самом деле возвращает struct dirent *, и его макет не обязательно соответствует вашему Dirent, в частности, до d_name, вероятно, имеется больше членов.Таким образом, когда вы смотрите на dp->name, чтобы получить имя, вы, вероятно, в конечном итоге пытаетесь прочитать некоторую не строку как строку, и это дает вам глупость;все разваливается, как только вы получаете ерунду.

Так что исправьте все, что упомянул Майкл Аарон Сафян, включите флаги предупреждений вашего компилятора (-Wall -Werror - хорошее начальное место для gcc), и ваша программа должна начать работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...