3

I'm dynamically allocating an array in main() to store the prime factors of any number calculated through a PrimeFactors() function I coded myself. Problem is, when the size of the array >= 4, the program displays what looks like undefined behavior; reading garbage values and whatnot. Works perfectly when size < 4.

I thought that maybe the array address was changing through the function and as a consequence main() would read garbage values, not knowing the "new" address of the array; and it seems like this is the case.

OS is Linux Ubuntu 18.04 LTS.

As i am learning I do not want to simply use vector and forget about this problem.

Why is this happening and what could I do to fix this ? I am clueless and it seems google and SO are as well.

Here's the main() code :

int main(int argc, char* argv[])
{
    unsigned long long n = atoll(argv[1]);

    unsigned long long* factors = malloc(sizeof(unsigned long long));
    if(factors == NULL)
    {
        printf("malloc failed to allocate factors\n");
        return -1;
    }
    else
        printf("First address in main() %p\n\n", (void *)&(factors[0]));

    unsigned short int* size = malloc(sizeof(unsigned short int));
    if(size == NULL)
    {
        printf("malloc failed to allocate size\n");
        return -1;
    }

    PrimeFactors(factors, size, n);

    printf("Last address in main() : %p\n", (void *)&(factors[0]));

    for(int i = 0; i < *size; ++i)
        printf("%llu\n", factors[i]);

    free(factors);
    free(size);
    return 0;
}

And here's the PrimeFactors() code :

int PrimeFactors (unsigned long long* factors, unsigned short int* size, unsigned long long n)
{
    printf("In PrimeFactors() :\n");
    *size = 2;
    factors = realloc(factors, (*size) * sizeof(unsigned long long));
    if (factors == NULL)
    {
        printf("realloc failed re-allocating factors array\n");
        return -1;
    }
    else
        printf("realloc() %d address in PrimeFactors() : %p\n", *size, (void *)&(factors[0]));

    if (IsPrime(n))
    {
        factors[0] = n;
        return 0;
    }

    unsigned short int factorsCount = 0;
    for (unsigned long long i = 2; i <= n; ++i)
    {
        while(n % i == 0)
        {
            if(factorsCount >= *size)
            {
                factors = realloc(factors, ++(*size) * sizeof(unsigned long long));
                if (factors == NULL)
                {
                    printf("realloc failed re-allocating factors array\n");
                    return -1;
                }
                else
                    printf("realloc() %d address : %p\n", *size, (void *)&(factors[0]));
            }
            factors[factorsCount] = i;
            ++factorsCount;

            n /= i;
        }
    }

    printf("last address in PrimeFactors() : %p\n", (void *)&(factors[0]));
    for(int i = 0; i < *size; ++i)
        printf("%llu\n", factors[i]);

    printf("Exiting PrimeFactors()...\n\n");
    return 0;
}

Compile flags :

gcc -Wall -Wshadow -Wpointer-arith main.c libMath.c -o learnlab.exe

Execute command :

./learnlab 80

Expected output :

First address in main() 0x5626aa180260

In PrimeFactors() :

realloc() 2 address in PrimeFactors() : 0x5626aa180260

realloc() 3 address : 0x5626aa180260

realloc() 4 address : 0x5626aa180260

realloc() 5 address : 0x5626aa180260

last address in PrimeFactors() : 0x5626aa180260

2

2

2

2

5

Exiting PrimeFactors()...

Last address in main() : 0x5626aa180260

2

2

2

2

5

Actual output :

First address in main() 0x5626aa180260

In PrimeFactors() :

realloc() 2 address in PrimeFactors() : 0x5626aa180260

realloc() 3 address : 0x5626aa180260

realloc() 4 address : 0x5626aa1812b0

realloc() 5 address : 0x5626aa1812b0

last address in PrimeFactors() : 0x5626aa1812b0

2

2

2

2

5

Exiting PrimeFactors()...

Last address in main() : 0x5626aa180260

0

2

2

4113

7233098161058900294

Sorry this is a long post, I couldn't really figure out a way to make it shorter. Thank you.

R2K
  • 56
  • 1
  • 5
  • 1
    Just for your information, but e.g. `factors` and `&factors[0]` is exactly the same. – Some programmer dude May 31 '18 at 07:13
  • 1
    Well, you are in luck. Since this is C -- you don't have the option of using `vector` (unless you write it yourself) `:)` (and very well done on your question) – David C. Rankin May 31 '18 at 07:13
  • 5
    As for your problem, remember that C only have pass-by-value. When you pass an argument to a function, it is copied. Any assignments you make to the local argument variable only modifies the copy, not the original. Please do some research about *emulating pass by reference in C* (which you already do with the `size` argument). – Some programmer dude May 31 '18 at 07:15
  • 4
    Basically you must call `unsigned long long **factors, ...` to be able to `realloc` in your function. Otherwise, the new address for `factors` is never seen in `main()` because your function receives a **copy of** the pointer. So you are required to pass its address, if you want to change that with `realloc` within the function and have the change seen in `main()` (right now you have a nice *memory leak*) – David C. Rankin May 31 '18 at 07:18
  • More dupes: https://stackoverflow.com/q/13431108/1848654, https://stackoverflow.com/q/29443151/1848654, https://stackoverflow.com/q/23994665/1848654 – melpomene May 31 '18 at 07:19
  • (I liked the 2nd link the best as Basile's answer was the most thorough and it references the other duplicates as well) – David C. Rankin May 31 '18 at 07:23
  • @DavidC.Rankin is right. My advice would be to get used to some debugging tools. In particular **valgrind** is very easy to use and would have given you good hints of what the problem was. Just compiling with `-g` flag and running `valgrind ./learnlab 80` does the job (after installing **valgrind** of course :)) – myradio May 31 '18 at 07:23
  • 1
    Oh and you don't need `malloc` to create pointers. Using e.g. `unsigned short size = 1;` and then use the address-of operator as in `&size` is enough (and what is usually done). – Some programmer dude May 31 '18 at 07:24
  • @DavidC.Rankin For future reference, a good canonical dupe for this FAQ is [Dynamic memory access only works inside function](https://stackoverflow.com/questions/39486797/dynamic-memory-access-only-works-inside-function). From the [C wiki FAQ](https://stackoverflow.com/tags/c/info). – Lundin May 31 '18 at 07:37
  • Thank you all for the comments, I will look into unsigned long long **factors as well as the dupes links I could not find when doing my own research. – R2K May 31 '18 at 07:37
  • @Kyo -- if none of those answers work for you (doubtful), feel free to request to `reopen` your question and explain what is still not working and we will be glad to help further. The purpose for marking the question as a duplicate is to prevent further scattering of related answers all over the StackOverflow site (not because of anything you did). Let us know, and good luck with your coding! – David C. Rankin May 31 '18 at 07:59
  • @DavidC.Rankin no of course your answers were of great help, i fixed it defining a `unsigned long long** factorsPtr = &factors;` and passing it to `PrimeFactors()` -- was that the correct way to go ? – R2K May 31 '18 at 08:03
  • Sure, but you can even skip the `long long** factorsPtr = &factors;` part and just pass `&factors` as a parameter to your function (it doesn't matter -- it does the same thing, you just use an additional variable). The key is to pass the **address of** `factors` to your function, and then `realloc (*factors, ...`. You are free to use an additional variable if it helps. (but it's just one more variable to keep track of `:)` – David C. Rankin May 31 '18 at 08:07

0 Answers0