简体   繁体   中英

C Program - reading integers from a file and decoding secret message

Hey:) I need some help with my code, which I think is mostly correct but I am having trouble figuring out where I am going wrong.

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

I added some print functions to trouble shoot, and during the load_nums function the printf functions continuously prints 0's, it is not reading in the correct integer values from the file pointed to.

Could any of you help particularly with the load_nums function? Thank you all and let me know if you need any extra information. "-6 -76 53 -34 32 79 142 55 177 78" is what is in the file pointed to.

You are making things much more complicated than they need to be. You are dynamically allocating storage for both codedPtr and decodedPtr , there is no need to make two passes through the file (one to count integers, and one to read after allocation). Your decode is much more complex than necessary and there is a logic error. Adding '0' (it's not necessary in this case -- though normally it is to convert a decimal digit to its ASCII character value)

To address load_nums , change the return type to int * and allocate for codedPtr within load_nums using realloc as needed to increase the size of your allocated block of memory. Then return a pointer to the allocated block of memory holding your int values. Pass ncount as a pointer (eg int *ncount ) so you can update the value at that address with the number of integers read so that the count is available back in the calling function ( main() here).

Approaching allocation in this manner reduces your file I/O to a single-pass through the file (and file I/O is one of the most time consuming operations) Further, you completely eliminate the need for a num_count() function.

Putting those pieces together, you could do:

/* 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 */
}

You would call the function in main() as, codedPtr = load_nums (fp, &numCount) . You can wrap it in an if(...) statement to determine whether the allocation and read succeeded or failed:

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

(there is no need to pass codedPtr from main() . You can further validate by checking numCount > 0 -- that is left to you)

For your decode function, simply set up the for loop use two loop variables to iterate from the beginning and end towards the middle. This greatly simplifies things, eg

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 starts at the first integer value and j at the last. Don't use *(codePtr + i) instead use codePtr[i] -- though equivalent, index notation is easier to read)

In main() you can alternatively open the file provided as the first argument to your program or read from stdin by default if no argument is provided (this is the way many Linux utilities work). Adding a simple ternary is all you need. Whether you are reading input or allocating memory (or using any function that is necessary for the continued correct operation of your code), you cannot use that function correctly unless you check the return to determine if the operation succeeded or failed. Lesson: validate, validate, validate... .

Putting it altogether, you could do:

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

Example Use/Output

Testing your program, you find the decoded message is "Hello" , eg

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

Memory Use/Error Check

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ 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)

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

Look things over and let me know if you have further questions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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