繁体   English   中英

在C中使用指针代替二维数组字符串

[英]Using pointers instead of 2-D array string in C

我有一个类似这里的问题:编写一个程序以读取多行文本文件,并将'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;
}

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

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

如果您确实想节省内存,则不应复制文件的任何内容,而应将其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. 获取文件中的行数
    • 您可以使用带有fgets和反变量的循环来执行此操作
  2. 分配一个char* -Array( char**
    • 您可以将malloc()与计数器变量一起使用
  3. 将文件的所有行读入分配的内存部分,并为每一行分配内存
    • 您可以使用getline()获取指向已分配内存部分的指针
    • 真正的getline需要您做很多工作,但是您也可以将以下功能组合在一起:
      • fgets
      • malloc
      • realloc
      • strlen
  4. 按行的长度对char**进行排序
    • 您可以使用strlen一些swap功能全到archieve这(通过简单的申请某事。像冒泡
  5. printf输出sortet char**的前N
  6. 不要忘记使用free()释放所有分配的内存!

有关提示,请参见以下帖子

请注意,我提供的步骤并未针对内存或cpu-usage进行优化。 但是,如果您的程序正常运行,并且您了解所做的工作,那么下一步可能会是。

此任务还显示了现代编程语言的巨大好处,其中此类任务是五行脚本。 这是执行您要归档的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或汇编程序中对此类任务进行编程,以更深入地了解计算机的工作方式。

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

如果您愿意坚持使用最大行长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); ,并且仅当该行大于当前的最小成员时才插入该行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM