Thread: beginner question about -- you guessed it -- arrays and pointers

  1. #1
    Registered User
    Join Date
    Nov 2010
    Posts
    7

    beginner question about -- you guessed it -- arrays and pointers

    Hi all;

    I've been searching archives and playing around with tutorial code for a few hours now so I thought I'd give this forum a try.

    My problem is that I'm having a really hard time figuring out how to pass arrays in functions. Specifically, the following code is giving me trouble:

    Code:
    #ifndef employee_h
    #define employee_h
    
    typedef struct emp_struct {	
    	char name[100];
    	int employee_no;
    	float salary, tax_to_date;
    } Employee;
    
    typedef Employee *Database[10];
    
    int comp_employee (int *database[], int i, int j);
    void swap_employee (int *data[], int i, int j);
    
    void init_database(Database employees, int no_employees)
    {
    	**employees[0].name = "Fred" // this gives error of  "request for member 'name' 
                                                             // in something not a structure or union
    }
    
    #endif
    In the above, pretty much everything except for the **employees[0].name line has been provided as part of the tutorial.

    I cannot, for the life of me, figure out how to assign values to the "employees" argument of the init_database method. The attempt I've made above is based on the following logic:

    -the Database type represents a 10-element array of pointers to the Employee type.
    -Understanding that an array's value is a pointer to its first element: dereferencing "employees" once should produce its first element, which would be a pointer to an Employee type.
    -dereferencing "employees" again, then, should yield the actual Employee object, which, I was hoping, could then be assigned. Unfortunately this does not seem to be the case.

    Running "sizeof" on "employees", "*employees", and "**employees" seems to confirm my hunch that these items are a pointer, a pointer, and an Employee, respectively. However, I still can't figure out how to make this assignment. Is it just a matter of syntax? Or am I just totally out to lunch with how this array/pointer thing works?

    Any help would be very much appreciated. This C thing is a lot harder than Ruby or Java, haha.

  2. #2
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Code:
    typedef struct emp_struct {	
    	char name[100];
    	int employee_no;
    	float salary, tax_to_date;
    } Employee;
    
    typedef Employee *Database[10];
    The second typedef is your problem. Typedefs do not create an instance of an array. Since employee is now a recognized type in your code, you can use it like any other type....

    Code:
    typedef struct emp_struct {	
    	char name[100];
    	int employee_no;
    	float salary, tax_to_date;
    } Employee;
    
    Employee Database[10];

  3. #3
    Registered User
    Join Date
    Nov 2010
    Posts
    7
    Hi CommonTater;

    Thanks for the reply.

    As I understand it, the intention of the second typedef statement is to create a Database type that is 10-element array of pointers to the Employee types.

    So, taking this as a given, I'm wondering how I can assign data to this type when it's passed into a function.

    Since the code I've posted below is from a header file, I might be helpful to have the code from the main loop which accesses the init_database method:

    Code:
    #include <stdio.h>
    #include "employee.h"
    
    int main (int argc, const char * argv[]) {
    	
    	const int no_employees = 10;
    	Database people;
    	init_database (people, no_employees);
    }

  4. #4
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    And where is your code for the init_database function?

    (FWIW... Methods are C++ not C)

  5. #5
    Registered User
    Join Date
    Nov 2010
    Posts
    7
    The init_database function (apologies, as I mentioned I come from a Java/Ruby background) is defined in the employee.h header file, which is copied in my initial post above.

  6. #6
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by jeemang View Post
    The init_database function (apologies, as I mentioned I come from a Java/Ruby background) is defined in the employee.h header file, which is copied in my initial post above.
    First off, putting active code in header files is generally not a very good idea. .h files should be used for definitions, references, prototypes etc. Active code should always be in it's own .c file... in this case, employee.c

    In the function itself you are trying to assign a string using the good old equals sign...
    C doesn't know how to do that. In fact C does not natively know anything about strings. To assign "Fred" to your employees array you need to use one of the library functions such as strcpy() or memcpy()...
    Last edited by CommonTater; 11-19-2010 at 03:33 AM.

  7. #7
    Registered User
    Join Date
    Nov 2010
    Posts
    7
    Re: header file: thanks for the heads up. One question: if I toss the active code into employee.c, how do I reference employee.c from employee.h? Do I just put an #include "employee.c" at the bottom of employee.h?

    Re: strcpy: dumb mistake on my part, one of the few things I actually know about C is that you can't assign strings with the equals sign.

    Problem is, even when I use strcpy I get the same error message that I pointed out in my original post:

    "Request for member "name" in something not a structure or union."

    This is funny to me, because sizeof(Employee), sizeof(*employees[0]), and sizeof(**employees) all return the same result (implying that *employees[0] and **employees point to an Employee). However,

    Code:
    strcopy(*employees[0].name, "Fred");
    Code:
    strcopy(**employees[0].name, "Fred");
    and

    Code:
    strcopy(**employees.name, "Fred");
    all give the error above.

    Thanks very much for your help on this.

  8. #8
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Do I just put an #include "employee.c" at the bottom of employee.h?
    Heavens, no! The header file is so other files (not employee.c) know about stuff in employee.c. Typically this is things like definitions of constants, macros and function prototypes. Your function prototype should include the keyword "extern" at the beginning, so that other files that include employee.h know what the function signature of foo is (return type and parameter list), and that it is located in a different, "extern"al translation unit (.c file). E.g. "extern int foo(char *, char *, int);"

    "Request for member "name" in something not a structure or union."
    I'm guessing you need to use the -> operator instead of the . for member access. If you haven't changed your code from the OP, you have an array of pointers to Employee objects, not an array of Employee objects.

    This is funny to me, because sizeof(Employee), sizeof(*employees[0]), and sizeof(**employees) all return the same result (implying that *employees[0] and **employees point to an Employee)
    That is to be expected. All 3 refer to an emp_struct. Again, assuming you haven't changed your code from the OP, you have a 1-d array of pointers to Employee objects. Let's look at the 3 different sizeof() statements
    1. Employee refers to the type definition of the emp_struct, not a variable. It is similar to sizeof(int) or sizeof(char) in this regard.
    2. *employees[0] "give me the first element (pointer to an Employee) in the employees array, then dereference it to give me the actual Employee object".
    3. Similaraly to 2, since the name of an array is like a pointer to a contiguous block of those elements, **employees says "dereference the address employees contains (the start of the array of Employee pointers, or &employees[0]), and then dereference the address you find there to give me an actual Employee object".

  9. #9
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by jeemang View Post
    Re: header file: thanks for the heads up. One question: if I toss the active code into employee.c, how do I reference employee.c from employee.h? Do I just put an #include "employee.c" at the bottom of employee.h?
    Depending on your compiler and IDE, you would add employee.c to your project files list (or add it to your makefile) and include employee.h in your C files as needed. The header really just makes a promise to the compiler that *someplace* in your code all this stuff actually exists.


    Re: strcpy: dumb mistake on my part, one of the few things I actually know about C is that you can't assign strings with the equals sign.

    Problem is, even when I use strcpy I get the same error message that I pointed out in my original post:
    The problem is that you've pretty much buried yourself in typedefs... even to the point of nesting them. My best advice at this point is "simplify, simplify, simplify"... simply creating a typedef for your struct then create your array... In fact you don't even have to typedef the struct, but it is a bit tidier (imo).
    Code:
    typedef struct tEmployee
      {  // names and stuff in here
          } Employee, *pEmployee;
    
    Employee Employees[10];
    That is a simple array you can create in the top of your main.c file. You would access it as (for example) Employee[6].name or to mount the name... strcpy(Employee[3].name,"Fred");

    There's no reason to typedef anything that you can simply create.
    Last edited by CommonTater; 11-19-2010 at 06:05 PM.

  10. #10
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    I think the lesson to take away about typedefs is this: don't let the typedef mask any information.

    That is to say, don't have the typedef include array or pointer stuff. If you do, all that will happen is that you will obscure the nature of your variables and be more likely to misuse them later. Typedefs are fine for shortening "struct something" into "Something", but not for saving you from typing array brackets or pointer *'s later.

  11. #11
    Registered User
    Join Date
    Nov 2010
    Posts
    7
    Thanks, CommonTater and anduril, for the advice.

    I finally got the first chunk of my code working (I won't say how long it took me as it's embarrassing).

    Basically, the big mistake I was making was in not allocating any memory for the Employee types pointed to in the *employees array. Below is my working code. I'd be interested to see if anyone had any suggestions for a better way to code the init_database function in employee.c (or any other suggestions).

    main.c:

    Code:
    #include <stdio.h>
    #include "employee.h"
    
    int main (int argc, const char * argv[]) {
        
    	const int no_employees = 10;
    	Employee *employees[10];
    	
    	init_database(employees, no_employees);
    }
    employee.h:

    Code:
    #ifndef employee_h 
    #define employee_h
    
    typedef struct emp_struct 
    { char name[100];
    	int	employee_no;
    	float salary, tax_to_date; 
    } Employee;
    
    void init_database (Employee *employees[], int no_employees);
    
    #endif
    employee.c:

    Code:
    #include "employee.h"
    #include <string.h>
    #include <stdlib.h>
    
    void init_database (Employee *employees[], int no_employees)
    {
    	Employee temp[10] =
    	{	{"Fred", 10, 10000, 3000}, 
    		{"Jim", 9, 12000, 3100.5}, 
    		{"Fred", 13, 1000000, 30}, 
    		{"Mary", 11, 170000, 40000}, 
    		{"Judith", 45, 130000, 50000}, 
    		{"Nigel", 10, 5000, 1200}, 
    		{"Trevor", 10, 20000, 6000}, 
    		{"Karen", 10, 120000, 34000}, 
    		{"Marianne", 10, 50000, 12000}, 
    		{"Mildred", 10, 100000, 30000}
    	};
    	
    	for (int i = 0; i < no_employees; i++) {
    		employees[i] = (Employee *) malloc(sizeof(Employee));
    		*employees[i] = temp[i];
    	}
    }

  12. #12
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    At the risk of asking out of turn...

    Why do you need to initialize into a temp array then copy it to a pointer?
    You can create an instance of employees and intialize it directly...
    Code:
    #include <stdio.h>
    #include "employee.h"
     
    const int no_employees = 10;
    Employee employees[10] =
        { {"Fred", 10, 10000, 3000}, 
          {"Jim", 9, 12000, 3100.5}, 
          {"Fred", 13, 1000000, 30}, 
          {"Mary", 11, 170000, 40000}, 
          {"Judith", 45, 130000, 50000}, 
          {"Nigel", 10, 5000, 1200}, 
          {"Trevor", 10, 20000, 6000}, 
          {"Karen", 10, 120000, 34000}, 
          {"Marianne", 10, 50000, 12000}, 
          {"Mildred", 10, 100000, 30000} };
    
    
    int main (int argc, const char * argv[]) {
       
    
    }
    Last edited by CommonTater; 11-22-2010 at 08:32 PM.

  13. #13
    Registered User
    Join Date
    Nov 2010
    Posts
    7
    That would definitely be a smarter way to do things.

    However, this is basically just an example program I'm using to learn how to do things like pass pointer arrays into functions and the like (which, if I don't need to do in this program, I imagine I will probably need to do at some point if I continue to use C).

    Thanks a lot for your comments and suggestions.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Pointers and multidimensional arrays question
    By d2rover in forum C Programming
    Replies: 10
    Last Post: 11-07-2010, 10:43 AM
  2. pointers of chars and arrays of chars Question
    By shiroaisu in forum C++ Programming
    Replies: 9
    Last Post: 08-09-2010, 10:42 AM
  3. Replies: 7
    Last Post: 05-19-2010, 02:12 AM
  4. a question on pointers, structs and arrays
    By onefootswill in forum C Programming
    Replies: 3
    Last Post: 12-06-2007, 01:27 AM
  5. pointers
    By InvariantLoop in forum C Programming
    Replies: 13
    Last Post: 02-04-2005, 09:32 AM

Tags for this Thread