2

I am extremely new to C and struggling a lot. I've looked all over the place and can't find anything that I can understand or that fits my query. My task is to create an array that contains details about 6 people using structures. I then have to create a menu system that allows someone to search gender, first name, last name, date of birth and start date.

This is my .h file:

#define NAME_LENGTH 50

struct strDate
{
    int nDay;
    int nMnth;
    int nYear;
};

struct strPerson
{
    char    arcFirstName[NAME_LENGTH];
    char    arcLastName[NAME_LENGTH];
    char    cSex;
    struct  strDate    strDOB;
    struct  strDate    strStartDate;
};

And this is my .c file (I have taken out all but one of the structure definitions just for the sake of keeping the code to a minimum on here. Elements 1-5 of the array are very similar to element 0, only the actual data varies):

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "Header.h"

/*Function prototypes*/
void Gender(struct strPerson *arPeople[5]);
void FirstName(struct strPerson *arPeople[5]);
void Surname(struct strPerson *arPeople[5]);
void Name(struct strPerson *arPeople[5]);
void YearOfBirth(struct strPerson *arPeople[5]);
void WholeDOB(struct strPerson *arPeople[5]);
void DOB(struct strPerson *arPeople[5]);
void YearOfStart(struct strPerson *arPeople[5]);

int main(void)
{
    int nChoice = 1;
    struct strPerson arPeople[5];

    /*populating structure within array "arPeople"*/
    strcpy(arPeople[0].arcFirstName, "David");
    strcpy(arPeople[0].arcLastName, "Hodgkiss");
    arPeople[0].cSex = 'M';
    arPeople[0].strDOB.nDay = 13;
    arPeople[0].strDOB.nMnth = 5;
    arPeople[0].strDOB.nYear = 1964;
    arPeople[0].strStartDate.nDay = 1;
    arPeople[0].strStartDate.nMnth = 9;
    arPeople[0].strStartDate.nYear = 2001;

    while (nChoice != 5)
    {
        printf("\nPlease enter number relating to search option required...\n");
        printf("1   Search by Gender\n");
        printf("2   Search by Name\n");
        printf("3   Search by Date of Birth\n");
        printf("4   Search by Start Date\n");
        printf("5   Exit\n\n");
        printf("Please enter your choice : ");
        scanf("%d", &nChoice);
        printf("\n");

        switch (nChoice)
        {
        case 1: Gender(arPeople);
            break;

        case 2: Name(arPeople);
            break;

        case 3: DOB(arPeople);
            break;

        case 4: YearOfStart(arPeople);
            break;

        case 5: break;

        default: printf("Invalid input, please try again : \n\n");
        }
    }

    system("pause");
    return 0;
}

void Gender(struct strPerson *arPeople[5])
{
    char cSexMF = 'M';

    printf("Please enter 'M' to search for male members of staff or 'F' to search for female memebers of staff : \n\n");
    scanf("%c \n", &cSexMF);

    printf(".... %c ... %c ....\n", cSexMF, arPeople[0].cSex);
} 

So this is obviously just to search by Gender at the moment... my questions are:

  1. How do I call the function in main in the menu system? At the moment I keep getting the error "warning C4047: 'function' : 'strPerson **' differs in levels of indirection from 'strPerson [5]'". I literally have no idea what this means.

  2. Assuming I've passed it to the function correctly, how do I then print anything from the array? Where I have tried above to print arPeople[0].cSexit says "expression must have a struct or union type". I don't understand this because I thought I'd passed it as a structure and so it would know what I am referencing.

I'd very much appreciate some help with this, I've looked everywhere I can think of, searched everything I can think of and I still can't make it work. I've been sitting here for days trying to work this out and am getting to the point where I don't even care if I fail my class.

6
  • 1
    Remove the asterisk from the function prototypes as well as their declations void YearOfStart(struct strPerson *arPeople[5]) <-- Remove the asterisk. Once you do this, everything will be alright Commented Apr 10, 2015 at 12:53
  • @CoolGuy This is not a good idea. While it will work in removing the asterisk in the function prototype, this means putting the whole struct array on the stack when calling a function. This could lead to many problems if the array and/or the struct are bigger, and is not a good practice. Keep calling functions with pointers arguments. Commented Apr 10, 2015 at 12:56
  • @CoolGuy: CoolGuys recommendation is correct; that is not related. Just put simple pointer as argument to function. Then where you allocate your array - be on heap or stack is up to OP Commented Apr 10, 2015 at 13:21
  • your function prototypes are wrong in this case, just use simple pointers as arguments Commented Apr 10, 2015 at 13:24
  • @Eregrith: no, the function will still only receive a pointer to the first element of the array (in the context of a function parameter declaration, T a[] and T a[N] are interpreted as T *a). The only time an array is copied into a function parameter is if it's being passed as a member of a struct instance. Commented Apr 10, 2015 at 15:05

3 Answers 3

2

Why are you using array of pointers. Instead of this:

void Gender(struct strPerson *arPeople[5])
{
    char cSexMF = 'M';

    printf("Please enter 'M' to search for male members of staff or 'F' to search for female memebers of staff : \n\n");
    scanf("%c \n", &cSexMF);

    printf(".... %c ... %c ....\n", cSexMF, arPeople[0].cSex);
}

Do this (this is how you typically pass arrays to functions as parameters in C):

void Gender(struct strPerson arPeople[], int arrLength)
{
    // Enter search criteria
    char cSexMF = 0;
    printf("Please enter 'M' to search for male members of staff or 'F' to search for female memebers of staff : \n\n");
    scanf(" %c", &cSexMF);

    // Go through array
    for(int i = 0; i<arrLength; i++)
    { 
      // Check gender of each element of array against what user entered
      if(arPeople[i].cSex == cSexMF)
      {
        printf(".... %c ... %c .... %s\n", cSexMF, arPeople[i].cSex, arPeople[i].arcFirstName);
      }
    }
}

This should print names and also gender of people whose gender matches what user entered; you can apply such technique to other methods of yours too, like I said I doubt you need to use this construct: struct strPerson *arPeople[5], in your case.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, I just stuck in the print statement to check that cSexMF and arPerson.cSex were outputting what they were meant to before moving on. I'll give this a try when I move on to doing what I'm actually meant to.
@elarinya17: What I am saying I doubt in this case you need array of pointers, just use pointers as arguments in functions
think I need to read up on pointers to understand what you're saying haha... can you see why this has been a long process?
@elarinya17: Yes but just read up on plain pointers and how to pass structures to functions using pointers - and that's it; you are making more complicated when you use array of pointers:"struct strPerson *arPeople[5])" in your function prototypes -when you don't need it. Check how my function looks. You can also run it and see if it works.
@elarinya17: Actually you don't even need pointers, see the edit
1

Let's start with the basics: in C/C++ when evaluating expressions, the "maximum munch" rule applies, which is go right as much as possible and still get a valid expression, then go left.

how this applies here? let's evaluate your data declaration

struct strPerson *arPeople[5] 
  1. start with your variable: arPeople
  2. go right: arPeople is an array of 5 elements arPeople[5]
  3. go left since you cannot go right anymore: you have an array of 5 elements which are pointers *arPeople[5]
  4. go right: you have an array of 5 pointers to strPerson strPerson *arPeople[5]

Now, back to your problem: arrays are treated the same as pointers by the compiler; take the following declaration:

int *p0;
int p1[20];

Both p0 and p1 are pointers from the compiler's point of view: p0[0] and p1[1] represent the first element, p0++/p1++ increment the address and so on. That is not to say pointers are the same as arrays, they are just treated the same.

When you want to pass in an array as argument, the following function prototypes do the same thing:

void foo(int* p);
void foo(int p[]);
void foo(int p[10]);

The compiler will generate the exact same code for them, and from it's point of view you are passing in the address to the start of the data block.

If you want to modify the array struct strPerson arPeople[5] then your function prototypes should simply be:

void Gender(struct strPerson *arPeople);
OR
void Gender(struct strPerson arPeople[]);

For all the above mentioned reasons.

Hope this helps clear up things...

2 Comments

It does a bit, thank you, but it then means that "case 1: Gender(&arPeople);" is still wrong and also it means that where I could previously reference parts of the array in the function it now doesn't like how I've declared it but I don't know what to do instead, I've tried so many different things!
you do not call the functions by passing in &arPeople, you pass in arPeople; if you have a pointer like int* p; than p is the addres p points to (the one you want in this case) and &p is the address of the pointer, which is where in memory the pointer is located. In short, change your code to what I showed in my answer and you should be fine after a few necessary modifications.
0

Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.

Also, in the context of a function parameter declaration, a declaration of the form T a[] or T a[N] will be treated as T *.

In the function call

Gender(arPeople);

the expression arPeople is converted from type "5-element array of struct strPerson" to "pointer to struct strPerson", or struct strPerson *.

Thus, your function declaration and definition need to be written as

void Gender( struct strPerson *arPeople )

In your code, you declare the arPeople parameter as an array of pointers, which according to the second rule above is treated as a pointer to a pointer, which is where your error message is coming from. Note that you can use the subscript operator [] on a pointer expression, so you can still write expressions like

printf(".... %c ... %c ....\n", cSexMF, arPeople[0].cSex); 

You could pass an actual pointer to the array, like so:

Gender( &arPeople );

Since in this case the expression arPeople is the operand of the unary & operator, it isn't converted to a pointer type; the type of the expression &arPeople is "pointer to 5-element array of struct strPerson", or struct strPerson (*)[5]. In this case, your function declaration and definition would need to be written as

void Gender( struct strPerson (*arPeople)[5] )

I don't recommend this for two reasons. First, your function will only ever be able to deal with 5-element arrays, which isn't terribly flexible (a pointer to a 5-element array is not compatible with a pointer to a some-number-other-than-5-element array). Second, in the body of your code, you must dereference the pointer before applying the subscript, like so:

printf(".... %c ... %c ....\n", cSexMF, (*arPeople)[0].cSex); 
                                        ^^^^^^^^^^^

which honestly just clutters things up.

Some style notes:

You don't want to pass an array without also passing its size, since the receiving function only gets a pointer to the first element, not an actual array object. I recommend you change your functions to take an additional size argument:

void Gender( struct strPerson *arPeople, size_t arSize )

and call it as

Gender( arPeople, sizeof arPeople / sizeof *arPeople );

This way your Gender function knows how many elements are in the array.

Secondly, for ease of maintenance, you can define your functions before the main function, like so:

void Gender( struct strPerson *arPeople, size_t arSize )
{
  // code for Gender function
}

void Surname( struct strPerson *arPeople, size_t arSize )
{
  // code for Surname function
}
...
int main( void ) 
{
  // code for main function
}

This way you don't have to worry about keeping the function declarations and definitions in sync. It does make your code read "backwards", but trust me, from a maintenance perspective this makes things a lot simpler.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.