1

I have a requirement to pass some sort of reference pointer (not allocating space for it yet in calling function). I want malloc call in called function. There could be any number of objects present and pointed to by the pointer (like (pointer+index)=malloc(..)) to allocate space in called function. And also in the calling function I need the ability to access and modify objects like (pointer+index)->member=some_val or pointer[index]->member=some_val. Is this possible?

This is what I tried:

struct abc{ char *c;};

void abc(struct abc *t);
void main() {
    struct abc *k;
    printf("%s",k->c);
}
void abc(struct abc *t){
    t=(struct abc *)malloc(sizeof(struct abc));
    *t->c="hello";
}
kaylum
  • 13,833
  • 2
  • 22
  • 31
user786
  • 3,902
  • 4
  • 40
  • 72
  • Does this answer your question? [Changing address contained by pointer using function](https://stackoverflow.com/questions/13431108/changing-address-contained-by-pointer-using-function) – kaylum Mar 13 '21 at 07:29
  • Or this? [C Programming: malloc() inside another function](https://stackoverflow.com/q/2838038/) – jamesdlin Mar 13 '21 at 07:30
  • 1
    I think that is what you are asking though it isn't entirely clear. You can pass in a `struct abc **t_p` and then return the allocated memory to the caller as such: `*t_p = malloc(..);`. In the caller it would be `struct abc *k; abc(&k);` – kaylum Mar 13 '21 at 07:31
  • @jamesdlin this line in called function in my example is not compiling `t->c="hello";` How can I access in both called and calling function members of objects pointed to by pointer reference? – user786 Mar 13 '21 at 07:36
  • @kaylum same as above comment – user786 Mar 13 '21 at 07:37
  • 1
    `(*t_p)->c = "hello";` Note: you have to change the function paramter to `abc **t_p` if you want the caller to be able to get a reference to the allocated memory, That is, you need to pass in a pointer to the pointer. – kaylum Mar 13 '21 at 07:38
  • @kaylum thanks, few words of explanation may be? – user786 Mar 13 '21 at 07:40
  • @kaylum what if I keep the parameter same but assign the parameter passed pointer to `struct abc **t_p=&parameter_pointer` will it work? – user786 Mar 13 '21 at 07:46
  • When you pass a pointer to a function, the function receives a copy of the pointer. You are allocating and assigning the result to a copy of the original pointer. Change to `void abc(struct abc **t)` and call with `*t=malloc(sizeof **t);` and `(*t)->c = "hello";` Or just declare `struct abc *abc (void) { struct abc * t = malloc (sizeof *t); t->c = "hello"; return t; }` and assign `struct abc *k = abc();` in main. Note you need to validate the return of `malloc()` each time and you do not need to cast the return of `malloc()` in C (you do in C++). – David C. Rankin Mar 13 '21 at 08:07
  • In C, there is no need to cast the return of `malloc`, it is unnecessary. See: [Do I cast the result of malloc?](http://stackoverflow.com/q/605845/995714) Also, since you are assigning the address of a string-literal to `c`, you cannot modify the contents of the memory pointed to by `c`. – David C. Rankin Mar 13 '21 at 08:08
  • @DavidC.Rankin `Also, since you are assigning the address of a string-literal to c, you cannot modify the contents of the memory pointed to by c` what this mean but I am able to modify it, I checked in main tried `k->c=some_val` it worked – user786 Mar 13 '21 at 08:36
  • A Sting-Literal, e.g. `"hello"` is non-mutable. For most executable types, string literals are place in the `.rodata` section of the executable (read-only data). So if you attempt `t->c[0] = 'j';` attempting to change `"hello"` to `"jello"` you will most likely SegFault. (there are non-standard compilers that allow modification of string literals... but note the **non-** part) – David C. Rankin Mar 13 '21 at 08:41
  • @DavidC.Rankin so you are saying if I plan to change the value of `c` (initially assiged string literal) the segfault can occur. if yes then how exactly I assign some string like `hello` in abc and change it to `jello` in main or abc – user786 Mar 13 '21 at 08:49
  • 1
    Yes, you can change where the pointer points (e.g. `t->c = "new hello";`), but you cannot change the content of the string literal itself. (e.g. `t->c[1] = 'o';` attempting to change `"new"` to `"now"` -- BOOM SegFault) If you want `c` mutable, you need to allocate storage for it, e.g. `t->c = malloc (strlen ("hello") + 1);` and then copy `strcpy (t->c, "hello");` Now you have a mutable copy of the string-literal you can freely modify. – David C. Rankin Mar 13 '21 at 08:51

1 Answers1

3

Continuing from the comments, your initial problem is you are passing a pointer by-value to your function abc(). The function will receive a copy of the pointer and any changes you make to the copy of the pointer in function abc() will be lost when the function returns. (you also incorrectly dereference *t->c = "hello";)

You have two optons:

Pass the Address Of the Pointer

When you pass the address of the pointer to your function, you will pass a pointer-to-pointer holding the original address of your pointer from main() to your function. You can then change the address held by the original pointer by updating the address held by the original pointer in your abc() function. For example:

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

struct abc {
    char *c;
};

void abc (struct abc **t)           /* use pointer-to-poitner to struct abc */
{
    *t = malloc (sizeof **t);       /* allocate */
    
    if (*t == NULL) {               /* validate */
        perror ("malloc-*t");
        return;
    }
    
    (*t)->c = "hello";              /* assign on success */
}

int main (void) {       /* void main only valid in freestanding environment */
    
    struct abc *k;
    
    abc (&k);                       /* call abc passing address of k */
    
    if (k) {                        /* validate k not NULL */
        puts (k->c);
        free (k);                   /* don't forget to free what you allocate */
    }
}

(note: you must validate EVERY allocation by checking the return from malloc() is not NULL)

Also note the use of the derefernced pointer to set the type-size for the allocation. If you always use the dereferenced pointer to set the type-size, you will never get it wrong. In C, there is no need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc?.

The parenthesis around (*t)->c = "hello"; are necessary for C-operator precedence. If you omit them (e.g. *t->c), you will receive an error because t (holding the address of the original pointer) is not type struct abc*, but instead is type struct abc** and as such has no member named c.

Exmaple Use/Output

$ ./bin/abc_ptr2ptr
hello

Change The Function Return Type to struct abc * And Return Allocated Pointer

Your second option is simply to change the return type of your abc() function to return a pointer to struct abc (e.g. struct abc*). Making that change you do not need to pass any parameter to your function. You can simply declare the pointer local to your function and allocate storage for one struct abc assigning the address of the allocated block to your local pointer and then return that pointer to the caller (main() here)

For example, you could do:

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

struct abc {
    char *c;
};

struct abc *abc (void)                      /* return pointer to allocated struct */
{
    struct abc *t = malloc (sizeof *t);     /* allocate */
    
    if (t == NULL) {                        /* validate */
        perror ("malloc-t");
        return NULL;
    }
    
    t->c = "hello";                         /* assign on success */
    
    return t;                               /* return pointer to allocated struct */
}

int main (void) {       /* void main only valid in freestanding environment */
    
    struct abc *k = abc();                  /* declare and initialize k */
    
    if (k) {                                /* validate k not NULL */
        puts (k->c);
        free (k);                           /* don't forget to free what you allocate */
    }
}

Don't forget to validate the return...

(the output is the same)

Proper Declaration for main() is int main() Unless in a Freestanding Environment

Unless you are programming in a freestanding environment (without the benefit of any OS), in a standards conforming implementation, 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, 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.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85