Reputation: 57
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:
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.
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].cSex
it 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.
Upvotes: 2
Views: 7726
Reputation: 123598
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.
Upvotes: 0
Reputation: 4961
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]
arPeople
arPeople[5]
*arPeople[5]
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...
Upvotes: 1
Reputation: 28685
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.
Upvotes: 2