PDA

View Full Version : C prog: printing values from array of structures


Math Is Hard
Aug8-04, 03:12 PM
Hello, I have a little function I have written that takes an array of structures as an argument


void print_names(struct pers_info arr[])
{
printf("Here is person 1: %d %s %s %s\n", arr[0].ssn,
arr[0].pers_name.first,arr[0].pers_name.middle, arr[0].pers_name.last);
printf("Here is person 2: %d %s %s %s\n", arr[1].ssn,
arr[1].pers_name.first,arr[1].pers_name.middle, arr[1].pers_name.last);
}

This is working fine - it prints a number, then a first name, middle name, and last name. The problem is that when I print the middle name I only want to print the first initial. I can't figure out how to print only the first character of the pers_name.middle character string. Thanks for your help.

Goalie_Ca
Aug8-04, 04:26 PM
Try this:


printf("%c",arr[0].pers_name.middle[0]);


A string is just a character array.

Math Is Hard
Aug8-04, 04:44 PM
Thanks! That did the trick!!

plover
Aug8-04, 05:18 PM
You can also give the string conversion maximum number of characters. (Though this is obviously more useful for numbers greater than 1...)

printf("%.1s",arr[0].pers_name.middle);

Math Is Hard
Aug8-04, 05:31 PM
hey there plover. You know, that was my first thought- that I would do some kind of a truncation thingy when I did the specifications. But I figure he wants to see us specifically access the character in the array, so I went with that.
I am doing some alterations now to make the print function loop through all the elements in the structure array and print it one way if a middle name is found and another way if a middle name is not found. I'm not quite sure if that's the most elegant way to handle it but I'll see how it comes out and if it starts looking clunky I'll post again.

Math Is Hard
Aug8-04, 06:47 PM
well, I got the first part of the assignment taken care of which was to pass the structure array to my printing function. At least I think I handled it right. I called the function like this:

print_names(arr);

and then wrote my function heading like this:

void print_names(struct pers_info arr[])


now, the second part of the assignment asks me to go back and modify the program by passing the structure value instead of the address this time.
I am getting confused here. Is he asking me to pass individual elements of the structure maybe?
:confused:
Thanks.

plover
Aug8-04, 07:23 PM
How would you set up the function so that this call made sense?

print_name(arr[n]);

What would happen when the function was called?

Math Is Hard
Aug8-04, 07:47 PM
well, I'm not sure. maybe I should create an argument for the number of elements in the array? like...

void print_names(struct pers_info arr[], int n)

Goalie_Ca
Aug8-04, 08:03 PM
Are you familiar with pointers? if so the following statements are equivalent


arr[];
*arr;


When you pass them by value you will not pass a pointer to an array, you will pass an individual object (note: object is bad choice of word :P). The only way this can be achieved is by calling the function print_names as many times as the array is large.

Just as plover was hinting at you want to do this:

int n=0;
for (n=0;n<size;n++){
print_names(arr[n]);
}


You should also re-write your function above to be the same thing but instead pass a reference to the object, kinda like


print_names(&arr[n]); //you could ultimately do it other ways, but this is conceptually simple
//on the function prototype you would need to use a * to let the compiler know that an address was being passed.


Otherwise you would have to add the size parameter as you thought of above.

Math Is Hard
Aug8-04, 08:43 PM
When you pass them by value you will not pass a pointer to an array, you will pass an individual object (note: object is bad choice of word :P). The only way this can be achieved is by calling the function print_names as many times as the array is large.

Just as plover was hinting at you want to do this:

int n=0;
for (n=0;n<size;n++){
print_names(arr[n]);
}




hmmmm....In my current version I have a loop that sorta does that. I give it an array of structure addresses, and I display the structure contents


void print_names(struct pers_info arr[])
{
int i;
for (i=0;i<5;i++)
{
if (arr[i].pers_name.middle[i] == '\0') //no middle initial
{
printf("%s, %s -- %d \n", arr[i].pers_name.last, arr[i].pers_name.first,
arr[i].ssn);
}
else //if there is a middle initial
{
printf("%s, %s %c. -- %d \n", arr[i].pers_name.last,
arr[i].pers_name.first, arr[i].pers_name.middle[0],arr[i].ssn);
}
}
}



You should also re-write your function above to be the same thing but instead pass a reference to the object, kinda like


print_names(&arr[n]); //you could ultimately do it other ways, but this is conceptually simple
//on the function prototype you would need to use a * to let the compiler know that an address was being passed.


Otherwise you would have to add the size parameter as you thought of above.

Can I keep what's inside my printing function as is but just pass
&arr[i] to it to specify that the value should be passed rather that the address?

Thanks.

plover
Aug8-04, 08:44 PM
C uses a method called pass-by-value when sending arguments to a function. So, if you a have a function:
void uselessFunc(int n) { n += 1; }
and later have the instructions:
int a = 100;
uselessFunc(a);
printf("%d\n", a);
the number printed will be 100, as the value of a is copied to the argument n (i.e. passed by value). Thus any operations on n do not affect a. This is why you pass a pointer when you want to affect the original object, because then it is the address that is value that gets copied, and a copy of the address will, of course, still de-reference to the original object.

The same principle works if the argument is a struct rather than just an int.

Do the questions in my previous post make more sense with this to go with them? (Also note that my function was called print_name rather than print_names.)

It seems slightly strange to me that the teacher would imply that both of the functions you're asked to write would be able to work on the entire array at once, but perhaps I am misunderstanding something in your description.

The follow-up that Goalie_Ca gave to my hint is correct, and in most situations his advice to pass the address would be good. However, from what I can tell of your instructions, you are being asked to not pass the address here.

I should probably point out that I am assuming you've seen these ideas before. I'm just trying to show the context from which the problem can be answered.

(I hope I'm not being too oblique. You should tell me if I'm just confusing you more than helping...)

Math Is Hard
Aug8-04, 09:03 PM
I think I am understanding now. The difference is just between passing the pointer address and passing the value that is being pointed to.

It seems slightly strange to me that the teacher would imply that both of the functions you're asked to write would be able to work on the entire array at once, but perhaps I am misunderstanding something in your description.

No, I think he is just asking for two different versions of the same program.
I actually hadn't heard the term "pass by value" method before. I think initially that is all we were doing but it didn't have a special name. I get confused I think because I don't know how much the rules change (if at all) when I am working with a structure.
And I do appreciate the help. Sorry I am not absorbing the info faster. Maybe I shoud work on a smaller simpler version of what I am trying to do and then try to apply it back to my program. Just so I'll know how to use pass by value in this case.

Math Is Hard
Aug8-04, 10:56 PM
I made up a simpler example to see if I can get my head around this. I just have a character array and I am passing a pointer to it. Maybe someone could show me how to write this using Pass By Value? Thanks. :smile:


#include <stdio.h>
int main(void)
{
void print_array(char * a); //proto

char arr[3] = {'A','B','C'};

print_array(arr); //call printing function
return 0;
}

void print_array(char * a)
{
int i;
for (i=0;i<3;i++)
{
printf("%c \n",a[i]);
}
}

Goalie_Ca
Aug9-04, 12:41 AM
You cannot pass an array by value. An array is actually a pointer to a memory block. When you do array[3] you are essentially adding 3*data_size to the array starting address. Mmmkay.. You have to move that for loop outside the function.


#include <stdio.h>
void print_array(char a); //proto


int main(void)
{
char arr[3] = {'A','B','C'};
int n=0;
for (n=0;n<3;n++)
print_array(arr[n]); // equivalent to *(arr+3)****
return 0;
}

void print_array(char a)
{
********printf("%c \n",a);
}

Math Is Hard
Aug9-04, 12:53 AM
Goalie - thank you. I remembered from examples that for arrays we had to specifically pass a pointer to a function but I was getting a little muddled on why that was.
So you are saying use the loop to grab the value first and then pass the value off to the printing function I think? uh.. I hope I understood that..
Anyway, I appreciate your explanations and your patience.

plover
Aug9-04, 12:57 AM
'Pass-by-value' is not something the programmer does -- it is built in to the C language. It is the method that C programs use to pass arguments to functions. (The other common method is pass-by-reference, where the function argument assumes the identity of the variable that is passed in instead of copying it. C++ uses both methods.)

Suppose you have a struct:
struct point2d {
double x;
double y;
} targetCoord;
and a function with the prototype:
int swatFly(int id, struct point2d target);
At some point in the code you have:
int flyId = 666;
targetCoord.x = 3.1415;
targetCoord.y = 2.7183;
int isFlyToast = swatFly(flyId, targetCoord);

When you call swatFly, what happens? The system sets up the memory space necessary to execute a function (it's called a stack-frame -- if that's meaningless to you, don't worry about it). Included in that memory space will be spots for all of the arguments and local variables used by the function. Since C uses call-by-value, it then looks at the value of 'flyId' and copies that value to the address that will be used by the parameter 'id'. Then it looks at 'targetCoord' and copies the value of that to 'target'; since the system has already seen the definition of 'point2d', it knows that this value is two items of type 'double'.

The slightly confusing case is arrays. If you have an array A, then an item in that array A[n] acts just like any other variable of whatever type is stored in A. But what does the symbol 'A' by itself mean? In most circumstances, the most useful meaning is to say where the array A is. So the symbol A is interpreted as the address of the first item in the array (another way to say this would be that the array is converted to a pointer). Thus when an array is passed to a function, the value that is copied by the 'pass-by-value' mechanism is the address. (The prototype of the function can specify either an array or a pointer in these cases.)

I am glossing over a few things, but I hope this is enough information to clarify the current problem.

Math Is Hard
Aug9-04, 01:17 AM
plover, that is very interesting - thank you. Tell me, why is targetCoord outside of the structure in

struct point2d {
double x;
double y;
} targetCoord;

and the other thing I am not sure about (just occurred to me) is what the & symbol does here in the post by Goalie
print_names(&arr[n]);

Thanks rain-bird friend! :smile:

plover
Aug9-04, 02:21 AM
I'm sorry, I didn't know you hadn't seen this syntax. It is possible to include variable declarations along with the definition of a struct. Thus, targetCoord is just being declared as a variable of type struct point2d.

In &arr[n], the '&' means "take the address of". Thus, &arr[n] passes a pointer to the array element, where arr[n] would pass the array element itself. For a large struct, you usually want to pass the pointer as that is much more efficient than copying the whole struct.

Math Is Hard
Aug9-04, 11:57 AM
I'm sorry, I didn't know you hadn't seen this syntax. It is possible to include variable declarations along with the definition of a struct. Thus, targetCoord is just being declared as a variable of type struct point2d.


ahhh... I see!


In &arr[n], the '&' means "take the address of". Thus, &arr[n] passes a pointer to the array element, where arr[n] would pass the array element itself. For a large struct, you usually want to pass the pointer as that is much more efficient than copying the whole struct.

Thanks. I was thinking it had something to do with the address because I had seen it used when assigning an address to a pointer like

MyPointer = &Fred;

to assign the address of Fred to MyPointer. But I was surprised to see it used as part of a parameter in a function call.

Let me see if I understand. I am still not sure I have grasped it, so I will state my interpretation and you'll be able to see my thought processes on this and where I might be having trouble:

Using this as a parameter in a function call passes a pointer to the nth element of the array
&arr[n]
in other words, the address of the nth element in the array.

Using this as a parameter in a function call passes the value stored at the nth position in the array:
arr[n]
for instance if arr was a char array it would pass an actual character like 'A' or 'j'. It would behave the same as if you were passing over a single char variable even though it is actually a member of an array.

Thanks. :smile:

Goalie_Ca
Aug9-04, 12:07 PM
Here's something else for you to think of, if you understand this then you have a decent idea of pointers...



char arr[3][2];

what would arr pass?
what would arr[2] pass?
what would arr[2][1] pass?

One other thing, you can also have function pointers, void pointers, and others.

Math Is Hard
Aug9-04, 12:36 PM
hmm.. I think arr is an array of 3 pointers to arrays of two characters each.

Did I understand that much?

Math Is Hard
Aug9-04, 01:13 PM
what would arr pass?
I think this would pass the address of the first element in the array of pointers called arr. (i.e. it's passing an address)

what would arr[2] pass?
I think this would pass the address of the third element in the array of pointers called arr (i.e. it's passing an address)

what would arr[2][1] pass?
I think this would pass the value of the second character stored in the third character array? I am visualizing it kinda like this

================================================== ========
arr[0] | arr[1] | arr[2]
================================================== ========
1st char| 2nd char | 1st char| 2nd char |1st char| 2nd char
================================================== ========


(sorry- I tried my best to draw a picture)

but I don't know if that's right.

plover
Aug9-04, 04:35 PM
As far as it goes, you've got the right idea. The question now is: what does the parameter type have to be in each case for the argument to be accepted?

Math Is Hard
Aug9-04, 06:26 PM
uhhh... pointer, pointer, and char respectively?

plover
Aug9-04, 07:10 PM
The third one is char, of course. But 'arr' and 'arr[2]' do not have identical pointer types. E.g. 'arr' and 'arr[0]' may indicate the same address, but '(arr + 1)' and '(arr[0] + 1)' do not.

Note that a compiler may let you use them interchangeably (though it should at least spit out a warning), but doing so will get you in trouble if you don't know precisely what is going on (and is not good programming practice anyway).

Math Is Hard
Aug9-04, 08:13 PM
I don't think I am passing it the character from the structures the right way. I still can't get this to work. Here's the whole enchilada.
Take a look? Thanks...


#include <stdio.h>
# define LEN 35

void print_names(char a); //prototype of printing function

struct name // defines the name structure
{
char first[LEN];
char middle [LEN];
char last[LEN];
};

struct pers_info //defines the personal info structure
{
char ssn[10];
struct name pers_name;
};

int main(void)
{
//create and init array of pers_info structures
struct pers_info arr[5] =
{
{
"123456123", {"Anne","Appleby","Adams"}
},
{
"623001200", {"Bud", "","Burke"}
},
{
"423456999", {"Carol","","Cooper"}
},
{
"327856909", {"Darryl","Dimitri","Dodd"}
},
{
"563456929", {"Evelyn","Elizabeth","Eggwhite"}
}

};

int n=0;
for (n=0;n<5;n++)
print_names(struct pers_info arr[n]);
return 0;
}

void print_names(char a)
{
printf("%c \n", a);
}



All this is supposed to do is print names and social security numbers.

Math Is Hard
Aug9-04, 08:15 PM
should I be passing strings? or can I even do that?

chroot
Aug9-04, 08:17 PM
It doesn't even look like this will compile:

print_names(struct pers_info arr[n]);

- Warren

Math Is Hard
Aug9-04, 08:18 PM
no - it won't - it's a mess

Math Is Hard
Aug9-04, 08:20 PM
This is the version I wrote that works - but it doesn't pass values: :frown:


#include <stdio.h>
# define LEN 35

void print_names(struct pers_info arr[]); //prototype of printing function

struct name // defines the name structure
{
char first[LEN];
char middle [LEN];
char last[LEN];
};

struct pers_info //defines the personal info structure
{
int ssn;
struct name pers_name;
};

int main(void)
{
//create and init array of pers_info structures
struct pers_info arr[5] =
{
{
123456123, {"Anne","Appleby","Adams"}
},
{
623001200, {"Bud", "","Burke"}
},
{
423456999, {"Carol","","Cooper"}
},
{
327856909, {"Darryl","Dimitri","Dodd"}
},
{
563456929, {"Evelyn","Elizabeth","Eggwhite"}
}

};

print_names(arr); //call printing function
return 0;
}

void print_names(struct pers_info arr[])
{
int i;

for (i=0;i<5;i++)
{
if (arr[i].pers_name.middle[i] == '\0') //no middle initial
{
printf("%s, %s -- %d \n", arr[i].pers_name.last, arr[i].pers_name.first,arr[i].ssn);
}
else //if there is a middle initial
{
printf("%s, %s %c. -- %d \n", arr[i].pers_name.last, arr[i].pers_name.first,
arr[i].pers_name.middle[0],arr[i].ssn);
}
}
}

chroot
Aug9-04, 08:21 PM
Your function should look like this:


void print_name(struct pers_info record) {
...
}


And the code calling it should look like:


for (int n=0;n<5;n++)
print_name(arr[n]);


Passing a struct by value is no different than passing any other kind of variable by value.

- Warren

plover
Aug9-04, 08:22 PM
print_names(struct pers_info arr[n]);

You don't need to include the type when passing an argument (except for type casts, which is not what's required here).

Math Is Hard
Aug9-04, 08:27 PM
Your function should look like this:


void print_name(struct pers_info record) {
...
}


record? Does that mean one of the individual structures I made? thanks. Record isn't a keyword is it?

plover
Aug9-04, 08:29 PM
record is a keyword in Pascal (I think), not in C though.

In chroot's example, it's just the name of the argument.

chroot
Aug9-04, 08:30 PM
When you create a function, you can call the variables it receives by any names you'd like. Even if the calling function referred to a hunk of data as arr[n], the called function can choose to call that incoming hunk of data anything it wants. In this case, I chose the word 'record' to refer to the variable being passed into the print_name function. It's not a reserved word in C, and means, well, nothing -- you can call it aStudent if you want, or theThingy, too.

- Warren

Math Is Hard
Aug9-04, 08:38 PM
HOLY COW! IT'S WORKING!! Here's the function.

void print_names(struct pers_info record)
{
printf("%s -- %s \n", record.pers_name.last, record.ssn);
}

Math Is Hard
Aug9-04, 08:39 PM
Thank You!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! :biggrin: :biggrin: :biggrin:

This the output:

Adams -- 123456123
Burke -- 623001200
Cooper -- 423456999
Dodd -- 327856909
Eggwhite -- 563456929
Press any key to continue