【问题标题】:Using pointers instead of 2-D array string in C在 C 中使用指针而不是二维数组字符串
【发布时间】:2014-05-12 08:37:30
【问题描述】:

我有这样的问题: 编写一个程序来读取多行文本文件并将“N”个最长的行写入标准输出。在命令行中指定要读取的文件的位置。

现在我这样编写程序:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
  int a,k,n,i=0,j;
  int part;
  char ar[1000][1000],str[1000];
  /* char file[200]; */
  /* scanf("%s",file); */
  FILE *f = fopen(argv[1],"r");
  if ( f == NULL || argc < 2)
  {
    return 0;
  }

   fscanf(f,"%d",&a);

   while (fscanf(f,"%s",str)==1)
   {
        strcpy(ar[i++],str);

      for ( k = 0 ; k < i ; k++ )
      {
        for ( j = k ; j < i ; j++)
        {
          if ( strlen(ar[k]) < strlen(ar[j]))
          {
            strcpy(str,ar[k]);
            strcpy(ar[k],ar[j]);
            strcpy(ar[j],str);
          }
        }
      }

   }

   for ( j = 0 ; j < a ; j++ )
   {
    puts(ar[j]);
  }

return 0;
}

首先它对我来说运行良好,但在提交时它给了我运行时错误。 其次,我想使用指针和动态分配内存来做到这一点。我该怎么做?

对不起,我睡了一会儿。你能解释一下我的代码有什么问题吗?为什么它不起作用。我想没有人解释我哪里做错了。请让我知道在发布我的问题几个小时后如何引起人们的注意。再次感谢大家给我这么多时间。

【问题讨论】:

  • 您具体看到了什么运行时错误?请将实际的错误消息复制并粘贴到您的问题中。
  • 网站未指定任何错误。
  • 只需使用malloc(size) 分配内存,使用动态内存几乎与使用常规数组相同。只是不要忘记释放您分配的内存。动态分配不会节省内存,但允许您使用比您可以放入堆栈更长的数组。
  • @EliasVanOotegem - 它将占用一个额外的指针和几个字节的分配数据 - 相对于整体内存使用量略有增加。至于访问 - 这真的取决于编译器的性能。如果直接作为堆栈偏移量访问而不是从堆栈偏移量处的指针取消引用,堆栈数组可能会更快。无论如何,堆栈空间通常非常有限,因此我不会将动态内存分配作为一种不好的做法。
  • @jahan 您正在访问argv[1] 之前您检查argc 是否足够大。如果程序在没有任何参数的情况下运行,则会触发未定义的行为。

标签: c arrays pointers


【解决方案1】:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
    int a, n=0, i;
    char **ar, str[1000];
    int *lens;

    FILE *f = fopen(argv[1],"r");

    if ( f == NULL || argc < 2){
        return 0;
    }

    fscanf(f,"%d",&a);
    if(NULL==(ar=calloc(a, sizeof(char*))) || NULL==(lens=calloc(a, sizeof(int)))){
        perror("malloc");
        return -1;
    }
    while (fscanf(f, " %999[^\n]", str)==1){
        ++n;
        int len = strlen(str);
        for(i = 0;i < a;++i){
            if(lens[i] < len){
                free(ar[a-1]);
                memmove(&lens[i+1], &lens[i], (a-i-1)*sizeof(int));
                memmove(&ar[i+1], &ar[i], (a-i-1)*sizeof(char*));
                ar[i]=strdup(str);
                lens[i]=len;
                break;
            }
        }
    }
    fclose(f);
    for (i = 0 ; i < n && i < a ; i++ ){
        puts(ar[i]);
        free(ar[i]);
    }
    free(ar);free(lens);

    return 0;
}

【讨论】:

    【解决方案2】:

    如果您真的想节省内存,那么您不应该复制文件的任何内容,而是将mmap 复制到您的进程中,并且只将偏移量保存到代码中最长的行中。对于较长的文件,这应该会显着提高性能,因为它将内存管理外包给内核。

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/mman.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    
    
    int main(int argc, char ** argv) {
        char * filename = argv[1];
        if (!filename) {
            fprintf(stderr, "Usage: %s filename\n", argv[0]);
            exit(-1);
        }
    
        int fd = open(filename, O_RDONLY);
        if (fd < 0) {
            perror("open");
            abort();
        }
    
        struct stat file_stats;
        if(fstat(fd, &file_stats)) {
            perror("fstat");
            abort();
        }
    
        char * file_contents = mmap(NULL    // anywhere
            , file_stats.st_size + 1        // file length + 1 byte for null terminator
            , PROT_READ                     // we only need read only
            , MAP_PRIVATE                   // this doesn't really matter since we are read only
            , fd                            // from this file descriptor
            , 0);                           // from beginning
    
        if (file_contents == MAP_FAILED) {
            perror("mmap");
            abort();
        }
    
        // optional
        // Expect page references in sequential order.
        // Hence, pages can be aggressively read ahead, and may be freed soon after they are accessed.
        madvise(file_contents, file_stats.st_size + 1, MADV_SEQUENTIAL);
    
        struct {
            size_t start;
            size_t end;
        } longest_lines[10]; // struct to hold ofsets for longest lines
    
        memset(longest_lines, 0, sizeof(longest_lines)); // initialise
        int shortest_line_id = 0;
    
        char * start = file_contents;
        char * next;
    
        // while we are not at the end
        while (start < file_contents + file_stats.st_size) {
            if (!(next = strchr(start, '\n'))) // if line ternimator wasn't found, then go to the end
                next = file_contents + file_stats.st_size;
    
            size_t line_length = next - start;
    
            // if this line is longer then our shortest
            if (line_length > longest_lines[shortest_line_id].end - longest_lines[shortest_line_id].start) {
                longest_lines[shortest_line_id].start = start - file_contents;
                longest_lines[shortest_line_id].end = next - file_contents;
    
                // determine new shortest line
                int i;
                for (i = 0; i < sizeof(longest_lines)/sizeof(*longest_lines); i++) {
                    if (
                    longest_lines[i].end - longest_lines[i].start
                    <
                    longest_lines[shortest_line_id].end - longest_lines[shortest_line_id].start
                    )
                        shortest_line_id = i;
                }
            }
            // next line starts at this offset
            start = next + 1;
        }
    
        int i; // print them
        for (i = 0; i < sizeof(longest_lines)/sizeof(*longest_lines); i++) {
            printf("%.*s\n", (int)(longest_lines[i].end - longest_lines[i].start), file_contents + longest_lines[i].start);
        }
    
        return 0;
    }
    

    【讨论】:

    • 这让我笑了:D +1
    【解决方案3】:

    因为它看起来像是某种家庭作业或某事。我不会提供完整的解决方案。但我会尽量给你一些平静,你可以开始。

    您应该按照职责划分您的任务,并尝试将您的程序创建得更模块化。

    几项任务似乎是:

    1. 获取文件中的行数
      • 您可以使用带有 fgets 的循环和一个反变量来执行此操作
    2. 分配一个char*-数组(char**)
      • 您可以将malloc() 与计数器变量一起使用
    3. 将文件的所有行读入分配的内存段并为每一行分配内存
      • 您可以使用getline() 获取指向已分配内存部分的指针
      • 真的getline 需要你做很多工作,但你也可以将以下功能组合起来:
        • fgets
        • malloc
        • realloc
        • strlen
    4. 按行长对char** 进行排序
      • 您可以使用strlen 和一些swap 函数来实现此功能(通过简单的应用,例如bubblesort)
    5. 用printf 输出排序集char** 的第一行N
    6. 别忘了用free()释放所有分配的内存!

    有关提示,请参阅以下post。

    请注意,我提供的步骤并未针对内存或 CPU 使用进行优化。但是,如果您的程序有效并且您了解自己所做的工作,那么这可能是下一步。

    此任务还显示了现代编程语言的巨大好处,其中此类任务将是一个五行脚本。这是执行您想要归档的F# 版本:

    open System
    open System.IO
    open System.Linq
    
    [<EntryPoint>]
    let main args =
      try
        let linesToPrint = Int32.Parse(args.ElementAt(0))
        let path         = @"C:\Users\XYZ\Desktop\test.txt"
        File.ReadAllLines(path).OrderByDescending(fun line -> line.Length).Take(linesToPrint).ToArray()
        |> Array.iter(printfn "%s")
      with
      | ex -> eprintfn "%s" ex.Message
      0
    

    注意解决方案的排序和可读性。 尽管如此,用 c 或汇编语言编写这样的任务是值得的,以更深入地了解计算机的工作原理。

    【讨论】:

    • 感谢您的宝贵时间和回答。我仍然对 F# 是什么感到困惑?你能告诉我我的代码有什么问题吗?
    • F# 是一种函数式编程语言。也许值得将来学习,因为正如您所见,用它来解决您的任务要容易得多。您的代码中的问题如下:fscanf(f,"%s",str) 也按其空格分割每一行。因此,您确实 NOT 将整行放入 str 但一行中的一个单词。您应该使用:while ((fgets(str, 1000, f) != NULL)) 将整行放入 str 中。此外,您的变量 a 设置错误。我调试了你的代码,a 设置为 -858993460。见stackoverflow.com/questions/1910724/…
    • 在问题中提到文件将第一行作为 N.
    • 哦,我没看到。所以变量 a 设置正确。但是fscanf 的问题仍然存在。我认为这是导致异常的问题。如果输入文件中有超过 1000 个单词,程序就会崩溃。因此,如果输入文件有 10 行和 1200 个单词,您的程序将崩溃,因为您将单词视为行并且只为 1000 个单词分配内存。所以使用fgets 方法,你的代码应该可以工作。
    • 任何程序的输入都应该有限制。毫无疑问,上述程序不适用于您描述的情况,但对于更高的输入值,每个程序仍然存在问题。
    【解决方案4】:

    如果您愿意坚持最大行长 1000,您可以这样做:

    char (*ar)[1000] = malloc(a * sizeof *ar);
    
    if (!ar)
        // error handling....
    

    然后像以前一样使用ar。

    有一个问题:fscanf(f,"%s",str)。这读取一个单词,并且不进行长度检查。我猜你实际上想读一整行,而不是溢出你的缓冲区:

    fgets(str, sizeof str, f);
    

    如果您愿意,您可以在此之后从 str 中删除换行符,但实际上这对您的程序没有任何影响。

    您的算法目前存在问题;您将每一行读入ar。相反,您应该将ar 的大小设置为a(我认为a 的意思是N),取出strcpy(ar[i++],str); 行,如果它大于当前最小的成员。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-28
      • 2017-04-26
      相关资源
      最近更新 更多