【问题标题】:C Program - reading integers from a file and decoding secret messageC 程序 - 从文件中读取整数并解码秘密消息
【发布时间】:2020-07-22 20:30:07
【问题描述】:

嘿 :) 我的代码需要一些帮助,我认为这大部分是正确的,但我无法弄清楚我哪里出错了。

#include <stdio.h>
#include <stdlib.h>

int num_count(FILE* ptr){
    int count = 0;
    int numHolder = 0;
    while((fscanf(ptr, "%d", &numHolder)) == 1){
     count++;
    }
    return count;
}

void load_nums(FILE* ptr, int *codedPtr, int ncount){
    int number = 0;
    ncount = ncount - 1;
    for(int i = 0; i <= ncount; i++){
     fscanf(ptr, "%d", &number);
     printf("%d", number);
     *(codedPtr + i) = number;
    }
    return;
}

void decode(int *codedPtr, int ncount, char *decodedPtr){
    char temp;
    ncount = ncount - 1;
    for(int i = 0; i <= ncount; i++){
     temp = ((*(codedPtr + i) + *(codedPtr + (ncount - i))) + '0');
     *decodedPtr = temp;
     decodedPtr++;
    }
    return;
}

int main(int argc, char *argv[]){
    int *codedPtr;
    char *decodedPtr;
    FILE *fp;

    if (argc == 2){
     fp = fopen(argv[1], "r+");
    }
    if(argc <= 1){
     printf("Invalid command line: cmd infile outfile\n");
    }
    int numCount = num_count(fp);
    printf("%d", *codedPtr);
    codedPtr = (int*)calloc(numCount, sizeof(int));
    decodedPtr = (char*)calloc(numCount, sizeof(char));
    load_nums(fp, codedPtr, numCount);
    decode(codedPtr, numCount, decodedPtr);
    printf("\n%s\n\n", decodedPtr);
    fclose(fp);
    return(0);
}

我添加了一些打印函数来排除故障,并且在 load_nums 函数期间 printf 函数连续打印 0,它没有从指向的文件中读取正确的整数值。

你们中的任何人都可以帮助特别解决 load_nums 函数吗?谢谢大家,如果您需要任何额外信息,请告诉我。 “-6 -76 53 -34 32 79 142 55 177 78”是文件中指向的内容。

【问题讨论】:

  • num_count 运行后,fp 位于文件末尾。致电load_nums 之前请致电rewind。此外,这应该提醒您必须始终检查函数调用的返回值,尤其是在这种情况下为fscanf
  • 谢谢凯勒姆!我以前从未听说过 rewind() 函数,它就像动态数组被正确填充一样。但是当我尝试使用 printf("%d", *codedPtr) 打印出指针数组中的第一个值时,它返回的值与从文件中读取的值不同。是我的语法错误还是有其他问题?
  • 代码中显示的printf("%d", *codedPtr);?那是在错误的地方。它需要 load_nums 之后而不是在当前显示之前(codePtr 在您调用printf 时甚至没有分配)。
  • 在C语言中,malloccallocrealloc的返回不需要强制转换,没有必要。见:Do I cast the result of malloc?

标签: c file pointers file-io pass-by-reference


【解决方案1】:

你让事情变得比他们需要的复杂得多。您正在为codedPtrdecodedPtr 动态分配存储空间,无需两次通过文件(一次用于计算整数,一次用于在分配后读取)。您的decode 比必要的复杂得多,并且存在逻辑错误。添加'0'(在这种情况下没有必要 - 尽管通常是将十进制 digit 转换为其 ASCII 字符值)

要寻址load_nums,请将返回类型更改为int *,并根据需要使用reallocload_nums 中分配codedPtr,以增加分配的内存块的大小。然后返回一个指向已分配内存块的指针,该内存块保存您的int 值。将ncount 作为指针传递(例如int *ncount),以便您可以使用读取的整数数更新该地址的值,以便在调用函数中返回计数(main() 此处)。

以这种方式进行分配可将文件 I/O 减少为单次通过文件(文件 I/O 是最耗时的操作之一)此外,您完全不需要 num_count() 函数.

将这些部分放在一起,您可以:

/* read integers from fp, dynamically allocating storage as needed,
 * return pointer to allocated block holding integers and make ncount
 * available through update pointer value.
 */
int *load_nums (FILE* fp, int *ncount)
{
    int *codedPtr, avail = 2;   /* declare pointer & no. to track allocated ints */
    *ncount = 0;                /* zero the value at ncount */

    /* allocate avail no. of int to codedPtr - validate EVERY allocation */
    if (!(codedPtr = malloc (avail * sizeof *codedPtr))) {
        perror ("malloc-codedPtr");
        return NULL;
    }

    while (fscanf (fp, "%d", &codedPtr[*ncount]) == 1) {    /* read each int */
        if (++(*ncount) == avail) { /* check if realloc needed (count == avail) */
            /* always realloc to a temporary pointer */
            void *tmp = realloc (codedPtr, 2 * avail * sizeof *codedPtr);
            if (!tmp) {             /* validate that realloc succeeds */
                perror ("realloc-codedPtr");
                return codedPtr;    /* original codedPtr vals available on failure */
            }
            codedPtr = tmp;         /* assign new block of mem to codedPtr */
            avail *= 2;             /* update avail with no. of int allocated */
        }
    }

    return codedPtr;    /* return pointer to allocated block of memory */
}

您可以将main() 中的函数称为codedPtr = load_nums (fp, &amp;numCount)。您可以将其包装在if(...) 语句中,以确定分配和读取是成功还是失败:

    int *codedPtr = NULL, numCount = 0;
    ...
    if (!(codedPtr = load_nums (fp, &numCount)))    /* read file/validate */
        return 1;

(无需从main() 传递codedPtr。您可以通过检查numCount &gt; 0 进一步验证——这留给您)

对于您的decode 函数,只需设置for 循环,使用两个循环变量从头到尾向中间迭代。这大大简化了事情,例如

void decode (int *codedPtr, int ncount, char *decodedPtr)
{
    /* loop from ends to middle adding values, + '0' NOT required */
    for (int i = 0, j = ncount - i - 1; i < j; i++, j--)
        decodedPtr[i] = codedPtr[i] + codedPtr[j];
}

i 从第一个整数值开始,j 在最后一个整数值。不要使用*(codePtr + i) 而应使用codePtr[i]——虽然等效,但索引符号更易于阅读)

main() 中,您可以选择打开作为程序第一个参数提供的文件,或者如果没有提供参数,则默认从stdin 读取(这是许多Linux 实用程序的工作方式)。添加一个简单的三元就足够了。无论您是在读取输入还是分配内存(或使用代码继续正确运行所必需的任何函数),都无法正确使用该函数,除非您检查返回 确定操作是成功还是失败。课程:验证、验证、验证...

总而言之,你可以这样做:

#include <stdio.h>
#include <stdlib.h>

/* read integers from fp, dynamically allocating storage as needed,
 * return pointer to allocated block holding integers and make ncount
 * available through update pointer value.
 */
int *load_nums (FILE* fp, int *ncount)
{
    int *codedPtr, avail = 2;   /* declare pointer & no. to track allocated ints */
    *ncount = 0;                /* zero the value at ncount */

    /* allocate avail no. of int to codedPtr - validate EVERY allocation */
    if (!(codedPtr = malloc (avail * sizeof *codedPtr))) {
        perror ("malloc-codedPtr");
        return NULL;
    }

    while (fscanf (fp, "%d", &codedPtr[*ncount]) == 1) {    /* read each int */
        if (++(*ncount) == avail) { /* check if realloc needed (count == avail) */
            /* always realloc to a temporary pointer */
            void *tmp = realloc (codedPtr, 2 * avail * sizeof *codedPtr);
            if (!tmp) {             /* validate that realloc succeeds */
                perror ("realloc-codedPtr");
                return codedPtr;    /* original codedPtr vals available on failure */
            }
            codedPtr = tmp;         /* assign new block of mem to codedPtr */
            avail *= 2;             /* update avail with no. of int allocated */
        }
    }

    return codedPtr;    /* return pointer to allocated block of memory */
}

void decode (int *codedPtr, int ncount, char *decodedPtr)
{
    /* loop from ends to middle adding values, + '0' NOT required */
    for (int i = 0, j = ncount - i - 1; i < j; i++, j--)
        decodedPtr[i] = codedPtr[i] + codedPtr[j];
}

int main(int argc, char *argv[]) {

    int *codedPtr = NULL, numCount = 0;
    char *decodedPtr = NULL;
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    if (!(codedPtr = load_nums (fp, &numCount)))    /* read file/validate */
        return 1;

    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);

    if (!(decodedPtr = malloc (numCount + 1))) {    /* allocate/validate */
        perror ("malloc-decodedPtr");               /* don't forget room for '\0' */
        return 1;
    }

    decode (codedPtr, numCount, decodedPtr);        /* decode the message */
    decodedPtr[numCount] = 0;                       /* nul-terminate */

    puts (decodedPtr);                              /* output decoded message */

    free (codedPtr);        /* don't forge to free what you allocate */
    free (decodedPtr);
}

使用/输出示例

测试你的程序,你发现解码后的消息是"Hello",例如

$ echo "-6 -76 53 -34 32 79 142 55 177 78" | ./bin/codedptr
Hello

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您都有 2 个职责:(1)始终保留指向起始地址的指针内存块,因此,(2) 当不再需要它时可以释放

您必须使用内存错误检查程序来确保您不会尝试访问内存或写入超出/超出分配块的边界,尝试读取或基于未初始化的值进行条件跳转,最后,以确认您释放了已分配的所有内存。

对于 Linux,valgrind 是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。

$ echo "-6 -76 53 -34 32 79 142 55 177 78" | valgrind ./bin/codedptr
==32184== Memcheck, a memory error detector
==32184== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==32184== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==32184== Command: ./bin/codedptr
==32184==
Hello
==32184==
==32184== HEAP SUMMARY:
==32184==     in use at exit: 0 bytes in 0 blocks
==32184==   total heap usage: 7 allocs, 7 frees, 5,251 bytes allocated
==32184==
==32184== All heap blocks were freed -- no leaks are possible
==32184==
==32184== For counts of detected and suppressed errors, rerun with: -v
==32184== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认您已释放已分配的所有内存并且没有内存错误。

查看一下,如果您还有其他问题,请告诉我。

【讨论】:

  • 非常感谢您的超级详细回复!我已经阅读了它,并从你所说的事情中学到了很多东西。我完全同意你的观点,当你像你一样有经验时,试图理解过于复杂/臃肿的代码一定会令人沮丧。这是针对大学课程的,标准严格规定了我们必须具备的功能以及每个功能必须具备的返回类型和过程,几乎没有偏离的余地。不过,我已经用你所说的来修复我的代码并感谢你的时间!
  • 很高兴它有帮助。祝你编码好运! (C 是一种令人难以置信的语言,无论您最终使用哪种语言编写,它都会使您成为更好的程序员。您将了解内存处理和低级语义——即使您使用的语言试图对您隐藏它们 @987654358 @看一看:What should I do when someone answers my question?
猜你喜欢
  • 1970-01-01
  • 2018-05-12
  • 2015-03-13
  • 1970-01-01
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多