0% found this document useful (0 votes)
15 views23 pages

C Structures, Unions, and Enumerations Guide

The document provides an overview of structures, unions, enumerations, and typedef in C programming. It explains how structures group related variables, how unions share memory among different types, and how enumerations define named integer constants. Additionally, it covers accessing structure members, passing structures to functions, and the use of bit-fields for efficient data storage.

Uploaded by

unknownmstr007
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
15 views23 pages

C Structures, Unions, and Enumerations Guide

The document provides an overview of structures, unions, enumerations, and typedef in C programming. It explains how structures group related variables, how unions share memory among different types, and how enumerations define named integer constants. Additionally, it covers accessing structure members, passing structures to functions, and the use of bit-fields for efficient data storage.

Uploaded by

unknownmstr007
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

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.

Common questions

Powered by AI

The "sizeof" operator is significant for unions because it determines the memory allocation size based on the largest member within the union, ensuring that the union can accommodate any of its members. Regardless of which member is being accessed, the union must be large enough for the largest possible member, making "sizeof" crucial for understanding and managing memory use. For instance, if a union has members of varying types and sizes, using sizeof(union_type) ensures that memory is allocated according to the space required by the largest member .

A structure in C is a collection of variables that are stored at different memory locations, thus allowing each member to hold its own data simultaneously. In contrast, a union provides a memory location shared by all its members, meaning only one member can store data at any given time. This shared memory approach in unions is useful for interpreting the same data in various ways but restricts the simultaneous use of all members .

Typedef enhances C code clarity by allowing the use of meaningful aliases instead of redundant or complex declarations, such as replacing struct {int x; int y;} with Point. This simplifies declarations and improves readability. Typedef also aids portability as only the typedef declarations need to be adapted when porting code to new environments, decoupling the code from platform-specific data type details. This is vital for complex data types, where using typedef with structures or unions reduces syntactic clutter and clarifies the programmer's intent, streamlining maintenance and updates .

Structure padding in C occurs because the compiler aligns structure members to optimize memory access speed, potentially increasing the structure's size beyond the sum of its members' sizes. This behavior can vary across different architectures. Using the sizeof operator provides a compile-time evaluation of a structure's actual size, which can be greater than the sum of its parts due to padding. By using sizeof, developers can write portable code that dynamically allocates memory based on actual structure size, avoiding assumptions about member sizes that might lead to errors on different systems .

Passing the address of a structure enhances performance by minimizing the overhead of copying large structure data onto the stack. Instead, only the reference (or address) of the structure is used. This not only improves function call speed but also allows the function to modify the original structure data. The syntax for passing the address involves using the '&' operator to obtain the structure's address and the '->' operator to access its members within the function, as shown in the function call display(&s1) and definition void display(struct student *s) {printf("Name:%s", s->name);} .

Using pointers to structures allows for dynamic allocation of memory and the creation of linked lists, which are essential for applications requiring efficient memory management and data organization. When a pointer to a structure is passed to a function, only the address of the structure is passed, rather than the entire structure itself. This results in quicker function calls due to reduced stack overhead. Furthermore, using pointers permits the function to access and modify the actual data in the structure, enabling the creation and manipulation of complex data structures like linked lists more efficiently .

Using typedef with structures and unions is preferable when there is a need to enhance code readability, reduce complexity, and improve portability across different platforms. By creating an alias for a structure or union type, such as "Student" for a structure holding student details, typedef eliminates the need to repeatedly use the keywords "struct" or "union." This makes the code cleaner and easier to understand. Additionally, typedef can help in making the code more portable, as only the typedef declarations need to be changed when adapting the code to new machines or environments .

Nested structures in C involve defining a structure as a member of another structure, creating a hierarchy. For example, if an "Employee" structure contains an "Address" structure, the "Address" can store information like city and pincode. Accessing an innermost member, such as the "pincode" of an "Employee," involves chaining member access operators: e.g., e1.addr.pincode. This syntax reflects the order of structures from outermost (Employee) to innermost (pincode).

Enumeration types in C improve code maintenance by allowing developers to assign symbolic names to integer values, making the code more readable and maintainable. By using enums, developers can quickly map integer values to meaningful names, reducing errors and enhancing clarity. Translating enumeration values to strings enables easy debugging and output formatting, as integers are replaced with human-readable identifiers. This can be done by indexing string arrays with enumeration values, provided no initialization to non-default values occurs, simplifying code adaptations and error tracing .

Arrays of structures offer the advantage of organizing related data efficiently when handling collections with uniform structure types, such as a list of students with attributes like name and roll number. They allow for easy iteration and access using array indices. An array of structures is typically declared by first defining the structure type and then declaring an array of that type. For instance, declaring a 100-element array of a "student" structure involves specifying the structure before creating the array as in struct student s[100]; This layout makes operations on data homogeneous and streamlined .

You might also like