Structs, Unions, and Enums
CS449 Spring 2016
Data Type Review
• We already learned...
– Primitive data types:
• char, int, long, float, double...
– Derived data types:
• pointers, arrays, functions...
• Today we will learn two new derived types
– Structs
– Unions
• And one “syntactic sugar” data type
– Enums
Structs
• Struct: A derived type for a collection of related
variables under one name
– Much like classes in Java with no methods
– Members can be primitives or derived types
• Useful for...
– Readability of code through conceptual grouping
– File I/O of fixed length records into structs
– Designing recursive data structures (e.g. linked lists,
trees, graphs) using pointers
Struct Example
#include <stdio.h>
>> ./a.out
struct Point{
pnt=(10, 20)
int x;
int y;
};
void print_point(const struct Point *ppnt) {
printf("pnt=(%d, %d)\n", ppnt->x, ppnt->y);
}
int main (int argc, char *argv[])
{
struct Point pnt;
pnt.x = 10;
pnt.y = 20;
print_point(&pnt);
return 0;
}
Struct Type Declaration
#include <stdio.h>
• This declares a type “struct Point”
struct Point{
int x;
– Similar to a class declaration in
Java
int y;
};
• The struct has two members “x” and
“y”
void print_point(const struct Point *ppnt) {
printf("pnt=(%d, %d)\n", ppnt->x, ppnt->y); • Note: no new variables (storage
locations) have been declared yet
}
int main (int argc, char *argv[]) – “x” and “y” are not storage
{
locations
struct Point pnt;
pnt.x = 10;
pnt.y = 20;
print_point(&pnt);
return 0;
}
Struct Variable Declaration
#include <stdio.h>
• This declares a variable “pnt” of
struct Point{
type “struct Point”
int x;
int y;
}; We can also combine type declaration
with variable declaration, but in this
void print_point(const struct Point *ppnt) {
case the type declaration and variable
printf("pnt=(%d, %d)\n", ppnt->x, ppnt->y);
declaration will be in the same scope:
}
Example:
int main (int argc, char *argv[])
struct Point {
{
struct Point pnt; int x;
pnt.x = 10; int y;
pnt.y = 20; } pnt, pnt2, *ppnt;
print_point(&pnt);
return 0;
}
Operations on Structs
#include <stdio.h>
• Four valid operations on structs
struct Point{ – The sizeof operator
int x; – The reference (&) operator
int y; – Accessing member variables
}; • “.” (dot) operator (When
accessed through struct var)
void print_point(const struct Point *ppnt) {
• “->” (arrow) operator (When
printf("pnt=(%d, %d)\n", ppnt->x, ppnt->y); accessed through pointer to
} struct var)
int main (int argc, char *argv[]) – The assignment (=) operator
(e.g. struct Point x, y; x = y;)
{
• Recall: arrays were allowed sizeof, &, and []
struct Point pnt; operators but did not allow assign
pnt.x = 10; • Passing structs to functions
pnt.y = 20; – By pointer (copy just the pointer)
print_point(&pnt); – By value (copy the entire struct)
return 0;
}
Struct Initialization
#include <stdio.h>
• Good habit to initialize all structs
struct Point{
just like any variable
int x;
• Three ways to initialize
int y;
};
– Initialize members one by one
void print_point(const struct Point *ppnt) { – Through assigning to another
printf("pnt=(%d, %d)\n", ppnt->x, ppnt->y); struct
} – Through struct initializer
int main (int argc, char *argv[]) (e.g. struct Point pnt = {10, 20};)
{ • Struct initializers can only be used at
struct Point pnt; variable declaration time, just like
pnt.x = 10;
array initializers
pnt.y = 20;
print_point(&pnt);
return 0;
}
Recursive Member Definitions
• Can structs have members of their own type?
struct A {
int x;
struct A y;
};
It is Illegal! (Think of what the size of struct A would be.)
• Can structs have members of pointers to own type?
struct A {
int x;
struct A* y;
};
It is legal! (Now think of what the size of struct A would be.)
Recursive Data Structures
• Linked List:
struct Node {
data data data
int data;
struct Node *next; next next next
};
data
• Binary Tree:
struct Node { left right
int data;
struct Node *left; data data
struct Node *right; left right left right
};
Mysterious Sizeof Struct Example
#include <stdio.h>
int main (int argc, char *argv[]) >> ./a.out
{ sizeof(A)=16
struct {
char x;
long long y;
} A;
printf("sizeof(A)=%d\n", sizeof(A));
return 0;
}
•Why 16? Why not 9 (1 + 8)?
•Because the address of “y” needs to be aligned
Inefficiency of Word-Spanning Accesses
• Word: (largest) unit of data that can be accessed in a
single memory operation
– What it means to be a “32-bit” or “64-bit” system
– Usually, size of word == register size
• Efficient to load/store register in a single operation
• Problem: what if an access spans multiple words
(lands on the boundary between two words)?
– Would result in two memory accesses
• Imagine patching together a value from two accesses
• Imagine accesses to two different cache lines, pages etc
Alignment and Word Accesses
• n-byte aligned: address is n-byte aligned
– if it is a multiple of n
• aligned (access): access is aligned
– if address is n-byte aligned
– if datum is n bytes long (where n is a power of 2)
– then, aligned accesses never span words,
if n <= word size
Alignment and Word Accesses
• aligned (primitive pointer): pointer p is aligned
– if p points to a base type of n bytes
– if p always points to an n-byte aligned address
– then, accesses to p must be all aligned accesses
• aligned (aggregate pointer): pointer p is aligned
– if pointer to each primitive member is aligned
(even after performing pointer arithmetic on p)
– For array: only requires first element to be aligned
– For struct: more complicated (members differ in size)
Padding to Enforce Struct Alignment
struct { struct {
char x; char x;
long long y; char padding[7];
} A; long long y;
} A;
struct { struct {
long long y; long long y;
char x; char x;
} A; char padding[7];
} A;
• Compiler inserts padding to prevent misaligned accesses
(Even when A is used in an array, hence 2nd case)
• Different compilers may produce different padding
Must be careful when writing/reading file using struct
Unions
• Union: A derived type for a variable that can store
values of different data types
– Syntax is exactly the same as structs
– Members can be primitive or derived types
– Each member begins at the same memory location
(Members share space)
– Update of member overwrites shared space
– Only members last written to can be read
• Useful for...
– Declaring storage space used for multiple purposes
(e.g. storing a string, int, and float at different times)
– Saves storage space
Union Example
#include <stdio.h> >> ./a.out
#include <string.h> 5
union Number { Five
int num; 1702259014
char str[100];
};
int main (int argc, char *argv[])
{
union Number number;
number.num = 5;
printf("%d\n", number.num);
strcpy(number.str, "Five");
printf("%s\n", number.str);
printf("%d\n", number.num);
return 0;
}
Sizeof Union
#include <stdio.h>
>> ./a.out
union Number {
sizeof(union Number)=100
int num;
char str[100];
};
int main (int argc, char *argv[])
{
printf("sizeof(union Number)=%d\n", sizeof(union Number));
return 0;
}
•Size of union is at least as large as the largest member.
Enums
• Enumeration: data type consisting of a set of named values called
enumerators. E.g.:
– enum Suit {Spades, Diamonds, Clubs, Hearts} suit;
• In C, an enum is an alias for an integer type (size depends on compiler)
• Enumerators are aliases for integer constants
• Above variable declaration equivalent to:
int suit;
const int Spades = 0;
const int Diamonds = 1;
const int Clubs = 2;
const int Hearts =3;
• Can assign integer values to enumerators
– enum Suit {Spades = 1, Diamonds, Clubs, Hearts}
– enum Suit {Spades = 1, Diamonds = 2, Clubs = 4, Hearts = 8}
Enum Example
>> ./a.out
suit=1
#include <stdio.h>
suit=2
int main (int argc, char *argv[])
suit=3
{
suit=4
enum Suit {Spades=1, Diamonds, Clubs, Hearts};
Hearts=4
enum Suit suit;
for(suit = Spades; suit <= Hearts; ++suit) {
printf("suit=%d\n", suit);
}
printf("Hearts=%d\n", Hearts);
return 0;
}
Operations on Enums
• Since enum is really an
#include <stdio.h> integer, any arithmetic
operation is permitted
int main (int argc, char *argv[])
• However, only comparison
{
and increment/decrement
enum Suit {Spades=1, Diamonds, Clubs, Hearts}; operators make sense
enum Suit suit; semantically
for(suit = Spades; suit <= Hearts; ++suit) {
printf("suit=%d\n", suit);
}
printf("Hearts=%d\n", Hearts);
return 0;
}
Operations on Enumerators
• Since an enumerator is really
a integer constant, any
arithmetic operation or
#include <stdio.h>
assignment to integer
int main (int argc, char *argv[]) variables
{ • However, only meant to be
enum Suit {Spades=1, Diamonds, Clubs, Hearts}; used in relation to the
enum Suit suit; original enum type
for(suit = Spades; suit <= Hearts; ++suit) {
printf("suit=%d\n", suit);
}
printf("Hearts=%d\n", Hearts);
return 0;
}
Operations on Enums
• Since enum is really an
#include <stdio.h> integer, any arithmetic
operation is permitted
int main (int argc, char *argv[])
• However, only comparison
{
and increment/decrement
enum Suit {Spades=1, Diamonds, Clubs, Hearts}; operators make sense
enum Suit suit; semantically
for(suit = Spades; suit <= Hearts; ++suit) {
printf("suit=%d\n", suit);
}
printf("Hearts=%d\n", Hearts);
return 0;
}
Review Question 1
What is wrong with the following code?
#include<stdio.h>
#include<string.h>
void modify(struct emp*);
struct emp
{
char name[20];
int age;
};
int main()
{
struct emp e = {"John", 35};
modify(&e);
printf("%s %d", e.name, e.age);
return 0;
}
void modify(struct emp *p)
{
p ->age=p->age+2;
}
The struct emp is mentioned in the prototype of the function modify()
before declaring the structure.To solve this problem declare struct emp
before the modify() prototype.
Review Question 2
What is wrong with the following code?
#include<stdio.h>
int main()
{
struct emp
{
char n[20];
int age;
};
struct emp e1 = {"Dravid",
23};
struct emp e2 = e1;
if(e1 == e2)
printf("The structure
are equal");
return 0;
}
Structure cannot be compared using '=='
Review Question 3
What is wrong with the following code?
#include<stdio.h>
int main()
{
struct emp
{
char name[25];
int age;
float bs;
};
struct emp e;
e.name = "Suresh";
e.age = 25;
printf("%s %d\n", e.name,
e.age);
return 0;
}
Incompatible types in assignment. We cannot assign a string to a struct
variable like e.name = "Suresh"; in C.
We have to use strcpy(e.name, "Suresh"); to assign a string.
Review Question 4
Was array “a” copied to function “fun”
or was it passed by its address?
#include<stdio.h>
void fun(int a[],int n);
int main()
{
int a[5]={1,2,3,4,5};
fun(a,5);
}
void fun(int a[],int n)
{
int i;
for(i=0;i<=n-1;i++)
printf("value=%d\n",a[i]);
}
It was passed by its address.
Arrays cannot be passed by copy in C (directly).
Review Question 5
Was structure “pnt” copied to function “print_point” (including its fields)
or was it passed by its address?
#include <stdio.h>
struct Point{
int x;
int y;
};
void print_point(const struct Point ppnt) {
printf("pnt=(%d, %d)\n", ppnt.x, ppnt.y);
}
int main (int argc, char *argv[])
{
struct Point pnt;
pnt.x = 10;
pnt.y = 20;
print_point(pnt);
return 0;
}
It was copied.
Review Question 6
Was array “my_array” copied to function “print_point”
or was it passed by its address?
#include <stdio.h>
struct Point{
int stuff;
int my_array[4];
};
void print_point(const struct Point ppnt) {
//...
}
int main (int argc, char *argv[])
{
struct x Point = { 8, { 1, 2, 3, 4 } };
print_point(pnt);
return 0;
}
It was copied.