0

I was trying to write a function to add primes to a array but my realloc kept failing. Can someone please look at my code and tell me why it is failing?

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

void makePrimes(int *primes, int *primesLength, int primeTarget);
int isPrime(int input);

int main(void)
{
    int *primes = (int *)(malloc(1));
    *primes = 2;
    int primesLength = 1;
    makePrimes(primes, &primesLength, 3);
    printf("%d", *(primes + 2));
    free(primes);
    return 0;
}

//* Finds the prime number that is the prime target number prime
//* (i.e 2 is the zeroth prime)
//! PRIME TARGET COUNTS FROM ZERO BUT  PRIMES LENGTH COUNTS FROM 1
void makePrimes(int *primes, int *primesLength, int primeTarget)
{
    // do nothing
    if (*primesLength >= primeTarget + 1)
        return;

    int lastValue = *(primes + (*primesLength - 1));
    while (primeTarget + 1 != *primesLength)
    {
        lastValue++;
        if (isPrime(lastValue))
        {
            (*primesLength)++;
            primes = realloc(primes, *primesLength * sizeof(int));
            *(primes + (*primesLength - 1));
        }
    }
}

//* Checks if a given integer is prime
int isPrime(int input)
{
    if (input == 0 || input == 1)
    {
        return 0;
    }
    int testFactor = 2;
    for (int iii = 0; iii < (input - 2); iii++)
    {
        if ((input % testFactor) == 0)
            return 0;
        testFactor++;
    }
    return 1;
}

I was expecting it to add one more available element to the array 'primes'. Using a debugger I found that primes was being set to NULL and not running the following line. Thanks.

Jman4546
  • 7
  • 4
  • Can you post whole code? – Tom Jun 26 '23 at 03:53
  • Is there a reason you're doing `int lastValue = *(primes + (*primesLength - 1));` instead of `int lastValue = primes[*primesLength - 1];` – Ryan Haining Jun 26 '23 at 03:54
  • I prefer pointer arithmetic. Just a personal preference. – Jman4546 Jun 26 '23 at 03:55
  • What is the expected outcome that you are looking for? – Tom Jun 26 '23 at 04:02
  • 1
    "_primes was being set to NULL_": `realloc` returns a null pointer value if the allocation failed, just as `malloc` does. So it was impossible to fulfil your allocation request for the specified size. That's also why `x = realloc(x, /*...*/);` isn't safe and can result in memory leaks. – user17732522 Jun 26 '23 at 04:02
  • Would you happen to know why that is happening in this code? – Jman4546 Jun 26 '23 at 04:07
  • 2
    @Jman4546 Start by compiling and running your program with warnings and sanitizers enabled, e.g. `-Wall -Wextra -Og -g -fsanitize=undefined,address` for GCC/Clang. It will tell you some problems. Then fix all of them. https://godbolt.org/z/cd6784sfc – user17732522 Jun 26 '23 at 04:11
  • According your problem and code that your post, I as a non-native English speaker that can't understand what do you want to do. – Tom Jun 26 '23 at 04:18
  • You mean, it reaches `realloc` sometimes? With that `int *primes=malloc(1); *primes=2` segfault? If so, thank God for alignment (that is probably what makes malloc allocate more than what you've asked for) – chrslg Jun 26 '23 at 04:19
  • If you want to change what primes point to, and you do due to realloc, you need to pass a `int **primes` to makePrimes. `primes = realloc(primes ...)` leaks memory on failure. You need to assign it to a temporary check for NULL and then decide what you want to do. `malloc(1)` allocates a byte but you want `sizeof int` bytes. – Allan Wind Jun 26 '23 at 04:29
  • ` *(primes + (*primesLength - 1));` doesn't do anything. – Allan Wind Jun 26 '23 at 04:32
  • When it comes to calculating primes, standard recommended reading: [Sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes). – Chris Jun 26 '23 at 04:34
  • [Specifically, what's dangerous about casting the result of malloc?](https://stackoverflow.com/questions/1565496/specifically-whats-dangerous-about-casting-the-result-of-malloc?r=SearchResults&s=3%7C122.5552) – Chris Jun 26 '23 at 04:35

2 Answers2

3
  1. You initially malloc() 1 byte instead of sizeof(int) == sizeof *primes which is 4 on my system.
  2. Prefer sizeof variable instead of type. This means you don't have to repeat the type which makes the code less brittle in the face of future (type) changes.
  3. Don't cast the void * from malloc().
  4. As you want to change primes (with realloc()) you need to pass in &primes to makePrimes().
  5. Use a temporary variable (tmp) for the return value of realloc() otherwise you leak memory on failure.
  6. Check return value from malloc() and realloc().
  7. Assign the value you found to primes.
  8. (Not fixed) You are making it harder for yourself by insisting on using pointer instead of array notation.
  9. (Not fixed) Consider using unsigned types. This eliminates error classes. For instance what if primeTarget is negative what is the expected output? What if a negative prime number is passed in?
  10. isPrime() can be optimized to only look at testFactor <= input / testFactor.
  11. (Not fixed) Consider reworking the interface so makePrimes() fills in the first value, too, instead of relying on caller to put in the right magic values for *primes and *primesLenght.
  12. (Not fixed) As you specify how many primes you want, you could just pre-allocate the primes array instead of growing the array member at a time.
  13. I made a printPrimes() function which is better for[debugging; you use your original code is just want the last one. If you just want the last prime why bother storing the rest in an array?
#include <stdio.h>
#include <stdlib.h>

int isPrime(int input) {
    if (input == 0 || input == 1)
        return 0;
    for (int testFactor = 2; testFactor <= input / testFactor; testFactor++) {
        if (!(input % testFactor))
            return 0;
    }
    return 1;
}

void makePrimes(int **primes, int *primesLength, int primeTarget) {
    if(*primesLength < 1)
        return;
    int lastValue = *(*primes + (*primesLength - 1));
    while (*primesLength < primeTarget) {
        lastValue++;
        if (isPrime(lastValue)) {
            int *tmp = realloc(*primes, sizeof *tmp * (*primesLength + 1));
            if(!tmp) {
                printf("realloc failed\n");
                return;
            }
            *primes = tmp;
            *((*primes) + *primesLength) = lastValue;
            (*primesLength)++;
        }
    }
}

void printPrimes(int n, int primes[n]) {
    if(n <= 0) return;
    printf("%d", *primes);
    for(int i = 1; i < n; i++)
        printf(", %d", *(primes+i));
    printf("\n");
}

int main(void) {
    int *primes = malloc(sizeof *primes);
    if(!primes) {
       printf("malloc failed\n");
       return 1;
    }
    *primes = 2;
    int primesLength = 1;
    makePrimes(&primes, &primesLength, 3);
    printPrimes(primesLength, primes);
    free(primes);
    return 0;
}

example run:

2, 3, 5
Allan Wind
  • 23,068
  • 5
  • 28
  • 38
  • "int *primes = malloc(sizeof *primes);" in your main function at the first line, what is "sizeof *primes" and *primes? where is initial of primes? – Tom Jun 26 '23 at 05:00
  • @Whozcry See point 2: `sizeof(int) == sizeof *primes` and the doesn't repeat the type name. `*primes = 2` assigns the initial prime number. Did I answer all your questions? – Allan Wind Jun 26 '23 at 05:05
  • gcc on the code above: `35:13: warning: dereference of possibly-NULL ‘primes’ [CWE-690] [-Wanalyzer-possible-null-dereference]` then `38:5: warning: heap-based buffer over-read [CWE-126] [-Wanalyzer-out-of-bounds]` `38:5: note: read of 4 bytes from after the end of the region` and several others – yvs2014 Jun 26 '23 at 05:09
  • @yvs2014 Not able to reproduce that compiler warning. – Allan Wind Jun 26 '23 at 05:11
  • @AllanWind that was from gcc13 called with `-Wall -fanalyzer` flags – yvs2014 Jun 26 '23 at 05:13
  • @yvs2014 Thanks. I added the NULL check. Also see point 11 (and 12). – Allan Wind Jun 26 '23 at 05:15
  • @Allan Wind, `for (int testFactor = 2; testFactor <= input / testFactor; testFactor++) {` is _a lot_ faster. – chux - Reinstate Monica Jun 26 '23 at 05:30
  • @AllanWind No. Your code "malloc(sizeof *primes);" in your main function at the first line, when program run malloc at main function at the first line, sizeof * **primes**, If I were gcc compiler, I don't know what is primes. – Tom Jun 26 '23 at 05:55
  • there's one thing with this style of sizing, sometimes it might be a little harder to read. Something like `sizeof * p` and `sizeof v * n` would make you stop a bit and think about precedence of sizeof in regard to other operators – yvs2014 Jun 26 '23 at 06:52
  • @yvs2014 You can use whatever rationalization you want. The point is if others need to read your code it's unusual, hence harder to read and more verbose. – Allan Wind Jun 26 '23 at 17:04
  • @chux-ReinstateMonica Added the suggested optimization. Thanks. – Allan Wind Jun 26 '23 at 17:07
  • 1
    @Whozcry `sizeof *primes` is evaluated at compile-time, it doesn't matter what value is stored, only what type it is. The type of `*primes` is `int` hence it will return `sizeof(int)` which is `4` on my system. – Allan Wind Jun 26 '23 at 17:08
  • @AllanWind It seems that the type of *primes is int.(I can't see the declaration or definition of 'primes' until at least the line with “int *primes = malloc(sizeof *primes);“). I think it's better to **declaration** or **definition** primes **before using it**. Alternatively, use sizeof(int) or sizeof(4) directly. – Tom Jun 26 '23 at 21:42
  • @Whozcry Not sure why are repeating what I told you. You are confused with respect to declaration and definition of variables. A variable declaration would be something like `extern int foo;`. It's preferable to define **initialized** variables; less repetition of code and you avoid the error class of using uninitialized variables. `sizeof(4)` is confusing, it only works because `4` is also an `int` so `sizeof(5) == sizeof(4)` which happens to be 4 on my system. The magic value `4` has nothing to do with type of `primes`. – Allan Wind Jun 26 '23 at 22:15
  • @Whozcry I think (someone correct if I am wrong) that you can do this because there is a sequence point between the evaluations of the function designator (`malloc` in this case) and actual arguments in a function call and the actual call. (6.5.2.2). – Allan Wind Jun 26 '23 at 22:29
  • @AllanWind My English is not very good, so perhaps I haven't expressed myself clearly. What is 6.5.2.2 ? – Tom Jun 27 '23 at 01:13
  • @Whozcry The relevant section of the C 2017 standard. – Allan Wind Jun 27 '23 at 04:22
  • @AllanWind I am not familiar with newer standards, as I am using C89. It's possible that you are using a more recent syntax which could be the source of the issue. – Tom Jun 27 '23 at 05:08
-1

in comments (text after //) there's why it was added or changed
upd: makePrimes' retvalue like realloc()

--- orig0.c
+++ orig1.c
@@ -1,16 +1,23 @@
 #include <stdio.h>
 #include <stdlib.h>
+#include <err.h>
 
-void makePrimes(int *primes, int *primesLength, int primeTarget);
+int* makePrimes(int *primes, int *primesLength, int primeTarget);
 int isPrime(int input);
 
 int main(void)
 {
-    int *primes = (int *)(malloc(1));
+    int *primes = (int *)(malloc(sizeof(int))); // allocate proper size
+    if (!primes) // test a result of malloc allocation
+        err(-1, "malloc");
     *primes = 2;
     int primesLength = 1;
-    makePrimes(primes, &primesLength, 3);
-    printf("%d", *(primes + 2));
+    primes = makePrimes(primes, &primesLength, 10);
+    if (primes) { // print all of them
+        for (int i = 0; i < primesLength; i++)
+            printf("%d ", primes[i]);
+        printf("\n");
+    }
     free(primes);
     return 0;
 }
@@ -18,11 +25,11 @@
 //* Finds the prime number that is the prime target number prime
 //* (i.e 2 is the zeroth prime)
 //! PRIME TARGET COUNTS FROM ZERO BUT  PRIMES LENGTH COUNTS FROM 1
-void makePrimes(int *primes, int *primesLength, int primeTarget)
+int* makePrimes(int *primes, int *primesLength, int primeTarget) // return pointer like realloc()
 {
     // do nothing
     if (*primesLength >= primeTarget + 1)
-        return;
+        return primes;
 
     int lastValue = *(primes + (*primesLength - 1));
     while (primeTarget + 1 != *primesLength)
@@ -30,11 +37,17 @@
         lastValue++;
         if (isPrime(lastValue))
         {
+            int *reallocated = realloc(primes, (*primesLength + 1) * sizeof(int));
+            if (!reallocated) { // test a result of realloc()
+                warn("realloc");
+                break;
+            }
+            primes = reallocated;
+            *(primes + *primesLength) = lastValue; // forgotten assignment
             (*primesLength)++;
-            primes = realloc(primes, *primesLength * sizeof(int));
-            *(primes + (*primesLength - 1));
         }
     }
+    return primes;
 }
 
 //* Checks if a given integer is prime
yvs2014
  • 303
  • 2
  • 7
  • 1
    Can't believe I tried to malloc 1. Thanks. – Jman4546 Jun 26 '23 at 04:33
  • @Jman4546 and add `if (primes) { ... }` around printing after makePrimes() call, let's say it can indicate with NULL pointer that function failed – yvs2014 Jun 26 '23 at 05:02
  • 1
    diff is harder to read than the final answer (imho). – Allan Wind Jun 26 '23 at 05:19
  • @AllanWind in general I agree with you, but with diff it's clearer to point out key points. Otherwise a bit long description needs to be written. Let's say it's a diff way) – yvs2014 Jun 26 '23 at 05:27
  • @yvs2014 Avoid sizing mistakes and maintenance: `primes = (int *)(malloc(sizeof(int)))` --> `primes = malloc(sizeof primes[0])`. – chux - Reinstate Monica Jun 26 '23 at 05:33
  • @chux-ReinstateMonica can you please make an example of mistake with sizing int (in the original string of code)? – yvs2014 Jun 26 '23 at 05:43
  • @yvs2014 `primes = (int *)(malloc(sizeof(int)))` is the wrong size to allocate should later code re-factor to `long long *primes`. `malloc(sizeof primes[0])` is less maintenance as it does not need an update as the type of `primes` changes.. – chux - Reinstate Monica Jun 26 '23 at 14:50
  • @chux-ReinstateMonica please point out where you have found "long long" in the orginal string of code? On maintenance in general: it's at least questionable reading that sizeof form combining with other operators – yvs2014 Jun 26 '23 at 17:32
  • @yvs2014 `long long` is not in the original. It is an example type of how code could change. Using the form `malloc(sizeof primes[0])` recues maintenance over the need to also modify `(int *)(malloc(sizeof(int)))`. – chux - Reinstate Monica Jun 26 '23 at 17:44
  • @chux-ReinstateMonica it depends on what imaginable code one needs to refactor and not related to pointing out the main things why something doesn't work in the original q – yvs2014 Jun 26 '23 at 18:07
  • 1
    OP's original error was in `primes = (int *)(malloc(1));`. Using `primes = malloc(sizeof *prime);`, instead of the incorrect magic number like `1` or spending effort to match the type with `(int *)(malloc(sizeof(int)))` would solve that issue, ease review and decrease maintenance. – chux - Reinstate Monica Jun 26 '23 at 18:37
  • @chux-ReinstateMonica my q was about where you've found sizing mistake in the sizeof(int) for the original string, not about couldve and wouldve. I'm not in discussing one's preferences – yvs2014 Jun 26 '23 at 21:56