How do i use a pointer in C to return a value?

Viewed 678

I am trying to make a pointer P return to me the index of the first occurrence of given number in an array using count function that i wrote (J's job is to return the number of occurrences), the idea here is that im trying to write a function that returns 2 values instead of 1 (P returns index of first occurrence and J returns its number) in such way:

int count(int h[],int *p,int size,int d)
{
    int j=0;
    int bool = 0;
    for(int i=0; i<size ;i++)
    {
        if(h[i]==d && bool == 0)
        {
            j++;
            bool = 1;
            p = &i;
        }
        else if(h[i]==d)
            j++;
    }
    if(bool==0)
    {
        j=-1;
        *p=-1;
    return j;
    }
    else
    return j;
}

void main () 
{
int j;
int *p = NULL;
int h[] = {1,2,3,4,5,1,1,3,4};

j = count(h, p , sizeof(h)/sizeof(int) , 1);
printf("%d %d", *p,j);
}

I am fully aware aware that instead of using int *p, i can simply use P and pass its address in such way:

void printarray(int h[],int size){
    for(int i=0;i < size ;i++)
    {
        printf(" %d ",h[i]);
    }
    printf("\n");
}

int count(int h[],int *p,int size,int d)
{
    int j=0;
    int bool = 0;
    for(int i=0; i<size ;i++)
    {
        if(h[i]==d && bool == 0)
        {
            j++;
            bool = 1;
            *p = i;
        }
        else if(h[i]==d)
            j++;
    }
    if(bool==0)
    {
        j=-1;
        *p=-1;
    return j;
    }
    else
    return j;
}

void main () 
{
int j;
int p;
int h[] = {1,2,3,4,5,1,1,3,4};

printarray(h,sizeof(h)/sizeof(int));

j = count(h, &p , sizeof(h)/sizeof(int) , 1);
printf("%d %d", p,j);
}

What Really intrigues me is that why does it show a segmentation fault when using the pointer ? or am i just wrong about using a pointer in the first place and i should pass the address of an int in case i want my function to return 2 values for me ?

4 Answers

That which is a pointer has to point to something. So when you wrote int *p = NULL; you didn't give it somewhere to point. You could have done int n; int *p = &n; and proceed to pass p.

But it gets bumpy from here. You tried to write p = &i; inside the function. While that's completely valid code, from that point, assigning to *p will mess up your loop control function. This is not what you want. In the "I know I can" section you have the more reasonable *p = i;.

I'm guessing you want to know how to pass something allocated in the function to its caller. You can in fact do this; there's standard library functions designed for this task. You can in fact write

int count(int h[],int **p,int size,int d)
{
    int j=0;
    int bool = 0;
    for(int i=0; i<size ;i++)
    {
        if(h[i]==d && bool == 0)
        {
            j++;
            bool = 1;
            *p = malloc(sizeof(int));
            **p = i;
        }
        else if(h[i]==d)
            j++;
    }
    if(bool==0)
    {
        j=-1;
        return j;
    }
    else
        return j;
}

void main () 
{
int j;
int *p = NULL;
int h[] = {1,2,3,4,5,1,1,3,4};

j = count(h, &p , sizeof(h)/sizeof(int) , 1);
if (p) {
    printf("%d %d", *p,j);
    free(p);
} else {
    printf("(no index) %d", j);
}
}

So what happened here is we have a pointer in main() that is initialized to point nowhere, may or may not be set in count to point to the index. If nothing is found, p will still point to NULL (nowhere). The function malloc() returns the newly allocated memory so that p has somewhere to point; and the function free returns it.

** is a simple idea that gives people headaches. Since a pointer is * we add another * when we want a pointer to a pointer, so we can change that pointer. Just don't ask how deep you can go. The answer is don't try. Two happens a lot; three requires a good justification, and more than three is almost never done. More than two really is a headache and nobody wants headaches on their headaches. The limit is way higher. Nobody goes there.

It probably seems rather pointless, and it is. malloc() is used to allocate entire arrays in the function (whose size isn't known until runtime) and return them to the caller. A single int just isn't worth managing like this (the pointer is bigger than it is), but for an array it makes sense. So much that in big programs, almost everything is on the heap; unlike the small programs everybody starts with where almost everything is on the stack (local variables are on the stack).

You thinking about what you want to do correctly, but you are over complicating how to do it a bit. Instead of all the if ... else ... statements, what if you just used a flag like what you call bool (but let's rename it to something that won't conflict with the type from stdbool.h), call it first_set instead. Initialize it with a value of 0 (false) to begin with, and then when you find the first occurrence, update first_set = 1;

If you step back and thing of what your primary check needs to be, all you are really worried about locating in your array is the first, and all remaining indexes where the given value (let's call val instead of d) is equal to the array element. (and since a count, a length and an array index cannot be negative, let's choose size_t as the type for the positive counting values). To track the number of occurrences, let's use occurs as the variable (helps keep things readable in long programs)

So all you really want to do is loop over each element in your array and check if the current element is equal to the value you are searching for. That is the primary concern from which all other actions can be taken. You can keep your parameters in the order you like, but I find it easier to think about the function (1) taking an array, (2) of a given number of elements, where I will find (3) the first occurrence or (4) a given value. (whatever makes sense to you, but is also readable to whoever works on your code later) So with a slight tweak of the ordering (at least for my old eyes), you could set up your function to loop over each element and locate all of the occurrences of a given value as:

size_t arr_occurrences (int *a, size_t nelem, size_t *first, int val)
{
    int first_set = 0;                              /* flag if 1st occurrence set */
    size_t occurs = 0;                              /* total no. of occurrences */
    
    for (size_t i = 0; i < nelem; i++) {            /* loop over each element */
        if (a[i] == val) {                          /* is it equal to val? */
            ...
            occurs += 1;                            /* increment no. of occurrences */
        }
    }
    
    return occurs;      /* return total number of occurrences */
}

The only thing left to add is how to set the first occurrence. While you talk in terms of locating the "address" of the first occurrences in the function, what I think you really mean is locating the "index" of the first occurrence within the array. You can convert the index back to an address in the calling function and not have to worry about passing something that can hold the address (which would require passing a pointer-to-pointer rather than a pointer-to int)

So the first time the test of a[i] == val tests true, you want to capture the index and save it, ensuring you don't change it for any of the other times a[i] == val tests true during your loop. You have your first_set flag that is currently set to 0 (false), so all you really need to capture your first index in the array where val occurs is:

            if (!first_set) {                       /* if first not set */
                *first = i;                         /* update value at address of first */
                first_set = 1;                      /* set first_set flag true */
            }

If you put that in place of the ... placeholder in the function above, you would have:

size_t arr_occurrences (int *a, size_t nelem, size_t *first, int val)
{
    int first_set = 0;                              /* flag if 1st occurrence set */
    size_t occurs = 0;                              /* total no. of occurrences */
    
    for (size_t i = 0; i < nelem; i++) {            /* loop over each element */
        if (a[i] == val) {                          /* is it equal to val? */
            if (!first_set) {                       /* if first not set */
                *first = i;                         /* update value at address of first */
                first_set = 1;                      /* set first_set flag true */
            }
            occurs += 1;                            /* increment no. of occurrences */
        }
    }
    
    return occurs;      /* return total number of occurrences */
}

Short and sweet, no else needed, just a check of your flag first_set and if it isn't set, capture the index and set first_set true so you don't change in during the remainder of your loop. Your function makes the index of the first occurrence of val available back in the calling function by updating the value at the address held by the pointer first. The function returns the total number of occurrences for val in your array so it too is available to the caller. (with a slight change of type to size_t)

(another benefit of using size_t on platforms where size_t is larger than int is your array (or allocated block) can hold more than INT_MAX values and your code will still work without risk of integer overflow if a count exceeds what can be represented by an int)

Putting it altogether in a short example that creates an array of 100 elements holding random values from -10 to 10 and then chooses the value to find by choosing a random value in that range, you could check that all works as expected as follows:

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

#define NELEM       100         /* if you need a constant, #define one (or more) */
#define PERROW       10
#define LO_LIM      -10
#define HI_LIM       10

int rand_in_range (int lowlim, int highlim)
{
    return rand() % (highlim - lowlim + 1) + lowlim;
}

void arr_fill_rand (int *a, size_t nelem, int lowlim, int highlim)
{
    for (size_t i = 0; i < nelem; i++)
        a[i] = rand_in_range (lowlim, highlim);
}

void arr_prn (int *a, size_t nelem, const char* fmt)
{
    const char *format = fmt != NULL ? fmt : " %d";
    
    for (size_t i = 0; i < nelem; i++) {
        if (i && i % PERROW == 0)
            putchar ('\n');
        printf (format, a[i]);
    }
    putchar ('\n');
}

size_t arr_occurrences (int *a, size_t nelem, size_t *first, int val)
{
    int first_set = 0;                              /* flag if 1st occurrence set */
    size_t occurs = 0;                              /* total no. of occurrences */
    
    for (size_t i = 0; i < nelem; i++) {            /* loop over each element */
        if (a[i] == val) {                          /* is it equal to val? */
            if (!first_set) {                       /* if first not set */
                *first = i;                         /* update value at address of first */
                first_set = 1;                      /* set first_set flag true */
            }
            occurs += 1;                            /* increment no. of occurrences */
        }
    }
    
    return occurs;      /* return total number of occurrences */
}

int main (void) {
    
    int arr[NELEM] = {0},                           /* array of NELEM set all zero */
        val = 0;                                    /* value to find */
    size_t  first = 0,                              /* index of first occurrence */
            occurs = 0;                             /* total no. of occurrences */
    
    srand (time(NULL));                             /* seed random number generator */
    
    val = rand_in_range (LO_LIM, HI_LIM);           /* random in LO to HI (inclusive) */
    
    arr_fill_rand (arr, NELEM, LO_LIM, HI_LIM);     /* fill with random values in range */
    arr_prn (arr, NELEM, " % 3d");
    
    occurs = arr_occurrences (arr, NELEM, &first, val);     /* get occurrences of val */
    
    printf ("\n%d occurs first in array of %d "             /* output results */
            "elements ranging from %d to %d at:\n"
            "  index   : %zu\n"
            "  address : %p\n"
            "and occurs a total of %zu times.\n",
            val, NELEM, LO_LIM, HI_LIM, 
            first, (void*)&arr[first], occurs);
}

(note: you never need more than one call to printf() (or fputs() or puts()) to provide on block of output, regardless of the number of lines it contains. You can keep things readable in longer output by breaking the string up into adjacent strings (newlines and other whitespace between them is ignored) and the compiler with create a single string to output from all adjacent string literals)

Example Use/Output

The program outputs the array and then outputs the results with the index of the first occurrence (and it's actual address if that is what you wanted) along with the total number of times the value of interest was found:

$ ./bin/arr_rand_1st_occ+count
   3 -10   2  10   2  -9   4   5  -3   7
  -4  -6  10  -4   8  -5   8   5 -10  10
  10   6  -3   2  -5   1   1   8  10  -1
   6   0  -1  -5  -1  10  -6 -10   4   1
   5 -10   6   5  -4   1   8  -8  -4   8
  -9  -7   2  -4   5   7   5  -7   3   4
   2  -4  -6  10   1   1   9   5   0   0
  -7  -6  -2   9   7   3  -2   4   3   4
   2   4   5  -9   8  -3   6   2   0  -2
  -6 -10   4  -2   8  -8   7   6   5  -4

8 occurs first in array of 100 elements ranging from -10 to 10 at:
  index   : 14
  address : 0x7ffea2fb5938
and occurs a total of 7 times.

The array is output with 10 values PERROW to make things easier to read. Remember when confirming the results, arrays are zero-indexed in C.

Last note is unless you are programming in a freestanding environment (without the benefit of any OS), the allowable declarations for main for are int main (void) and int main (int argc, char *argv[]) (which you will see written with the equivalent char **argv). See: C11 Standard - §5.1.2.2.1 Program startup(p1). See also: What should main() return in C and C++?

In a freestanding environment (such as programming on a microcontroller) without the benefit of an operating-system, the name and type of the function called at program startup are implementation-defined. See: C11 Standard - 5.1.2.1 Freestanding environment

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

Your program behaviour is undefined because it end up dereferencing a NULL pointer here:

    int *p = NULL;
    int h[] = {1,2,3,4,5,1,1,3,4};

    j = count(h, p , sizeof(h)/sizeof(int) , 1);
    printf("%d %d", *p,j);
                    ^^
                     |
              Dereferencing p which is a NULL pointer

The way you are passing the p pointer from main() function to count() function, you are passing NULL. That means, in context of your program, this

j = count(h, p , sizeof(h)/sizeof(int) , 1);

is equivalent to this

j = count(h, NULL , sizeof(h)/sizeof(int) , 1);

The count() function parameter p will receive NULL as argument i.e. it's equivalent to p = NULL (where p is count() function parameter).

Whatever you assign to p in count() function, you are actually modifying a local variable of count() function. No changes are going to reflect in pointer variable p of main() function and it will remain a NULL pointer.

Now, lets come to the count() function.

In this snip of code of count() function:

    for(int i=0; i<size ;i++)
    {
        if(h[i]==d && bool == 0)
        {
            j++;
            bool = 1;
            p = &i;
        }
        else if(h[i]==d)
            j++;
    }

the scope and life of loop variable i is within the for loop block. Once, the loop exit the lifetime of i is ended. JFYI - accessing an object outside of its lifetime is undefined behaviour. So, if p hold &i and out of loop body if your program access p then it will lead to undefined behaviour. Also, *p=-1; statement in count() will also lead to undefined behaviour because, in count() function p will be a NULL pointer if bool==0.

Remember, if a function is passing a pointer to another function as a argument and expecting that function to modify the value of pointer, which it receive as parameter, then the pointer should be pointing to a valid memory location before the calling function access and modify it's memory contents or the caller function should pass address of pointer as argument and let the calling function allocate memory dynamically to it so that it should be accessible from the caller function as well (which is main() function, in your case).

There are couple ways to achieve this.

Method I:
Pass a valid memory location to calling function.
Either pass address of a local variable of caller function as argument (this is same as your second code snippet)

int main (void) {
    int j;
    size_t res = 0;
    size_t *p = &res;     // p is pointing to res variable
    int h[] = {1, 2, 3, 4, 5, 1, 1, 3, 4};
    int num = 1;

    j = count (h, p, sizeof (h) / sizeof (int), num);
    if (j != -1) {
        printf ("%zu %d", *p, j);
    } else {
        printf ("%d not found\n", num);
    }
    return 0;
}

Or allocate memory to pointer in caller function and pass it

int main (void)
{
    int j;
    size_t *p = NULL;
    int h[] = {1, 2, 3, 4, 5, 1, 1, 3, 4};
    int num = 1;

    p = malloc (sizeof (size_t));

    if (p == NULL) {
        fprintf (stderr, "Failed to allocate memory");
        exit (EXIT_FAILURE); // or whatever you want to do in case of allocation failure
    }

    j = count (h, p, sizeof (h) / sizeof (int), num);
    if (j != -1) {
        printf ("%zu %d", *p, j);
    } else {
        printf ("%d not found\n", num);
    }

    free(p);

    return 0;
}

and the count() function implementation for both the above cases:

int count (int h[], size_t *p, size_t size, int d) {
    int j = 0;

    for (size_t i = 0; i < size ; ++i) {
        if(h[i] == d) {
            if (j == 0) {
                *p = i;
            }
            ++j;
        }
    }

    return j == 0 ? -1 : j;
}

Method II:
Pass the address of p and dynamically allocate memory to p in count() function before accessing it. Note that, objects with allocated storage duration live until they are destroyed by a call to free(). So, if count() function allocates memory to a pointer, whose address passed as argument to count() function from main() function, then that memory will be accessible in main() function as well via pointer variable p.

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

int count (int h[], size_t **p, size_t size, int d) {
    int j = 0;

    for(size_t i = 0; i < size ; ++i) {
        if(h[i] == d) {
            if ((j == 0) && (p != NULL)) {
                *p = malloc (sizeof (size_t));
                if (p == NULL) {
                    fprintf (stderr, "Failed to allocate memory");
                    exit (EXIT_FAILURE);
                }
                **p = i;
            }
            ++j;
        }
    }

    return j == 0 ? -1 : j;
}

int main (void) {
    int retval;
    size_t *p = NULL;
    int h[] = {1, 2, 3, 4, 5, 1, 1, 3, 4};
    int num = 3;

    retval = count (h, &p, sizeof (h) / sizeof (int), num);
    if (retval != -1) {
        printf ("%zu %d\n", *p, retval);
    } else {
        printf ("%d not found\n", num);
    }

    // free memory once you are done with it
    if (p != NULL) {
        free (p);
    }

    return 0;
}

Additional:

  • Using void as return type of main function is not as per standards. The return type of main function should be int.

You are trying to return pointer pointing to a place which will be no longer valid once the loop completed it's execution, either declare i outside for or don't use the pointer part, as dereferencing a null pointer is illegal, also, you get segmentation fault when you try to access memory you have no business with and no permission to read or write, in this case, pointing to a memory location which is out of scope.

Instead of returning a pointer,simply create a local array of same type in the function, first index for first occurence of the given number, and second index for the total occurence, and return the array but make sure the return type should be pointer of same type.

edit: thanks Mr. Joshua, for pointing out the mistake.

Related