Module-5
Structures, Unions, Enumerations, and typedef
Structures
A structure is a collection of variables referenced under one name, providing a convenient
means of keeping related information together. A structure declaration forms a template
that can be used to create structure objects (that is, instances of a structure). The variables
that make up the structure are called members.
The following code fragment shows how to declare a structure that defines the name and
address fields. The keyword struct tells the compiler that a structure is being declared.
The declaration is terminated by a semicolon. The structure tag addr identifies this particular
data structure and is its type specifier. To declare a variable of type addr, write
This declares a variable of type addr called addr_info. We can also declare one or more
objects when you declare a structure. For example,
defines a structure type called addr and declares variables addr_info, binfo, and cinfo of that
type. It is important to understand that each structure variable contains its own copies of
the structure's members. For example, the zip field of binfo is separate and distinct from the
zip field of cinfo. Changes to zip in binfo do not, for example, affect the zip in cinfo.
The general form of a structure declaration is
where either tag or structure-variables may be omitted, but not both.
Accessing Structure Members
Individual members of a structure are accessed through the use of the . operator. For
example, the following statement assigns the ZIP code 12345 to the zip field of the structure
variable addr_info declared earlier.
The object name followed by a period and the member name refers to that individual
member. The general form for accessing a member of a structure is
Structure Assignments
The information contained in one structure can be assigned to another structure of the
same type using a single assignment statement. The following program illustrates structure
assignments.
After the assignment, y.a will contain the value 10.
Arrays of Structures
Structures are often arrayed. To declare an array of structures, we must first define a
structure and then declare an array variable of that type. For example, to declare a
100-element array of structures of type addr defined earlier, write
This creates 100 sets of variables that are organized as defined in the structure addr. To
access a specific structure, index the array name. For example, to print the ZIP code of
structure 3, write
Example program:
#include<stdio.h>
struct student
{
int roll_no;
char name[20];
};
void main()
{
struct student s1[20];
int i,n;
printf(“Enter number of student:”);
scanf(“%d”,&n);
for(i=0;i<n;i++)
{
printf(“enter rollno and name student-%d”,i+1);
scanf(“%d%s,&s1[i].roll_no,s1[i].name);
}
for(i=0;i<n;i++)
{
printf(“Details of student-%d”,i+1);
printf(“RollNo=%d\tName=%s\n”,s1*i+.roll_no,s1[i].name);
}
}
STRUCTURES AND FUNCTIONS
For structures to be fully useful, we must have a mechanism to pass them to functions and
return them.
A function may access the structure in three ways
❖Passing individual members
❖Passing the entire structure
❖Passing the address of the structure
1. Passing individual members
•elementary method
•unmanageable and inefficient when structure size is large
Example
struct student
{
char name[20];
int rollno;
};
void display(char [],int);
void main()
{
struct student s1;
printf(“enter name &rollno:”);
scanf(“%s%d”,[Link],&[Link]);
display([Link],[Link]);
}
void display(char n[20],int rn)
{
printf(“Name:%s\n”,n);
printf(“Rollno:%d”,rn);
}
2. Passing the entire structure
● Any changes to structure members within the function are not reflected in the
original structure.
● Called function must return entire structure back to the calling function.
Example
void display(struct student s);
struct student
{
char name[20];
int rollno;
}
void main()
{
struct student s1;
printf(“enter name & rollno:”);
scanf(“%s%d”,[Link],&[Link]);
display(s1);
}
void display(struct student s1)
{
printf(“Name:%s\n”,[Link]);
printf(“RollNo:%d”,[Link]);
}
3. Passing the address of the structure
Example
struct student
{
char name[20];
int rollno;
}
void display(struct student *s);
main()
{
struct student s1;
printf(“enter name & rollno:”);
scanf(“%s%d”,[Link],&[Link]);
display(&s1);
}
void display(struct student *r)
{
printf(“Rollno:%d”,r->rollno);
printf(“Name:%s”,r->name);
}
Structure Pointers
C allows pointers to structures just as it allows pointers to any other type of object.
Declaring a Structure Pointer
Structure pointers are declared by placing * in front of a structure variable's name. For
example, assuming the previously defined structure addr, the following declares
addr_pointer as a pointer to data of that type:
Using Structure Pointers
There are two primary uses for structure pointers: to pass a structure to a function using call
by reference and to create linked lists and other dynamic data structures that rely on
dynamic allocation.
There is one major drawback to passing all but the simplest structures to functions: the
overhead needed to push the structure onto the stack when the function call is executed.
When a pointer to a structure is passed to a function, only the address of the structure is
pushed on the stack. This makes for very fast function calls. A second advantage, in some
cases, is that passing a pointer makes it possible for the function to modify the contents of
the structure used as the argument.
To find the address of a structure variable, place the & operator before the structure's name.
For example, given the following fragment,
this places the address of the structure person into the pointer p:
To access the members of a structure using a pointer to that structure, you must use the –>
operator. For example, this references the balance field:
The –>, usually called the arrow operator,is used in place of the dot operator when you are
accessing a structure member through a pointer to the structure.
Example
/* Display a software timer. */
#include <stdio.h>
#define DELAY 128000
struct my_time
{
int hours;
int minutes;
int seconds;
};
void display(struct my_time *t);
void update(struct my_time *t);
void delay(void);
int main(void)
{
struct my_time systime;
[Link] = 0;
[Link] = 0;
[Link] = 0;
for(;;)
{
update(&systime);
display(&systime);
}
return 0;
}
void update(struct my_time *t)
{
t->seconds++;
if(t->seconds==60)
{
t->seconds = 0;
t->minutes++;
}
if(t->minutes==60)
{
t->minutes = 0;
t->hours++;
}
if(t->hours==24) t->hours = 0;
delay();
}
void display(struct my_time *t)
{
printf("%02d:", t->hours);
printf(''%02d:", t->minutes);
printf("%02d\n", t->seconds);
}
void delay(void)
{
long int t;
/* change this as needed */
for(t=l; t<DELAY; ++t) ;
}
Arrays and Structures within Structures
A member of a structure can be e
simple variable, such as an int or double, or an aggregate type. In C, aggregate types are
arrays and structures. You have already seen one type of aggregate element: the character
arrays used in addr. A member of a structure that is an array is treated as you might expect
from the earlier examples. For example, consider this structure:
To reference integer 3,7 in a of structure y, writ
When a structure is a member of another structure, it is called a nested structure. For
example, the structure address is nested inside emp in this example:
Here, structure emp has been defined as having two members. The first is a structure of
type addr, which contains an employee's address. The other is wage, which holds the
employee's wage. The following code fragment assigns 93456 to the zip element of address.
As you can see, the members of each structure are referenced from outermost to innermost.
The C89 standard specifies that structures can be nested to at least 15 levels. The C99
standard suggests that at least 63 levels of nesting be allowed.
Example: Student with array of marks
#include <stdio.h>
struct Student {
char name[50];
int marks[5]; // Array inside structure
};
int main() {
struct Student s1 = {"Alice", {85, 90, 78, 92, 88}};
printf("Student Name: %s\n", [Link]);
printf("Marks: ");
for (int i = 0; i < 5; i++) {
printf("%d ", [Link][i]);
}
return 0;
}
Structures within Structures (Nested Structures): Example: Employee with Address
structur
#include <stdio.h>
struct Address {
char city[50];
int pincode;
};
struct Employee {
char name[50];
int id;
struct Address addr; // Nested structure
};
int main() {
struct Employee e1 = {"Bob", 101, {"Mumbai", 400001}};
printf("Employee Name: %s\n", [Link]);
printf("City: %s\n", [Link]);
printf("Pincode: %d\n", [Link]);
return 0;
}
Unions
A union is a memory location that is shared by two or more different types of variables. A
union provides a way of interpreting the same bit pattern in two or more different ways.
Declaring a union is similar to declaring a structure.
Its general form is
union tag {
type member-name;
type member-name;
type member-name;
.
.
.
} union-variables;
For example:
This declaration does not create any variables. You can declare a variable either by placing its
name at the end of the declaration or by using a separate declaration statement. To declare
a union variable called cnvt of type u_type using the definition just given, write:
In cnvt, both integer i and character ch share the same memory location. Of course, i
occupies 2 bytes (assuming 2-byte integers), and ch uses only 1. Figure 7-2 shows how i and
ch share the same address. At any point in your program, you can refer to the data stored in
a cnvt as either an integer or a character.
When a union variable is declared, the compiler automatically allocates enough storage to
hold the largest member of the union. For example, (assuming 2-byte integers) cnvt is 2
bytes long so that it can hold i, even though ch requires only 1 byte. To access a member of a
union, use the same syntax that you would use for structures: the dot and arrow operators.
If you are operating on the union directly, use the dot operator. If the union is accessed
through a pointer, use the arrow operator. For example, to assign the integer 10 to element i
of cnvt, write
In the next example, a pointer to cnvt is passed to a function:
Unions are used frequently when specialized type conversions are needed because you can
refer to the data held in the union in fundamentally different ways. For example, you might
use a union to manipulate the bytes that constitute a double in order to alter its precision or
to perform some unusual type of rounding.
To get an idea of the usefulness of a union when nonstandard type conversions are needed,
consider the problem of writing a short integer to a disk file. The C standard library defines
no function specifically designed to write a short integer to a file. Although you can write
any type of data to a file using fwrite( ), using fwrite( ) incurs excessive overhead for such a
simple operation. However, using a union, you can easily create a function called putw( ),
which writes the binary representation of a short integer to a file one byte at a time. (This
example assumes that short integers are 2 bytes long.) To see how, first create a union
consisting of one short integer and a 2 byte character array:
Now, you can use pw to create the version of putw( ) shown in the following program.
Although putw( ) is called with a short integer, it can still use the standard function putc( ) to
write each byte in the integer to a disk file one byte at a time.
Bit-Fields(Self Study)
Unlike some other computer languages, C has a built-in feature, called a bit-field, that allows
you to access a single bit. Bit-fields can be useful for a number of reasons, such as:
• If storage is limited, you can store several Boolean (true/false) variables in one byte
• Certain devices transmit status information encoded into one or more bits within a byte.
• Certain encryption routines need to access the bits within a byte.
Although these tasks can be performed using the bitwise operators, a bit-field can add more
structure (and possibly efficiency) to your code. A bit-field must be a member of a structure
or union. It defines how long, in bits, the field is to be. The general form of a bit-field
definition is
type name: length;
Here, type is the type of the bit-field, and length is the number of bits in the field. The type
of a bit field must be int, signed, or unsigned. (C99 also allows a bit-field to be of type
_Bool.)
Bit-fields are frequently used when analyzing input from a hardware device. For example,
the status port of a serial communications adapter might return a status byte organized like
this:
You can represent the information in a status byte using the following bit-field
You might use statements like the ones shown here to enable a program to determine when
it can send or receive data:
To assign a value to a bit-field, simply use the form you would use for any other type of
structure element. For example, this code fragment clears the ring field
As you can see from this example, each bit-field is accessed with the dot operator. However,
if the structure is referenced through a pointer, you must use the –> operator. You do not
have to name each bit-field. This makes it easy to reach the bit you want, bypassing unused
ones. For example, if you only care about the cts and dsr bits, you could declare the
status_type structure like this:
Also, notice that the bits after dsr do not need to be specified if they are not used. It is valid
to mix normal structure members with bit-fields. For example,
defines an employee record that uses only 1 byte to hold three pieces of information: the
employee's status, whether the employee is salaried, and the number of deductions.
Without the bit-field, this information would take 3 bytes.
Bit-fields have certain restrictions. You cannot take the address of a bit-field. Bit-fields
cannot be arrayed. You cannot know, from machine to machine, whether the fields will run
from right to left or from left to right; this implies that any code using bit-fields may have
some machine dependencies. Other restrictions may be imposed by various specific
implementations.
Enumerations (Self Study)
An enumeration is a set of named integer constants. Enumerations are common in everyday
life.
For example, an enumeration of the coins used in the United States is penny, nickel, dime,
quarter, half-dollar, dollar
Enumerations are defined much like structures; the keyword enum signals the start of an
enumeration type. The general form for enumerations is
enum tag { enumeration list } variable_list;
Here, both the tag and the variable list are optional. (But at least one must be present.) The
following code fragment defines an enumeration called coin
The enumeration tag name can be used to declare variables of its type. The following
declares money to be a variable of type coin:
Given these declarations, the following types of statements are perfectly valid:
The key point to understand about an enumeration is that each of the symbols stands for an
integer value. As such, they can be used anywhere that an integer can be used. Each symbol
is given a value one greater than the symbol that precedes it. The value of the first
enumeration symbol is 0. Therefore,
displays 0 2 on the screen. You can specify the value of one or more of the symbols by using
an initializer. Do this by following the symbol with an equal sign and an integer value.
Symbols that appear after an initializer are assigned values greater than the preceding value.
For example, the following code assigns the value of 100 to quarter:
Now, the values of these symbols are
One common but erroneous assumption about enumerations is that the symbols can be
input and output directly. This is not the case. For example, the following code fragment will
not perform as desired:
Remember, dollar is simply a name for an integer; it is not a string. Thus, attempting to
output money as a string is inherently invalid. For the same reason, you cannot use this code
to achieve the desired results:
That is, a string that contains the name of a symbol is not automatically converted to that
symbol. Actually, creating code to input and output enumeration symbols is quite tedious
(unless you are willing to settle for their integer values). For example, you need the following
code to display, in words, the kind of coin that money contains:
Sometimes, you can declare an array of strings and use the enumeration value as an index to
translate that value into its corresponding string. For example, this code also outputs the
proper string:
Of course, this only works if no symbol is initialized, because the string array must be
indexed starting at 0 in strictly ascending order using increments of 1.
Using Sizeof to Ensure Portability
You have seen that structures and unions can be used to create variables of different sizes,
and that the actual size of these variables might change from machine to machine. The
sizeof operator computes the size of any variable or type and can help eliminate
machine-dependent code from your programs. This operator is especially useful where
structures or unions are concerned.
For the following discussion, assume an implementation that has the sizes for the data types
shown here:
Therefore, the following code will print the numbers 1, 4, and 8 on the screen
The size of a structure is equal to or greater than the sum of the sizes of its members. For
example:
Here, sizeof(s_var) is at least 13 (8+4+1). However, the size of s_var might be greater
because the compiler is allowed to pad a structure in order to achieve word or paragraph
alignment. (A paragraph is 16 bytes.) Since the size of a structure may be greater than the
sum of the sizes of its members, you should always use sizeof when you need to know the
size of a structure. For example, if you want to dynamically allocate memory for an object of
type s, you should use a statement sequence like the one shown here (rather than manually
adding up the lengths of its members):
Since sizeof is a compile-time operator, all the information necessary to compute the size of
any variable is known at compile time. This is especially meaningful for unions, because the
size of a union is always equal to the size of its largest member. For example, consider
Here, the sizeof(u_var) is 8. At run time, it does not matter what u_var is actually holding.
All that matters is the size of its largest member, because any union must be as large as its
largest element.
Typedef
You can define new data type names by using the keyword typedef. You are not actually
creating a new data type, but rather defining a new name for an existing type. This process
can help make machine-dependent programs more portable. If you define your own type
name for each machine dependent data type used by your program, then only the typedef
statements have to be changed when compiling for a new environment. typedef also can aid
in self-documenting your code by allowing descriptive names for the standard data types.
The general form of the typedef statement is:
typedef type newname;
where type is any valid data type, and newname is the new name for this type. The new
name you define is in addition to, not a replacement for, the existing type name. For
example, you could create a new name for float by using
This statement tells the compiler to recognize balance as another name for float. Next, you
could create a float variable using balance:
Here, over_due is a floating-point variable of type balance, which is another word for float.
Now that balance has been defined, it can be used in another typedef. For example
tells the compiler to recognize overdraft as another name for balance, which is another
name for float. Using typedef can make your code easier to read and easier to port to a new
machine. But you are not creating a new physical type.
typedef with struct and union in C
typedef creates an alias for a data type, which can be used with struct or union to give a
new, simpler name to the complex type definition.
Using typedef with struct and union eliminates the need to use the struct or union keyword
when declaring variables, making the code more readable and concise.
For a struct, typedef creates a new type that can hold multiple, distinct variables in memory,
while for a union, it creates a new type where all members share the same memory location,
allowing only one member to be used at a time.
typedef with struct
● Purpose: To create a new type name for a structure, grouping different data types into a
single entity.
● Syntax: typedef struct { ... } new_type_name;.
Example 1:
// Without typedef
struct Point {
int x;
int y;
};
struct Point p1; // Requires 'struct' keyword
// With typedef
typedef struct {
int x;
int y;
} Point;
Point p2; // Simpler declaration, no 'struct' keyword needed [3, 6]
Example 2: typedef with Structure
#include <stdio.h>
typedef struct {
int rollNo;
char name[20];
float marks;
} Student;
int main() {
Student s1 = {101, "Rahul", 89.5};
printf("Roll No: %d\nName: %s\nMarks: %.2f\n", [Link], [Link], [Link]);
return 0;
Explanation:
● typedef struct { ... } Student; creates an alias Student for the structure type.
● No need to write struct Student s1; — simply Student s1; is enough.
● Makes the code shorter and easier to read.
typedef with union
● Purpose: To create a new type name for a union, allowing members to share the same
memory space, which is useful for saving memory when only one of several possible types is
needed at a time.
● Syntax: typedef union { ... } new_type_name;.
Example 1
// Without typedef
union Value {
int i;
float f;
};
union Value v1; // Requires 'union' keyword
// With typedef
typedef union {
int i;
float f;
} Value;
Value v2; // Simpler declaration [5, 7]
Example 2:typedef with Union
#include <stdio.h>
typedef union {
int i;
float f;
char c;
} Data;
int main() {
Data d1;
d1.i = 100;
printf("Integer: %d\n", d1.i);
d1.f = 45.67;
printf("Float: %.2f\n", d1.f); // overwrites previous value
d1.c = 'A';
printf("Character: %c\n", d1.c); // overwrites float value
return 0;
Explanation:
● typedef union { ... } Data; creates the alias Data for the union type.
● Only one member of a union holds a value at any time (shared memory).
● typedef makes union declarations more readable and consistent.