Thread: program adding numbers using desired base doesn't show any output

  1. #1
    Registered User
    Join Date
    Nov 2011
    Posts
    44

    program adding numbers using desired base doesn't show any output

    Hello, I have a problem with an "add calculator".
    Valgrind reports no memory errors, no errors from compiler but the program doesn't show any output despite the printf - "Result is zero".

    All pointers, and variables are (n my opinion) correctly initialized.
    getnum function gets a number, returns a pointer to char *,
    add function processes two numbers as strings, returns result which is a pointer to char (char *) as well.

    Trying to work it out for the past 4 hours and can't find a mistake.
    Thanks in advance!

    Here's the code (sorry, it's quite long):

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    #include <string.h>
    
    #define MAX(A,B) ((A)>(B) ? (A) : (B))
    
    char *getnum(FILE *infile, int base)
    {
        int len = 10;
        int c;
        int pos = 0;
        char *num = NULL; 
        char *tmpnum = NULL;
        
        num = malloc(sizeof(char)*len);
        if(num == NULL) {
            perror("malloc fail");
            return NULL;
        }
        while (((c = fgetc(infile)) != EOF) && (isalnum(c))) {
            num[pos++] = c;
            if (isdigit(c)) {
                if (c >= base + '0') {
                    fprintf(stderr, "Wrong base\n");
                    free(num);
                    return NULL;
                }
            }
            else if (isalpha(c)) {
                if ( c >= base + '0' + 7) {
                    fprintf(stderr, "Wrong base, expected 16\n");
                    free(num);
                    return NULL; 
                }
            }
            if (pos >= len) {
                len *= 2;
                tmpnum = realloc(num, len*sizeof(char));
                if (tmpnum == NULL) {
                    perror("realloc fail");
                    free(num);
                    return NULL;
                }
                else num = tmpnum;
            }
        }
        num[pos] = '\0';
        return num;            
    }
    
    char *add(char *no1, char *no2, int base)
    {
        int len1, len2, length, i, j, pos, adder = 0;
        char *result = NULL; 
        char *tmp1 = NULL;
        char *tmp2 = NULL;
        
        len1 = strlen(no1);
        len2 = strlen(no2);
        length = MAX(len1, len2);
        if (len1 == 1 && len2 == 1) {
            length++;
        }    
        result = malloc((length + 1)*sizeof(char));
        if (result == NULL) {
            perror("malloc fail");
            free(result);
            return NULL;
        }
        tmp1 = realloc(no1, (length + 1)*sizeof(char));             
        if (tmp1 == NULL) {
            perror("realloc fail");
            free(result);
            return NULL;
        } else {
            no1 = tmp1;
        }
        tmp2 = realloc(no2, (length + 1)*sizeof(char));                
        if (tmp2 == NULL) {
            perror("realloc fail");
            free(result);
            free(no1);
            return NULL;
        } else {
            no2 = tmp2;
        }
        for (i = length - 1, j = len1 - 1; j >= 0; i--, j--) {
            no1[i] = no1[j];
        }
        for (i = 0; i < (length - len1); i++) {
            no1[i] = '0';
        }
        no1[length] = '\0';
    
        for (i = length - 1, j = len2 - 1; j >= 0; i--, j--) {
            no2[i] = no2[j];
        }
        for (i = 0; i < (length - len2); i++) {
            no2[i] = '0';
        }
        no2[length] = '\0';
        
        pos = length - 1;
        
        while (pos >= 0) {
            if (no1[pos] > '9') {
                no1[pos] -=7;
            }            
            if (no2[pos] > '9') {
                no2[pos] -=7;
            }
            result[pos] = no1[pos] + no2[pos] + adder - '0';
            adder = 0;
            if (result[pos] >= (base + '0')) {
                result[pos] -= base;
                adder = 1;
            }
            if (result[pos] > '9') {
                result[pos] += 7;
            }
            pos--;
        }
        result[length] = '\0';
        free(no1);
        free(no2);
        return result;
    }    
        
    int main(int argc, char **argv)
    {
        FILE *infile = NULL;
        char *number1 = NULL;
        char *number2 = NULL;
        char *result = NULL;
        int base, i, j = 0, length, count = 0;
        
        if (argc != 3) {
            printf("usage: ./add infile.txt base\n");
            return -1;
        }
        infile = fopen(argv[1], "r");
        if (infile == NULL) {
            perror("infile open failed");
            return -1;
        }
        base  = atoi(argv[2]);
        while (!feof(infile)) {
            number1 = getnum(infile, base);
            if (number1 == NULL) {
                fclose(infile);
                return -1;
            }
            number2 = getnum(infile, base);
            if (number2 == NULL) {
                fclose(infile);
                free(number1);
                break;                             /* or return ? */
            }                
            break;
        }
        printf("Base is %d\n", base);
        result = add(number1, number2, base);
        if (result == NULL) {
            free(number1);
            free(number2);
            fclose(infile);
            return -1;
        }
        length = strlen(result);
        for (i = 0; i <= length - 1; i++) {
            if (result[i] == '0') {
                count++;
            }
        }
        if (count == length) {
            printf("Result is 0\n");
            free(result);
            result = NULL;                     /* arguable */
            fclose(infile);
            return 0;
        }
        for (i = 0; i <= length - 1; i++) {
            if (result[i] != '0') {
                break;
            }
        }
        for (j = i; j == (length - 1); j++) {
            printf("Result is: %s\n", &result[j]);
            break;
        }
        free(result);
        result = NULL;
        fclose(infile);
        return 0;
    }

  2. #2
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    The main problem with your code is this freaky statement:
    Code:
     for (j = i; j == (length - 1); j++) {
    It say to keep looking WHILE j equals length - 1, which will not be true at first, so the body is never executed. Presumaly you want j < length.

    EDIT: And in future, please say how to run the program and the structure of the input file (providing one is nice).

    Actually, I should also mention that your main loop is very very wrong. It won't loop through all the numbers in the file, but only get the last two for processing.
    Last edited by oogabooga; 08-28-2012 at 11:31 AM.
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  3. #3
    Registered User
    Join Date
    Nov 2011
    Posts
    44
    OMG oogabooga You helped me a lot!
    I'd never get to that, I was expecting memory management is incorrect, and I was looking into it mainly.
    The program is supposed to run as
    Code:
    ./name input.txt base
    , where instead of base you put desired base eg. 2 or 10.
    Actually, I should also mention that your main loop is very very wrong. It won't loop through all the numbers in the file, but only get the last two for processing.
    Hmm after giving it much thought I'd suspect it of processing only two numbers from the beginning, not from the end?

  4. #4
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    Quote Originally Posted by Pole View Post
    Hmm after giving it much thought I'd suspect it of processing only two numbers from the beginning, not from the end?
    You're right! I didn't see the break at the end there. (And even without it, it will actually not process any numbers at all!) Still, it's probably not what you want. Presumably you want that loop to control most of the code that comes after it so that each group of two numbers is added.

    Once you've extended the body of the loop, a better loop condition is:
    Code:
    while ((number1 = getnum(infile, base)) != NULL) {
    //....
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  5. #5
    Registered User
    Join Date
    Nov 2011
    Posts
    44
    Thanks for reply. I have another question though. How to implement contnuous search and assignment for new numbers read from file. I mean, firstly assign
    Code:
    number = getnum(infile, base)
    and then how to pass that varying number of arguments to add function? Because it only operates on two numbers at a time.
    If anybody knows, please provide me with information, I don't demand code or even pseudocode; even suggestion is VERY MUCH appreciated.

  6. #6
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    If I understand your question correctly, you could write the function to receive a variable number of arguments. Or you could store the values you want in an array, with the next element of that array a key that indicates it's the end of arguments, and pass that array to your function.

  7. #7
    Registered User
    Join Date
    Nov 2011
    Posts
    44
    Quote Originally Posted by Matticus View Post
    If I understand your question correctly, you could write the function to receive a variable number of arguments. Or you could store the values you want in an array, with the next element of that array a key that indicates it's the end of arguments, and pass that array to your function.
    If I decide to store it in an array that would be a two-dimensional array. Then how to pass those strings, two at a time, and keep the result of the previous operation as one of the parameters passed to add function?

  8. #8
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    I'm also taking a guess at what you actually mean, so this might not be what you want but...

    You could pass the first two numbers to the add function and get the result. Then read the next number and pass that and the result to add. Then read the next number, etc. When you're out of numbers, print the result.
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  9. #9
    Registered User
    Join Date
    Nov 2011
    Posts
    44
    Quote Originally Posted by oogabooga View Post
    I'm also taking a guess at what you actually mean, so this might not be what you want but...

    You could pass the first two numbers to the add function and get the result. Then read the next number and pass that and the result to add. Then read the next number, etc. When you're out of numbers, print the result.
    I did as You advised. However, now program hangs after invoking, and I have to press Ctrl+C to stop it.
    I only changed main(), posting it below:
    Code:
    int main(int argc, char **argv)
    {
        FILE *infile = NULL;
        char *number1 = NULL;
        char *number2 = NULL;
        char *number_next = NULL;
        char *result = NULL;
        
        int base, length, count = 0;
        
        if (argc != 3) {
            printf("usage: ./add infile.txt base\n");
            return -1;
        }
        infile = fopen(argv[1], "r");
        if (infile == NULL) {
            perror("infile open failed");
            return -1;
        }
        base  = atoi(argv[2]);
        while (feof(infile) == 0) {
            number1 = getnum(infile, base);
            if (number1 == NULL) {
                fclose(infile);
                return -1;
            }
            number2 = getnum(infile, base);
            if (number2 == NULL) {
                fclose(infile);                    
                free(number1);                        
                return -1;                             /* or  BREAK ? */
            }                
            break;
        }
        
        result = add(number1, number2, base);
        if (result == NULL) {
            free(number1);
            free(number2);
            fclose(infile);
            return -1;
        }
        /*result_update = add(result, number3, base);*/
        while (((number_next = getnum(infile, base)) != NULL) || (feof(infile) == 0)) {
            result = add(result, number_next, base);
        }    
            
        
        length = strlen(result);
        
        for (count = 0; count < length; count++) {
            if (result[count] != '0')
                break;
        }
        if (count == length) {
            printf("Result is 0\n");
            free(result);
            result = NULL;                     /* arguable */
            fclose(infile);
            return 0;
        }
        printf("Result is: %s\n", &result[count]);
        printf("Base is %d\n", base);
        free(result);
        result = NULL;
        fclose(infile);
        return 0;
    }

  10. #10
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    Code:
    while (((number_next = getnum(infile, base)) != NULL) || (feof(infile) == 0))
    I suspect you have the wrong logic here. But I can't compile this code to verify without the associated files.
    Last edited by Matticus; 08-29-2012 at 07:28 AM.

  11. #11
    Registered User
    Join Date
    Nov 2011
    Posts
    44
    Quote Originally Posted by oogabooga View Post
    You're right! I didn't see the break at the end there. (And even without it, it will actually not process any numbers at all!) Still, it's probably not what you want. Presumably you want that loop to control most of the code that comes after it so that each group of two numbers is added.

    Once you've extended the body of the loop, a better loop condition is:
    Code:
    while ((number1 = getnum(infile, base)) != NULL) {
    //....
    @Matticus - I got it working, thank You!


    @oogabooga I quoted your post, why did you suggest that change in while condition? Is the one presented in the code in the first post prone to some overflow or bugs?

  12. #12
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    It's considered best to control a file-reading loop by the return value of the reading function instead of by feof. But that really depends on how you've written your getnum routine. If you have a solution that works you may as well stick with it.

    The code you posted in post#9 seems to have a memory leak. This
    Code:
    result = add(result, number_next, base);
    will return a new chunk of memory to result, but is not freeing the old memory pointed to by result.

    Also in post#9, the first while loop in main is not a loop at all, since it ends in a break (i.e., it can only execute it's block of code once). At best it's an if statement, except that it's condition will always be true when it is first hit, so it's kind of pointless.

    I was thinking of a loop something like this (I've removed the error handling of a NULL return for clarity, but you need to put it in) :
    Code:
    number1 = getnum(infile, base);
    while ((number2 = getnum(infile, base) != NULL /*and whatever*/) {
        result = add(number1, number2, base);
        free(number1);
        free(number2);
        number1 = result;
    }
    Does your program work with input like this (base 10) ?
    1111
    9999

    How about this (base 16) ?
    aaaa
    cccc
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  13. #13
    Registered User
    Join Date
    Nov 2011
    Posts
    44
    will return a new chunk of memory to result, but is not freeing the old memory pointed to by result.
    valgrind -v gives me no errors, so I guess no memory leak there.
    Also in post#9, the first while loop in main is not a loop at all, since it ends in a break (i.e., it can only execute it's block of code once). At best it's an if statement, except that it's condition will always be true when it is first hit, so it's kind of pointless.
    Fixed, thank You.
    But, condition
    Code:
    (feof(infile) == 0) 
    is not always true. What if a file would be empty?
    Does your program work with input like this (base 10) ?
    1111
    9999

    How about this (base 16) ?
    aaaa
    cccc

    Yeah, I got it already, change length assignment

  14. #14
    Registered User
    Join Date
    Aug 2005
    Location
    Austria
    Posts
    1,990
    Quote Originally Posted by Pole View Post
    But, condition
    Code:
    (feof(infile) == 0) 
    is not always true. What if a file would be empty?
    It would still not be true before you failed to read from the file.

    That is why it is better to use the return from a read function to control loops.

    Kurt

  15. #15
    Registered User
    Join Date
    Nov 2011
    Posts
    44
    Quote Originally Posted by ZuK View Post
    It would still not be true before you failed to read from the file.

    That is why it is better to use the return from a read function to control loops.

    Kurt
    So I guess I should add
    Code:
    ((feof(infile) == 0) || (number1 = getnum(infile, base) != NULL )) 
    number1 instead of number 2, as @oogabooga said.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Not Getting Desired Output
    By DevoAjit in forum C Programming
    Replies: 2
    Last Post: 03-09-2012, 05:46 AM
  2. Program doesn't show up
    By Krasimir11 in forum C Programming
    Replies: 7
    Last Post: 01-05-2006, 06:21 PM
  3. Replies: 3
    Last Post: 01-08-2004, 09:43 PM
  4. not getting the desired output. WHY???
    By KristTlove in forum C++ Programming
    Replies: 4
    Last Post: 11-06-2003, 02:08 PM
  5. adding base n numbers
    By doogle in forum C++ Programming
    Replies: 4
    Last Post: 11-11-2002, 11:23 AM