Thread: Function arguments, pointers to a structure...

  1. #1
    Registered User \007's Avatar
    Join Date
    Dec 2010
    Posts
    179

    Function arguments, pointers to a structure...

    I whipped up some short code to try an learn more about structures, pointers and functions and argument passing. I am not a total newbie, but still learning. I wrote this up so maybe we can use it to learn and talk about these issues.

    Code:
    #include <stdio.h>
    #include <string.h>
    
    struct person{
      char name[25];
      int age;
    } john; // this is just a random name not mine!
    
    void person_init(struct person *human, int agenum, char *name);
    int getAge(struct person *human);
    char* getName(struct person *human);
    
    int main(){
    
      person_init(&john, 24, "Johnny");
      getName(&john);
      getAge(&john);
      printf("\nHis name is certainly, %s\n", getName(&john)); // Pointless, just testing return value
      return 0;
    }
    
    void person_init(struct person *human, int agenum, char *name){
      strcpy(human->name, name);
      human->age = agenum; 
    }
    
    int getAge(struct person *human){
      printf("\nThe person is %d years old.\n", human->age);
      return human->age;
    }
    char* getName(struct person *human){
      printf("The person's name is %s", human->name);
      return human->name;
    }
    My real question is concerning passing a pointer to a structure within a function argument.

    Typically when you pass a pointer to a variable into a function you pass the base address. For example

    Code:
    void function(int *ptr); // This is the fun
    function(ptr_add); // this is what it would look like when you called it in main
    My question here is concerning the structure. Why does the structure require the & for the base address? It seems like a peculiar point because it doesn't seem to abbly to many other data structures... not arrays or functions. So why then does the structure require it? Shouldn't a structure be able to be bassed simply by stating

    Code:
    getName(john); // but this is wrong... requires &
    Not to mention when you declared the struct you didn't declare a pointer to it, you declared it as a standard struct but the argument treats it as a pointer to a structure. What's up with that?

    Any thoughts?

  2. #2
    Registered User claudiu's Avatar
    Join Date
    Feb 2010
    Location
    London, United Kingdom
    Posts
    2,094
    It's the same as every other type. John requires a & because it is an object of type person NOT a pointer to an object of type person(which is what your function is accepting as parameter).

    My feeling is that you don't really understand what pointers are.
    & means address of something. A pointer is a data type containing the address of something. Therefore &object is a sort of "on the fly pointer".
    1. Get rid of gets(). Never ever ever use it again. Replace it with fgets() and use that instead.
    2. Get rid of void main and replace it with int main(void) and return 0 at the end of the function.
    3. Get rid of conio.h and other antiquated DOS crap headers.
    4. Don't cast the return value of malloc, even if you always always always make sure that stdlib.h is included.

  3. #3
    Registered User \007's Avatar
    Join Date
    Dec 2010
    Posts
    179
    I understand that, but why then don't you do this:

    Code:
    int *ptr;
    
    func(&ptr); // redundant use since the base address is ptr
    I guess what bothers me the most is this doesn't work:

    Code:
    struct person *john; // pointer to a struct
    
    getName(john); // doesn't work
    This is what gets me. Or is there a way to get this up and working, maybe something like:

    Code:
    struct person *john, adams;
    john = &adams;
    getName(john);
    I have no idea if that would work and I don't know how it would be useful unless you recycled the pointer.

    Another thing:

    Code:
    function(); // lets pass this sucker into a function
    function2(function); // doesn't require a &
    // PS, I recall passing functions into functions like this during times like creating a pthread, and passing the thread func
    Last edited by \007; 12-07-2010 at 01:52 AM.

  4. #4
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    You don't do:
    Code:
    void func( int * );
    int *ptr;
    func( &ptr );
    Because &ptr is the address of a pointer, not the address of an object. What you are providing, would be for this function:
    Code:
    void func( int ** );
    You're giving it the wrong thing.


    Quzah.
    Hope is the first step on the road to disappointment.

  5. #5
    Registered User \007's Avatar
    Join Date
    Dec 2010
    Posts
    179
    Code:
    #include <pthread.h>
    #include <stdio.h>
    #define NUM_THREADS     5
    
    void *PrintHello(void *threadid)
    {
       long tid;
       tid = (long)threadid;
       printf("Hello World! It's me, thread #%ld!\n", tid);
       pthread_exit(NULL);
    }
    
    int main (int argc, char *argv[])
    {
       pthread_t threads[NUM_THREADS];
       int rc;
       long t;
       for(t=0; t<NUM_THREADS; t++){
          printf("In main: creating thread %ld\n", t);
          rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
          if (rc){
             printf("ERROR; return code from pthread_create() is %d\n", rc);
             exit(-1);
          }
       }
       pthread_exit(NULL);
    }
    // Taken from: https://computing.llnl.gov/tutorials/pthreads/

    I understand that. What I am asking is why doesn't a struct when declared contain the base address already?

    struct person john;

    "john" should be the base address and not require a &, at least that makes more sense. You will never issue john by itself. I understand it doesn't and requires &john to point to the address. I just think it's bad design and I am asking why. Proof being a number of other examples where the base address is clear from declaring the variable name alone.. such as a function as shown above.
    Last edited by \007; 12-07-2010 at 01:58 AM.

  6. #6
    Registered User claudiu's Avatar
    Join Date
    Feb 2010
    Location
    London, United Kingdom
    Posts
    2,094
    Code:
    struct person *john; // pointer to a struct
    
    getName(john); // doesn't work
    It doesn't work because john is an uninitialized pointer (i.e. points to some INVALID memory location where NO person object resides).
    1. Get rid of gets(). Never ever ever use it again. Replace it with fgets() and use that instead.
    2. Get rid of void main and replace it with int main(void) and return 0 at the end of the function.
    3. Get rid of conio.h and other antiquated DOS crap headers.
    4. Don't cast the return value of malloc, even if you always always always make sure that stdlib.h is included.

  7. #7
    Registered User claudiu's Avatar
    Join Date
    Feb 2010
    Location
    London, United Kingdom
    Posts
    2,094
    Quote Originally Posted by \007 View Post
    Code:
    #include <pthread.h>
    #include <stdio.h>
    #define NUM_THREADS     5
    
    void *PrintHello(void *threadid)
    {
       long tid;
       tid = (long)threadid;
       printf("Hello World! It's me, thread #%ld!\n", tid);
       pthread_exit(NULL);
    }
    
    int main (int argc, char *argv[])
    {
       pthread_t threads[NUM_THREADS];
       int rc;
       long t;
       for(t=0; t<NUM_THREADS; t++){
          printf("In main: creating thread %ld\n", t);
          rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
          if (rc){
             printf("ERROR; return code from pthread_create() is %d\n", rc);
             exit(-1);
          }
       }
       pthread_exit(NULL);
    }
    // Taken from: https://computing.llnl.gov/tutorials/pthreads/
    Yes, that's correct.

    thread[t] is of type pthread. &thread[t] is of type pthread* which is something that the hello function will accept since it takes a void* which means you can pass any kind of pointer to it.
    1. Get rid of gets(). Never ever ever use it again. Replace it with fgets() and use that instead.
    2. Get rid of void main and replace it with int main(void) and return 0 at the end of the function.
    3. Get rid of conio.h and other antiquated DOS crap headers.
    4. Don't cast the return value of malloc, even if you always always always make sure that stdlib.h is included.

  8. #8
    Registered User claudiu's Avatar
    Join Date
    Feb 2010
    Location
    London, United Kingdom
    Posts
    2,094
    I would suggest reading our tutorial on pointers. (skip the C++ bit)

    Cprogramming.com FAQ > A tutorial on pointers
    1. Get rid of gets(). Never ever ever use it again. Replace it with fgets() and use that instead.
    2. Get rid of void main and replace it with int main(void) and return 0 at the end of the function.
    3. Get rid of conio.h and other antiquated DOS crap headers.
    4. Don't cast the return value of malloc, even if you always always always make sure that stdlib.h is included.

  9. #9
    Registered User claudiu's Avatar
    Join Date
    Feb 2010
    Location
    London, United Kingdom
    Posts
    2,094
    Quote Originally Posted by \007 View Post
    Code:
    #include <pthread.h>
    #include <stdio.h>
    #define NUM_THREADS     5
    
    void *PrintHello(void *threadid)
    {
       long tid;
       tid = (long)threadid;
       printf("Hello World! It's me, thread #%ld!\n", tid);
       pthread_exit(NULL);
    }
    
    int main (int argc, char *argv[])
    {
       pthread_t threads[NUM_THREADS];
       int rc;
       long t;
       for(t=0; t<NUM_THREADS; t++){
          printf("In main: creating thread %ld\n", t);
          rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
          if (rc){
             printf("ERROR; return code from pthread_create() is %d\n", rc);
             exit(-1);
          }
       }
       pthread_exit(NULL);
    }
    // Taken from: https://computing.llnl.gov/tutorials/pthreads/

    I understand that. What I am asking is why doesn't a struct when declared contain the base address already?

    struct person john;

    "john" should be the base address and not require a &, at least that makes more sense. You will never issue john by itself. I understand it doesn't and requires &john to point to the address. I just think it's bad design and I am asking why. Proof being a number of other examples where the base address is clear from declaring the variable name alone.. such as a function as shown above.
    Ahhh.. I see what your are saying.

    Well, that's just a design of the language, since C does not pass objects by reference only by value.

    In Java for example, you would be correct. In C, you need to distinguish between a pointer (essentially an address) and the VALUE of the object. In this sense, a pointer is a value too, the value of an address
    1. Get rid of gets(). Never ever ever use it again. Replace it with fgets() and use that instead.
    2. Get rid of void main and replace it with int main(void) and return 0 at the end of the function.
    3. Get rid of conio.h and other antiquated DOS crap headers.
    4. Don't cast the return value of malloc, even if you always always always make sure that stdlib.h is included.

  10. #10
    Registered User
    Join Date
    Jan 2009
    Posts
    1,485
    Quote Originally Posted by \007 View Post
    "john" should be the base address and not require a &, at least that makes more sense. You will never issue john by itself.
    Unless you do something like this:

    Code:
    struct Person{
            int age;
            char gender;
    };
    
    int main()
    {
            struct Person john = {88, 'm'};
            struct Person paul = john;
    
            return 0;
    }

  11. #11
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by \007 View Post
    My question here is concerning the structure. Why does the structure require the & for the base address? It seems like a peculiar point because it doesn't seem to abbly to many other data structures... not arrays or functions. So why then does the structure require it?
    It's actually arrays and function pointers that are the anomaly, not structs. Passing a pointer to a struct, just like passing a pointer to a single int, requires use of the & operator.

    When the name of an array is passed to a function, it is converted - by the compiler - into a pointer (to the first element of the array).

    So, if x is an array of 5 int
    Code:
    func(x);
    is the same as
    Code:
    func(&x[0]);
    Note that &x and &x[0] are different things (&x is a pointer to an array of 5 int, but &x[0] is a pointer to the first element of an array). Technically, &x and &x[0] refer to the same area of memory, but they have different types. After all, an array of 5 int is not the same thing as an int.

    Similarly, the name of a function - if passed as an argument to another function - is passed as a pointer. The compiler does the necessary conversion.
    Last edited by grumpy; 12-07-2010 at 03:11 AM.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  12. #12
    Registered User \007's Avatar
    Join Date
    Dec 2010
    Posts
    179
    Could you explain what the compiler does behind the scenes during that assignment? Does the compiler copy the storage space of john to the space of paul? If so how did the compiler know the storage space? My assumption is that john shows the base address and the compiler counts space for the total size of the struct (sum of all variables in it). Now if thats true then why would &john be needed for the address when passing into a function? If the compiler figured it out with only john in that case why not when passing into a function?

    Sorry for spelling, I'm on my phone.

  13. #13
    Registered User
    Join Date
    Jan 2009
    Posts
    1,485
    Quote Originally Posted by \007 View Post
    Could you explain what the compiler does behind the scenes during that assignment? Does the compiler copy the storage space of john to the space of paul? If so how did the compiler know the storage space? My assumption is that john shows the base address and the compiler counts space for the total size of the struct (sum of all variables in it). Now if thats true then why would &john be needed for the address when passing into a function? If the compiler figured it out with only john in that case why not when passing into a function?

    Sorry for spelling, I'm on my phone.
    Yes, the size is known at compile time from your declaration plus eventual padding that the compiler might add, sizeof() also works on structs. In the case above everything is put on the stack. You can actually pass a struct directly into a function as well, it's probably not as efficient for large structs though.

  14. #14
    Registered User claudiu's Avatar
    Join Date
    Feb 2010
    Location
    London, United Kingdom
    Posts
    2,094
    Because there is a difference between:

    void hello(struct person JohnArg)

    and

    void hello2(struct person* JohnArg).

    Say you call these in main as follows:

    struct person john;
    hello(john);
    hello2(&john);

    The calling of hello creates a COPY of the object john which it calls (in my example) JohnArg and performs any operations in the function ON THAT COPY. This has two implications:
    1) If struct person is a large data structure this becomes a time and space wasting operation.
    2) The original john object in main is completely unaffected by whatever happens in the function hello.

    The calling of hello2 creates a copy of the ADDRESS of object john from main which it calls JohnArg.
    1)This implies that any operations in hello2 AFFECT john from main, because you are writing to the memory location indicated by john's address which is where all of john's fields are stored.
    2)There is no time taking copying involved, only the copying of an address, which is a relatively tiny data.
    1. Get rid of gets(). Never ever ever use it again. Replace it with fgets() and use that instead.
    2. Get rid of void main and replace it with int main(void) and return 0 at the end of the function.
    3. Get rid of conio.h and other antiquated DOS crap headers.
    4. Don't cast the return value of malloc, even if you always always always make sure that stdlib.h is included.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Getting an error with OpenGL: collect2: ld returned 1 exit status
    By Lorgon Jortle in forum C++ Programming
    Replies: 6
    Last Post: 05-08-2009, 08:18 PM
  2. Including lib in a lib
    By bibiteinfo in forum C++ Programming
    Replies: 0
    Last Post: 02-07-2006, 02:28 PM
  3. pointers
    By InvariantLoop in forum C Programming
    Replies: 13
    Last Post: 02-04-2005, 09:32 AM
  4. Staticly Bound Member Function Pointers
    By Polymorphic OOP in forum C++ Programming
    Replies: 29
    Last Post: 11-28-2002, 01:18 PM
  5. pointers as function arguments
    By brianptodd in forum C++ Programming
    Replies: 2
    Last Post: 11-19-2002, 06:28 PM

Tags for this Thread