Thread: When is a pointer a pointer but not a pointer but then a pointer

  1. #1
    Registered User
    Join Date
    Apr 2019
    Posts
    808

    When is a pointer a pointer but not a pointer but then a pointer

    i have the following
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void AddStrings( char *, char *, char * );
    
    int main()
    {
        unsigned long long x = 399, y = 399;
        char strNumberx[20] = { '\0' }, strNumbery[20] = { '\0' }, strResult[21] = { '\0' }; //strresult is 1 more incase of a carry
        sprintf( strNumberx, "%llu", x );
        sprintf( strNumbery, "%llu", y );
    
        AddStrings( strNumberx, strNumbery, strResult );
        printf("%s\n", strResult);
    
        printf("Just to prove you can index strings %c %d\n", strNumberx[0], strNumberx[0]);
    
        return 0;
    }
    
    void AddStrings( char *Numx, char *Numy, char *Result )
    {
        //it is assumed that numx and numy have the same number of digits
        int x = 0, y = 0, Carry = 0;
    
        for ( int Answer = 0, i = strlen( Numx ) - 1; i >= 0; i-- )
        {
            x = atoi( &Numx[i] );
            y = atoi( &Numy[i] );
            Carry = 0;
    
            Answer += x + y;
    
            if ( Answer > 9 )
            {
                int tmpNum = Answer;
                Answer %= 10;
                Carry = (tmpNum - Answer ) / 10;
            }
            sprintf( &Result[i], "%d", Answer );
    
            Answer = Carry;
        }
    }
    this is what the compiler says
    gcc -Wall -g -pedantic -Wall -std=c99 -c "/home/****/Documents/my code/add_2_strings/main.c" -o obj/Debug/main.o
    gcc -o bin/Debug/add_2_strings obj/Debug/main.o -lm
    Output file is bin/Debug/add_2_strings with size 18.15 KB
    Process terminated with status 0 (0 minute(s), 0 second(s))
    0 error(s), 0 warning(s) (0 minute(s), 0 second(s))
    issue is when i step through the function code on the first iteration x and y both = 9 which is correct how ever on the second and third iteration x and y = 99 or 399 on the third iteration

    i have tried replacing &Numx with Numx, *Numx and (*)Numx the first one produces a warning "|29|warning: passing argument 1 of ‘atoi’ makes pointer from integer without a cast [-Wint-conversion]|" 2nd and 3rd ones produce errors

  2. #2
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    atoi won't convert single digits.

  3. #3
    Registered User
    Join Date
    Apr 2019
    Posts
    808
    well how on earth am i supposed to add numbers like 15946249379839316505 and 50561393897394264951 accurately the result is too big for a unsigned long long and too many digits for a double to be accurate (more than 17)
    Last edited by cooper1200; 06-12-2023 at 08:59 AM. Reason: changed the numbers for one that is too big for ull

  4. #4
    Registered User
    Join Date
    Dec 2017
    Posts
    1,633
    atoi takes a string, i.e., a character array that has a '\0' in it to indicate the end of the active string data. You are trying to pass it single characters (but as a pointer, which actually indicates the beginning of a string). To just be a single character, the character right after the character you are trying to pass must be '\0'. Perhaps the best way to do this is:
    Code:
    char tmp[2] = {0};
    tmp[0] = Numx[i];
    int x = atoi(tmp);
    tmp[0] = Numy[i];
    int y = atoi(tmp);
    However, there is no reason to use atoi here. We usually just do this:
    Code:
    int x = Numx[i] - '0';
    int y = Numy[i] - '0';
    And instead of
    Code:
            sprintf( &Result[i], "%d", Answer );
    just do this
    Code:
            Result[i] = Answer + '0';
    So it might be written something like this:
    Code:
    void AddStrings( char *Numx, char *Numy, char *Result )
    {
        //it is assumed that numx and numy have the same number of digits
        for ( int Answer = 0, i = strlen( Numx ) - 1; i >= 0; i-- )
        {
            int x = Numx[i] - '0';
            int y = Numy[i] - '0';
            Answer += x + y;
            Result[i] = Answer % 10 + '0';
            Answer /= 10;
        }
    }
    But note that this will not be able to add, e.g., 999 + 399 = 1398 since you are writing the answer characters to the same position in result so the initial 1 in the answer would need to go 1 before the beginning of result.
    It's also obviously a problem that the input numbers need to have the same number of digits.

    To do this kind of thing it's easier to put the numbers into the strings "backwards" so that way things line up better. You just need to write input and output routines to reverse the strings.
    A little inaccuracy saves tons of explanation. - H.H. Munro

  5. #5
    Registered User
    Join Date
    Apr 2019
    Posts
    808
    to solve the input and carry issues i was going to write a function that prepends the relevant number to the string ie 312 + 4321 becomes 0312 + 4321

    The final object of the problem was to take numbers 1 through 9999 and reverse each number and add them together and see if they are palindromic or can be made to be by taking the result and reversing that up to 50 times

    i was trying this and when i tested 9999 as a maximum after 32 iterations it got to the above number hence i was side tracked onto this
    Last edited by cooper1200; 06-12-2023 at 09:41 AM.

  6. #6
    Registered User
    Join Date
    Apr 2019
    Posts
    808
    what i was banging on about
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int AddStrings( char *, char *, char * );
    void PrependString( char *, int );
    
    int main()
    {
        unsigned long long x = 1900, y = 900;
        char strNumberx[20] = { '\0' }, strNumbery[20] = { '\0' }, strResult[21] = { '\0' }; //strresult is 1 more incase of a carry
        sprintf( strNumberx, "%llu", x );
        sprintf( strNumbery, "%llu", y );
    
        if ( strlen( strNumberx ) != strlen( strNumbery ) )
        {
            while ( strlen( strNumberx ) < strlen( strNumbery ) )
                PrependString( strNumberx, 0 );
    
            while ( strlen( strNumbery ) < strlen( strNumberx ) )
                PrependString( strNumbery, 0 );
        }
    
        int Carry = AddStrings( strNumberx, strNumbery, strResult );
    
        if ( Carry )
        {
            PrependString( strResult, Carry );
        }
    
        printf("%s\n", strResult);
    
        return 0;
    }
    
    int AddStrings( char *Numx, char *Numy, char *Result )
    {
        //it is assumed that numx and numy have the same number of digits
        int Answer = 0;
    
        for ( int i = strlen( Numx ) - 1; i >= 0; i-- )
        {
            int x = Numx[i] - '0';
            int y = Numy[i] - '0';
            Answer += x + y;
            Result[i] = Answer % 10 + '0';
            Answer /= 10;
        }
    
        return Answer;
    }
    
    void PrependString( char *strPrePend, int x )
    {
        for ( int i = strlen( strPrePend ) - 1; i >= 0; i-- )
            strPrePend[i + 1] = strPrePend[i];
    
        strPrePend[0] = x + '0';
    }

  7. #7
    Registered User
    Join Date
    Apr 2019
    Posts
    808
    here is the finished article
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int AddStrings( char *Numx, char *Numy, char *Result )
    {
        //it is assumed that numx and numy have the same number of digits
        int Answer = 0;
    
        for ( int i = strlen( Numx ) - 1; i >= 0; i-- )
        {
            int x = Numx[i] - '0';
            int y = Numy[i] - '0';
            Answer += x + y;
            Result[i] = Answer % 10 + '0';
            Answer /= 10;
        }
    
        return Answer;
    }
    
    void PrependString( char *strPrePend, int x )
    {
        for ( int i = strlen( aaaaaaaaaaaaaaaaaaaaaaaaaaa ) - 1; i >= 0; i-- )
            strPrePend[i + 1] = strPrePend[i];
    
        strPrePend[0] = x + '0';
    }
    
    void ReverseString( char *Source, char *Result )
    {
        for ( int j = strlen( Source ) - 1, i = 0; i < strlen( Source ); i++, j-- )
            Result[j] = Source[i];
    }
    
    int isPalindrome( char *Number )
    {
        if ( Number[strlen( Number ) - 1] == '0' ) return 0;
    
        for ( int j = strlen( Number ) - 1, i = 0; i <= strlen( Number ) / 2; i++, j-- )
            if ( Number[i] != Number[j] ) return 0;
    
        return 1;
    }
    
    int main()
    {
        int LychrelNum = 0;
    
        for ( int i = 10; i < 10000; i++ )
        {
            char strNum[32] = { '\0' }, strResult[32] = { '\0' }, strReversed[32] = { '\0' };
    
            sprintf( strNum, "%d", i );
            ReverseString( strNum, strReversed );
    
            int Carry = AddStrings( strNum, strReversed, strResult );
    
            if ( Carry )
                PrependString( strResult, Carry );
    
            int Loopcnt = 1;
            while ( !isPalindrome( strResult ) && Loopcnt < 50 )
            {
                if ( strlen( strResult ) > 30 ) // check the next iteration will fit with poss carry
                {
                    printf("array overflow detected\n");
                    exit(1);
                }
    
                strcpy( strNum, strResult );
                ReverseString( strNum, strReversed );
                Carry = AddStrings( strNum, strReversed, strResult );
    
                if ( Carry )
                    PrependString( strResult, Carry );
    
                Loopcnt++;
            }
    
            if ( Loopcnt >= 50 ) LychrelNum++;
        }
    
        printf("%d\n", LychrelNum);
    
        return 0;
    }
    I have a memory management question. in my for loop in main i declare three char arrays as they are with in the loop i believe they exist with in the scope of the curly brackets. if this is the case does it create multiple copies of each variable or do they loose scope when the for loop iterates?

  8. #8
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,661
    They will be re-initialised (filled with zeros) each time around the loop.

    Aside from the initialisation cost, there is nothing else really to worry about.
    You're not going to be using up huge amounts of memory just by doing it.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  9. #9
    Registered User
    Join Date
    Dec 2017
    Posts
    1,633
    The initialization costs are entirely self-imposed as there is no need to initialize them at all (as long as the functions ReverseString and AddStrings are properly written to zero terminate the strings they make, which at the moment they aren't).

    It doesn't make much sense that the AddStrings function doesn't do all the work of adding the strings but offloads the task of prepending the final carry. It's easy enough to put it in the AddStrings function.

    I also happened to notice that there is some non-standard whitespace in the code, e.g., non-breaking spaces (UTF-8 C2 A0).
    Also, aaaaaaaaaaaaaaaaaaaaaaaaaaa?
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    void prependDigit( char *str, unsigned digit )
    {
        memmove(str + 1, str, strlen(str) + 1); // strlen + 1 to include '\0'
        *str = digit % 10 + '0'; // % 10 to ensure single digit
    }
     
    // Assumes numx and numy have the same number of digits.
    void addStrings( const char *numx, const char *numy, char *result )
    {
        int answer = 0, i = strlen( numx );
     
        result[i] = '\0'; // 0-terminate result string 
     
        while ( i-- > 0 )
        {
            answer += (numx[i] - '0') + (numy[i] - '0');
            result[i] = answer % 10 + '0';
            answer /= 10;
        }
     
        if ( answer )
            prependDigit( result, answer );
    }
     
    void reverseString( const char *source, char *result )
    {
        result += strlen( source );
        *result = '\0'; // properly 0-terminate string
        while ( *source )
            *--result = *source++;
    }
     
    int isPalindrome( const char *str )
    {
        for ( const char *end = str + strlen(str) - 1; str < end; )
            if ( *end-- != *str++ ) return 0;
        return 1;
    }
     
    int main()
    {
        int lychrelNum = 0;
     
        for ( int n = 1; n < 10000; ++n )
        {
            char num[64], result[64], reversed[64];
     
            sprintf( num, "%d", n );
            reverseString( num, reversed );
            addStrings( num, reversed, result );
     
            int i = 1;
            for ( ; !isPalindrome( result ) && i < 50; ++i )
            {
                strcpy( num, result );
                reverseString( num, reversed );
                addStrings( num, reversed, result );
            }
     
            if ( i == 50 ) ++lychrelNum;
        }
     
        printf( "%d\n", lychrelNum );
     
        return 0;
    }
    A little inaccuracy saves tons of explanation. - H.H. Munro

  10. #10
    Registered User
    Join Date
    Apr 2019
    Posts
    808
    i have just realised on my code in function prependstring i have a string of a's being passed to strlen rather than the variable name. this must of happened when i was cutting and pasting using control a to select all i will correct that now. Not sure what you mean by the non standard white space though .

    it wont let me edit the code sorry
    Last edited by cooper1200; 06-14-2023 at 11:09 PM.

  11. #11
    Registered User
    Join Date
    Apr 2019
    Posts
    808
    secondly this pointer thing is really beginning to grind my gears. this is about the 4th time that i have tried to do something and ended up with a bunch of war5nings about making pointers with out cast or expects int * but ... is int etc etc and then you or someone else does exactly what i tried and it works

    thirdly on line 34 you have *--Result then on line 40 you have *end-- i realize that one decrements before use and one after but surly *end-- would decrement the value end is pointing to not the address

    lastly having compared our versions i realize that my prepend function assumes that what ever is at the address strlen(strprepend) + 1 isn't needed or isn't out of bounds. Does memmove check whats there and that it is in bounds. If so does it move it somewhere else to a valid location and update strprepend

  12. #12
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,661
    You have to be careful when you have --, ++ and * all in the same expression.
    You have to be really careful when using them right next to each other (not separated by the variable).

    They occupy the same level in the precedence table.
    C Operator Precedence - cppreference.com

    Code:
    $ cat foo.c
    #include <stdio.h>
    
    int main(void) {
        char test[] = "02468";
        char *p = test;
        
        char c1 = *p++;
        printf("C=%c, p now points to %c\n", c1, *p);
        
        char c2 = ++*p;  // this changes the data, not the pointer
        printf("C=%c, p now points to %c\n", c2, *p);
    
        char c3 = *++p;
        printf("C=%c, p now points to %c\n", c3, *p);
        
        printf("test=%s\n", test);
        return 0;
    }
    $ gcc -g -Wall foo.c
    $ ./a.out 
    C=0, p now points to 2
    C=3, p now points to 3
    C=4, p now points to 4
    test=03468
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  13. #13
    Registered User
    Join Date
    Dec 2017
    Posts
    1,633
    and then you or someone else does exactly what i tried and it works
    Obviously you did not try "exactly" what I did.

    but surly *end-- would decrement the value end is pointing to not the address
    Why so surly (bad-tempered) ?
    It decrements end, not *end, due to precedence rules.

    strlen(strprepend) + 1 isn't needed or isn't out of bounds.
    strlen(str) + 1 is the location of the '\0'.
    I wanted to move it along with the rest of the string data.
    A little inaccuracy saves tons of explanation. - H.H. Munro

  14. #14
    Registered User
    Join Date
    Apr 2019
    Posts
    808
    in my version of prepend i had this
    Code:
    void PrependString( char *strPrePend, int x )
    {
        for ( int i = strlen( strprepend ) - 1; i >= 0; i-- )
    
            strPrePend[i + 1] = strPrePend[i];
     
        strPrePend[0] = x + '0';
    }
    as i subtracted 1 from the strlen( strprepend ) all i have done is loose the terminating \0 however lets suppose that strprepend[0] equates to address 0x000000 and the terminating \0 is at address 0x000010 and some other variable is stored at 0x000014 i assume that doing what i have done would mess up the other variable. if i had done what you did and used memmove would it avoid that

  15. #15
    Registered User
    Join Date
    Dec 2017
    Posts
    1,633
    i assume that doing what i have done would mess up the other variable.
    As long as the underlying char array is large enough there is no problem.
    In modern code we would always pass the total size of the underlying char array to a function to ensure we don't overrun it.

    The thing that is wrong with your version of PrependString (besides it misleading name since it is prepending a digit character, not a general string) is that it does not properly zero-terminate the string. It would be more correct to start i at strlen(str), not strlen(str)-1, so that the '\0' is also moved.

    I feel you are confused about the difference between a string and a char array.
    A char array is the underlying data structure within which a string is held.
    A string is a sequence of non-zero bytes followed by a zero byte, indicating the end of the "active" data in the string.
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    void prependDigit(char *str, size_t size, int x) {
        size_t len = strlen(str);
        if (len + 2 > size) {
            fprintf(stderr, "prependDigit: string overflow\n");
            exit(EXIT_FAILURE);
        }
        for (size_t i = len; i-- > 0; ) str[i + 1] = str[i];
        str[0] = x % 10 + '0';
    }
     
    int main() {
        char num[32] = "12345"; // if size was only 6 it would overflow
        prependDigit(num, sizeof num, 0);
        printf("%s\n", num);
        return 0;
    }
    Note that since size_t is unsigned you cannot say:
    Code:
        for (size_t i = len; i >= 0; --i)
    i will always be >= 0 and will simply wrap-around to its highest value if it is decremented when it is 0.
    A little inaccuracy saves tons of explanation. - H.H. Munro

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 1
    Last Post: 08-05-2018, 12:14 AM
  2. Replies: 4
    Last Post: 08-18-2015, 03:13 PM
  3. Replies: 8
    Last Post: 03-01-2015, 12:39 AM
  4. Replies: 3
    Last Post: 10-30-2009, 04:41 PM
  5. Replies: 4
    Last Post: 08-27-2007, 11:51 PM

Tags for this Thread