全部博文(132)
分类: C/C++
2009-04-09 20:18:58
Prev:
Next:
A struct (short for STRUCTURE), is a collection of variables of different types. Structures are sometimes referred to as ABSTRACT DATA TYPES, or ADTs for short.
We already know how to store a group of variables of the same type using arrays.
Variables in a struct are called MEMBERS or FIELDS, and are accessed by their name.
Variables in an array are called ELEMENTS, and are accessed using square brackets an an index.
Declaring a struct requires the struct keyword, followed by a name. Then you declare your collection of variables between a pair of curly brackets. For example:
struct human {
char *name;
int age;
float height;
};
/* don't forget this
semi colon!!!! */
It's best to declare your struct before main.
human is now a structure type, with three fields: name, expressed as a string, an integer age and a floating point height.
You can put whatever data types you want. And you're not limited to normal variables either - arrays and pointers can also go into a struct, though, you must specify an array size.
You can NOT use the assignment operator with the struct declaration.
NOTE: The size of a struct is the sum of the sizes of all the variables it holds.
Like with enum, you can insert a list of variables after the end bracket like this:
struct album {
char *name;
char *artist;
int numTracks;
} CD1, CD2, CD3;
This declares three variables of the struct album type.
But if album was declared outside main, the three variables will be global variables.
So instead, you can write this inside main:
struct album CD1, CD2, CD3;
Or better still, declare your struct and write:
typedef struct album Album;
... then inside main:
Album CD1, CD2, CD3;
Try and group all your typedef statements outside main - it makes the code look neater and easier to maintain.
Now that you know how to declare structure variables, there are two ways to initialize them: all the fields in one statement, or one field at a time.
To initialize all fields in one statement, you declare your variable as usual, but assign it a list of variables enclosed in brackets, like this:
struct human TR = {"Tony", 12, 1.5};
This is similar to the method of initializing arrays.
Now the struct human variable, TR has all three fields initialized: the name field holds the string, "Tony", the age field holds the integer, 12, and the height is 1.5.
To reference an individual field, you use the DOT OPERATOR to separate the struct variable name and the field name.
You can use the dot operator to initialize each field individually:
struct human TR;
TR.name = "Tony";
TR.age = 12;
TR.height = 1.5;
To access each field, use the dot operator again.
For example, TR.age will return 12.
When I first encountered structs I used a sized char array to store strings.
Suppose I redefine human:
struct human {
char name[50];
int age;
float height;
};
The first disadvantage, is that the name array must have a size big enough to store any string I was going to use.
Secondly, the only (obvious) way to initialize a string field, is to do it all at once:
struct human TR = {"Tony", 12, 1.5};
TR.name = "Tony" won't work, simply because the only way of assigning a string into an array is when you declare an array: char name[5] = "Tony"; Recall that TR.name is actually a char pointer, but it's an R-value in this case. In other words, assigning a value into it wouldn't work, as it wasn't declared as a char *. But...
TR.name[0] = 'T';
TR.name[1] = 'o';
TR.name[2] = 'n';
TR.name[3] = 'y';
TR.name[4] = '\0';
... works because the elements of the name array are L-values.
If human was defined with char *name, you could use TR.name = "Tony"; because pointers are L-values: remember that strings are considered to be of type char * as well.
Like with the usual data types, you can store a group of similar typed variables in an array.
The method of declaring an array is the same as before, but initializing your elements isn't as simple as first thought...
#include
typedef struct robot ROBOT;
struct robot {
char *name;
int energy;
};
int main() {
int i;
ROBOT robots[3];
robots[0].name = "Lunar Lee";
robots[0].energy = 50;
robots[1].name = "Planetary Pete";
robots[1].energy = 20;
robots[2].name = "Martian Matt";
robots[2].energy = 30;
/* robots[0] = {"Lunar Lee", 50}; WON'T WORK,
BUT HERE'S ANOTHER WAY...
ROBOT robots[3] = { {"Lunar Lee", 50},
{"Planetary Pete", 20},
{"Martian Matt", 30} }; */
for(i=0 ; i<3 ; i++) {
printf("Robot %d is called %s ", i, robots[i].name);
printf("and has %d units of energy.\n", robots[i].energy);
}
return 0;
}
Output:
Robot 0 is called Lunar Lee and has 50 units of energy. |
Instinctively, I first declared my array of robots, then I tried initializing each element one by one like this:
robots[0] = {"Lunar Lee", 50};
Unfortunately, MSVC++ complained, and so I initialized each name and energy field separately for each robot, as shown above.
There is a quicker way of initialization, as shown in the commented section. It involves initializing the entire array all at once. Notice how I separated each array element with a line break for clarity. Also note that a group of fields must be enclosed in brackets as well: {"Lunar Lee", 50}, for example.
Prev:
Next:
Copyright © 2001-2003
Unauthorized copying not permitted
Designed and Developed Using
Prev:
Next:
Just thought I'll show you an example of using an array as a field (apart from char arrays!)...
#include
struct human {
char *name;
int age[2];
};
struct human {
char *name;
int age[2];
};
int main() {
struct human user;
int m;
printf("What is your name? ");
gets(user.name);
printf("\nHow old are you?\nYears: ");
scanf("%d", &user.age[0]);
printf("\nMonths: ");
scanf("%d", &user.age[1]);
m = user.age[1];
printf("\nYour name is %s and you are %d years ",
user.name, user.age[0]);
printf("and %d %s old.\n", m, (m==1 ? "month" : "months"));
return 0;
}
Output will depend on what you entered.
I must confess things didn't go according to plan: I declared user to be a global variable by placing it after the definition of a struct human, but MSVC++ didn't like it (something about assertion failure whatever that meant!). So I declared user inside main.
gets is used to get your name - we pass a char * into gets, which is ok here.
Then 2 scanfs are used to get your age: first the years, then the months. The program stores these values in the age array.
The rest is pretty much self explanatory: the (m==1 ? "month" : "months") part basically does a grammar correction.
Like with arrays, you can create pointers to structures. This time, the pointer stores the memory address of a structure variable.
Declaring you pointer follows along the same lines as with arrays, for example, if user was of type HUMAN (where HUMAN is struct human):
HUMAN *human_ptr = &user;
Or:
HUMAN *human_ptr;
human_ptr = &user;
- Both of these would do the trick. However, make sure you've declared all your program variables before initializing pointers, else you'd encounter problems later on!
Your compiler might complain if you try and declare variables after creating pointers, so it's best if you declare all your variables at the beginning of main, rather than "on the fly" e.g. halfway through main.
Now that you've created a pointer, you now access fields using the arrow operator, which is made up of two characters: a hyphen, then the greater than sign: ->. This replaces the dot operator, but other than that, accessing structure fields is similar:
human_ptr->name = "Martin";
Is the equivalent of:
user.name = "Martin";
Here's the last example rewritten using pointers:
#include
typedef struct human HUMAN;
struct human {
char name[50];
int age[2];
};
int main() {
HUMAN user;
HUMAN *human_ptr;
int m; /* Declare m here... */
human_ptr = &user;
printf("What is your name? ");
gets(human_ptr->name);
printf("\nHow old are you?\nYears: ");
scanf("%d", &(human_ptr->age[0]));
printf("\nMonths: ");
scanf("%d", &(human_ptr->age[1]));
/* int m; Declaring m here caused a compilation error!! */
m = human_ptr->age[1];
printf("\nYour name is %s and you are ", human_ptr->name);
printf("%d years and ", human_ptr->age[0]);
printf("%d %s old.\n", m, (m==1 ? "month" : "months"));
return 0;
}
If you compare this example to the previous, you'll notice how similar it looks. The only difference is the pointer to user, human_ptr.
Every occurrence of user. has been replaced by
human_ptr-> , as well as a few brackets added for clarity.
This is similar to passing arrays to functions.
In the function declaration you need to specify the structure's type, then a name and any other arguments.
Passing pointers is a better way, but for demonstration purposes, here's a function that expects a ROBOT variable and displays some info:
#include
typedef struct robot ROBOT;
struct robot {
char *name;
int energy;
int IQ;
float velocity;
};
void info(ROBOT r); /* declare function */
int main() {
ROBOT r1 = {"Earthling Ed", 100, 231, 4.1}; /* initialize 2 ROBOTs */
ROBOT r2 = {"Toxic Tom", 150, 254, 2.5};
info(r1);
info(r2);
return 0;
}
void info(ROBOT r) {
printf("%s has %d units of energy, an IQ of %d...\n",
r.name, r.energy, r.IQ);
printf("... and a velocity of %0.1f metres per second.\n",
r.velocity);
}
Output:
Earthling Ed has 100 units of energy, an IQ of 231... |
Notice how I moved the function declaration below the typedef statement, since I used the defined type, ROBOT, in the function's argument section.
info returns nothing, hence the void data type, and expects a ROBOT variable. r is a local variable that is created whenever info is called.
The %0.1f format specifier in the second printf displays the velocity correct to 1 decimal place. The zero is the minimum field width (the least number of spaces the number will occupy when displayed on screen).
Prev:
Next:
Copyright © 2001-2003
Unauthorized copying not permitted
Designed and Developed Using
Prev:
Next:
Now suppose we want to create a function that modifies a structure. Now we'll turn to pointers...
#include
typedef struct robot ROBOT;
struct robot {
char *name;
int energy;
int IQ;
float velocity;
};
void info(ROBOT r); /* declare functions */
void harm(ROBOT *r);
int main() {
ROBOT r1 = {"Martian Martin", 140, 213, 5.1};
ROBOT r2 = {"Planetary Pete", 170, 309, 4.5};
ROBOT *ptr_r1 = &r1;
printf("Before damage: \n\n");
info(r1);
info(r2);
harm(ptr_r1);
harm(&r2);
printf("\nAfter damage: \n\n");
info(r1);
info(r2);
return 0;
}
void info(ROBOT r) {
printf("%s: Energy: %d IQ: %d ", r.name, r.energy, r.IQ);
printf("Velocity: %0.1f\n", r.velocity);
}
void harm(ROBOT *r) {
r->energy -= 20;
r->velocity -= 1;
}
Output:
Before damage: |
I would like to take this opportunity to apologize for the unimaginative names!!!
The info function is almost the same as before - just modified the way it displayed the information. I had to use two separate printfs because using one distorted the web page!
harm expects a ROBOT pointer as an argument. Then it takes the ROBOT passed in, and decreases its energy by 20, and its velocity by 1 (that should teach it a lesson).
You can see by the output that the fields have been modified when I called harm using the two different methods: passing a pointer to r1 I created earlier, or simply passing the address of r2.
You can have fields of any data type in a structure, as well as pointers and arrays AND other structures.
You must create a struct before you use it in another struct, but declaring the field is similar to declaring an array.
#include
typedef struct weapon WEAPON;
typedef struct robot ROBOT;
struct weapon {
int ammo;
char *name;
int damage;
};
struct robot {
WEAPON weapon1; /* struct within a struct */
char *name;
int energy;
};
int main() {
ROBOT boss = { {100, /* boss.weapon1.ammo */
"Laser Gun", /* boss.weapon1.name */
25}, /* boss.weapon1.damage */
"Krypton Kathy", /* boss.name */
250}; /* boss.energy */
ROBOT *ptr1;
WEAPON *ptr2;
/*boss.weapon1.ammo = 100; alternative initialization -
notice I had to put:
boss.weapon1.name = "Laser Gun";
this is AFTER declaring the 2 pointers above
boss.weapon1.damage = 25;
boss.name = "Krypton Kathy";
boss.energy = 250;*/
ptr1 = &boss;
ptr2 = &boss.weapon1;
printf("You have encountered %s.\n", ptr1->name);
printf("She has %d units of energy.\n", ptr1->energy);
printf("Her weapon is the deadly %s.\n", ptr2->name);
printf("Each hit takes up to %d units of energy off you.\n",
ptr2->damage);
printf("She has %d shots left.\n", ptr1->weapon1.ammo);
printf("You have 10 units of energy left. ");
printf("Situation critical.\n Abort mission.\n");
return 0;
}
Output:
You have encountered Krypton Kathy. |
In the definition of struct robot, you can see that I've used a struct weapon variable as one of the fields. struct weapon must been defined before struct robot, otherwise your compiler will complain about an undefined struct.
Inside main, I've declared and initialized a ROBOT variable. Look closely and you'll see that there are 2 brackets surrounding the first three items - this is because the first three items belong to the struct weapon field.
After that, I declared two pointers - one that points to the boss and the other points to the weapon1 field of boss.
The first four printf statements make use of ptr1 and ptr2 to access certain fields, but in the fifth, notice that it's ptr1->weapon1.ammo and not ptr1->weapon1->ammo. This is because weapon1 is not a pointer to a struct weapon, but an actual struct weapon field, hence the dot operator.
Prev:
Next:
Copyright © 2001-2003
Unauthorized copying not permitted
Designed and Developed Using
There are several ways by which a structure can be initialized. In
the first way, we first declare the structure variable and initialize
the fields of the structure with its name along with the variable name.
i.e., Suppose consider a structure ‘student‘
structure student {
int roll;
int class;
char name;
};
Now we declare variable for the structure student st1
Now to initialize this variable, we access the elements of the structure using the variable
i.e.,
st1.roll = 21;
strncpy (st1.name, "Albert", 10);
There can be case in which we do not initialize some elements of the structure, like in the above case, we did not initialize the element class for the variable st1. This can be considered as a big disadvantage. Because we may forget initializing some fields.
In the second way, we initialize the structure elements using the set notation.
like the variable st2 can be initialized as
struct student st2 = {22, 10, "Alan"};
Unlike the first way, where we can initialize the elements of the structure in any order, here we must remember the order of the elements in the structure. But chances of not initializing a particular element is very less.
The following program demonstrates these two ways of structure initialization. Also check the way an array of structures is initialized.
/*Different Ways of structure initialization*/#include <stdio.h>
#include <string.h>
#define NAME_LEN 25
typedef unsigned short age_t;
typedef unsigned int roll_t;
typedef struct student{
char name[NAME_LEN];
roll_t rno;
age_t age;
}student;
int main()
{
/*
* Method 1a:Commonly seen initialization of a structure
Possibly related posts: (automatically generated)
Filed under: | Tagged: , ,
[...] structures. These way utilizes the benefits of both the ways of structure initialization described here. That is we are able to initialize the elements of the structure using the set notation and also we [...]
===========================================================================
There is one more way by which we can initialize structures. These way
utilizes the benefits of both the ways of structure initialization
described .
That is we are able to initialize the elements of the structure using
the set notation and also we need not remember the order of the
elements of the structure.
Suppose consider the structure ‘student‘ we already used
struct student {
int roll;
int class;
char name;
};
Now let’s declare a variable st3 and initialize using the third method
student st3={
.name = "Mark", //Notice the equal to and the comma
.class = 10,
.roll = 1038
};
As you can see the way by which the variable st3 has been initialized. It has used the set notation and also see the order is not the same as in the case of the structure ‘student‘
The following program demonstrates this. Note the different ways by which the array of structures has been initialized
/*
*Different Ways of structure initialization
*/#include <stdio.h>
#include <string.h>
#define NAME_LEN 25
typedef unsigned short age_t;
typedef unsigned int roll_t;
typedef struct student{
char name[NAME_LEN];
roll_t rno;
age_t age;
}student;
int main()
{
/* Method 3a: Just like the Method 2a, but here you do not
* need to know order of the elements in the declaration
*/
student st3={
.name = "Mark",//Notice the equal to and comma
.age = 23,
.rno = 1038
};
printf("%s %hi %u\n\n",st3.name,st3.age,st3.rno);
/*
* Method 3b: For Initializing an array of structures
*/
student st4[]={
{
.name = "Neil",
.age = 23,
.rno = 1039 },
{
.name = "Peter",
.age = 23,
.rno = 1040
}
};
printf("%s %hi %u\n%s %hi %u\n\n",st4[0].name,st4[0].age,
st4[0].rno, st4[1].name,st4[1].age,st4[1].rno);
/*
* Method 3c : Change the order of initialization of the
* elements of the array. Normally as seen in Method 2b,
* the 0th array element is initialized then 1st, then
* 2nd and so on. So by using a variation of Method 3b,
* we can initialize the array elements in any order
*/
student st5[5]={
[3]={
.name = "Titus",
.age = 22,
.rno = 1041 },
[2]={
.name = "Stephen",
.age = 23,
.rno = 1042
}
/*As you can see only 2nd and 3rd array elements has
* been initialized and that too not in order. Such
* an initialization is useful, if we wish to allocate
* some fixed size array but only initialize some element
*/
};
printf("%s %hi %u\n%s %hi %u\n\n",st5[2].name,st5[2].age,
st5[2].rno, st5[3].name,st5[3].age,st5[3].rno);
}
------------------------------------------------------------------------------------------
Possibly related posts: (automatically generated)
Filed under: | Tagged: , , ,
------------------------------------------------------------------------------------------
« »
------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------
from:
struct student {Another way to declare the same thing is:
char *first;
char *last;
char SSN[9];
float gpa;
char **classes;
};
struct student student_a, student_b;
struct {As you can see, the tag immediately after struct is optional. But in the second case, if you wanted to declare another struct later, you couldn't.
char *first;
char *last;
char SSN[10];
float gpa;
char **classes;
} student_a, student_b;
struct student_t {Now we have created a student_t student and a student_t pointer. The pointer allows us greater flexibility (e.g. Create lists of students).
char *first;
char *last;
char SSN[10];
float gpa;
char **classes;
} student, *pstudent;
.
(period). For example, to assign the SSN of
student_a
:
strcpy(student_a.SSN, "111223333\0");
struct student *student_a;But how do we dereference the pointer to the struct and its fields? You can do it in one of two ways, the first way is:
printf("%s\n", (*student_a).SSN);This would get the SSN in student_a. Messy and the readability is horrible! Is there a better way? Of course, programmers are lazy! :)
->
.
The above example using the new operator:
printf("%s\n", student_a->SSN);
*student_a
could we start assigning things to pointer fields inside the structure?
No. You must malloc space for each individual pointer within
the structure that is being pointed to.typedef struct {Now we get rid of those silly
char *first;
char *last;
char SSN[9];
float gpa;
char **classes;
} student;
student student_a;
struct
tags. You can
use typedef
for non-structs:
typedef long int *pint32;x, y and z are all pointers to long ints.
pint32 x, y, z;
typedef
is
your friend. Use it.struct conditions {As you know, wind_chill is only calculated when it is "cold" and heat_index when it is "hot". There is no need for both. So when you specify the temp in today, feels_like only has one value, either a float for wind_chill or a float for heat_index.
float temp;
union feels_like {
float wind_chill;
float heat_index;
}
} today;
enum e_months {JAN=1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC};We are enumerating the months in a year into a type called month. You aren't creating a type, because enumerated types are simply integers. Thus the printf statement uses
typedef enum e_months month;
month currentmonth;
currentmonth = JUN; /* same as currentmonth = 6; */
printf("%d\n", currentmonth);
%d
,
not %s
.JAN=1
tells C to
make the enumeration start at 1 instead of 0.#define JAN 1
#define FEB 2
#define MAR 3
/* ... etc ... */
&
operator may be used with structs to show
addresses.->
for dereferencing the pointer.typedef
can help you clear your code up and can
help save some keystrokes.#define
statements.
===========================================================================
Just a note that this is NOT C++ compatible (it’s part of C99). Confirmed with gcc C++ 4.1.2 and Microsoft Visual Studio 2008 C++.
Is there anything else that is “C only”? Kinda bugs me, being a C++ guy.
Just check this : . This is completely C based. Hope this will help you.
Yep, those are the older ways to get the job done.
The new “set notation” way you explained here is nice though. Wish it were in C++ too. I was surprised by it because I thought C was strictly a subset of C++. Set notation proves that statement wrong. Thanks for the post about it.
I saw this utilization when I referred to the Linux Kernel. I thought this would be useful to share. If you have any suggestions, feel free to give any comment