在C中使用指针而不是2-D数组字符串

时间:2014-05-12 08:37:31

标签: c arrays pointers

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

现在我写了这样的程序:

#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;
}

首先,它对我来说效果很好但是在提交时它给了我运行时错误。 其次我想用指针和动态分配内存来做。我怎么能这样做?

抱歉,我上床睡了一段时间。你能解释一下我的代码有什么问题吗?为什么它不起作用。我想没有人解释我在哪里做错了。请告诉我如何在发布问题几个小时后引起人们的注意。再次感谢所有人给我这么多时间。

4 个答案:

答案 0 :(得分:1)

如果你真的想节省内存,那么你不应该复制文件的任何内容,而是将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;
}

答案 1 :(得分:1)

因为它似乎是某种功课或某事。我不会提供完整的解决方案。但我会尝试给你一些你可以开始的和平。

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

以下几项任务似乎是:

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

    有关提示,请参阅以下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或者也可以在汇编程序中编写这样的任务,以便更深入地了解计算机的工作方式。

答案 2 :(得分: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;
}

答案 3 :(得分:0)

如果你很高兴坚持你的最大行长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),取出行{{1}只有当它大于当前最小成员时才插入该行。