Lecture – Struct and Union

Ryan Robucci

Table of Contents

Review

Structs vs Objects

Coming from an objected-oriented programming background, think of
classes as an extension of struct. Classes have data members but allow
you to restrict access to them while providing a mechanism to organize
and bundle a set of related functions. You can think of a struct as an
OOP class in which all data members are public, and which has no
methods, not even a constructor.

Structs

Struct Example

Passing and Returning Structs

Struct Function Example

// struct point is a function parameter
void printPoint( struct point aPoint) {
 printf (( %2d, %2d ), aPoint.x, aPoint.y);
}

// struct point is the return type
struct point inputPoint( ) {
 struct point p;
 printf(“please input the x-and y-coordinates:);
 scanf(%d %d”, &p.x, &p.y);
 return p;
}

int main ( ) {
 struct point endpoint; // endpoint is a struct point variable
 endpoint = inputPoint( );
 printPoint( endpoint );
 return 0;
}

Initializing a Struct

Typedef and Struct and Enum

Struct Assignment

.... / int i; char c; int i; char c; \ / memcopy 3bytes \

Do not assume that the sizeof a struct is the same as the sum of the sizeof its memebers

Alignment/padding can affect the sizeof a structure variable

Ex: 16-bit memory architecture

0x7fff4 0x7fff5 0x7fff6 0x7fff7 .... / char c; 0x7fff8 padding int i; padding int i; \ char c; 0x8fff8 0x8fff9 0x8fffa 0x8fffb 0x8fffc / memcopy 4bytes \

Struct Within a Struct

Pointers to Struct

Elements of a struct or union may be accessed through a pointer using the member dereferencing operator ->
Example:

#include <stdio.h>

typedef struct coordinate_tag{
  int x; 
  int y;
} coordinate_t, * ptrCoordinate_t;

int main(){
  
  struct coordinate_tag a;
  struct coordinate_tag * ptrA=&a;
  
  coordinate_t b;
  coordinate_t * ptrB=&b;
  
  coordinate_t c;
  ptrCoordinate_t ptrC=&c;
  
  (*ptrA).x =   1;
  ptrA->y   =   2;
  (*ptrB).x = 101;
  ptrB->y   = 102;
  (*ptrC).x = 201;
  ptrC->y   = 202;
  
  printf("%03d,%03d\n",ptrA->x,ptrA->y);
  printf("%03d,%03d\n",ptrB->x,ptrB->y);
  printf("%03d,%03d\n",ptrC->x,ptrC->y);
}
001,002
101,102
201,202

Struct pointing to its own type

typedef struct node_tag {
    int value;
    struct node_tag * next;
    struct node_tag * prev;
} node_t, * nodePtr_t;
#include <stdio.h>
typedef struct node_t {
    int value;
    struct node_t * next;
    struct node_t * prev;
} node_t, * nodePtr_t;

int main(){
  node_t a,b;
  nodePtr_t ptrA,ptrB;
  b.value=99;
  ptrA = &a;
  ptrB = &b;
  ptrA->next=&b;
  printf("%d\n",   ptrA -> next -> value);
  printf("%d\n", (*ptrA -> next) . value);
  printf("%d\n",((*ptrA) . next)-> value);
}
/tank/robucci/GIT/cmpe311/Lectures/CBasics/gtcb5j9vd_code_chunk: In function ‘main’:
/tank/robucci/GIT/cmpe311/Lectures/CBasics/gtcb5j9vd_code_chunk:10:18: warning: variable ‘ptrB’ set but not used [-Wunused-but-set-variable]
   10 |   nodePtr_t ptrA,ptrB;
      |                  ^~~~
99
99
99

Arrays of Struct

Example Struct Array Code

/* assume same point and line struct definitions */
int main( ) {
  struct line lines[5]; //same as LINE_t lines[5];
  int k;
  /* Code to initialize all data members to zero */
  for (k = 0; k < 5; k++) {
    lines[k].start.x = 0;
    lines[k].start.y = 0;
    lines[k].end.x = 0;
    lines[k].end.y = 0;
  }
  /* call the printPoint( ) function to print
  ** the end point of the 3rd line */
  printPoint( lines[2].end);
  return 0;
}

Arrays Within a Struct

Example Struct with Arrays

struct month allMonths[ 12 ] =
    {31, “JAN”}, {28, “FEB”}, {31, “MAR”},
    {30, “APR”}, {31, “MAY”}, {30, “JUN”},
    {31, “JUL”}, {31, “AUG”}, {30, “SEP”},
    {31, “OCT”}, {30, “NOV”}, {31, “DEC”}
  }; //Same as MONTH_t allMonths[12]=…;

printf(%s has %d days\n”, 
        allMonths[8].name, allMonths[8].nrDays);

printf(%c\n”,allMonths[3].name[1]);

printf(%s\n”,allMonths[3].name);

 
 
 
 
 
 
 
 
• print the data for September
 
• value of allMonths[3].name[1] is P
 
•  value of allMonths[3].name is APR

Bit Fields

Unions

Union Definition

Application of Unions

struct square { int length; };
struct circle { int radius; };
struct rectangle { int width; int height; };

enum shapeType {SQUARE, CIRCLE, RECTANGLE };

union shapes {
 struct square aSquare;
 struct circle aCircle;
 struct rectangle aRectangle;
};

struct shape {
 enum shapeType type;
 union shapes theShape;
};

double area( struct shape s) {
  switch( s.type ) {
    case SQUARE:
     return (s.theShape.aSquare.length *
             s.theShape.aSquare.length);
    case CIRCLE:
     return 3.14 * (s.theShape.aCircle.radius *
                    s.theShape.aCircle.radius);
    case RECTANGLE :
     return (s.theShape.aRectangle.height *
             s.theShape.aRectangle.width);
  }
}

Union vs. Struct

Struct Storage in memory

Example Code Demonstrating Padding

#include <stdio.h> 
#include <stdlib.h> 
 
typedef struct dummy_tag1 {  
  signed char c1; 
  int i1; 
  signed char c2; 
} big_t; 
 
typedef struct dummy_tag2 {  
  int i1; 
  signed char c1; 
  signed char c2; 
} smaller_t; 
 
typedef struct __attribute__((__packed__)) dummy_tag3 {  
  signed char c1; 
  int i1; 
  signed char c2; 
} packed_t;


int main(){ 
 
  big_t big         =  {1,-1,1}; 
  smaller_t smaller =  {-1,1,1}; 
  packed_t packed   = {1,-1,1}; 
 
  unsigned char * ptrByte;  
 
  ptrByte = (unsigned char *)&big; 
  printf("BIG:(%lu bytes):\n",sizeof(big_t)); 
  for (int i=0; i<sizeof(big_t);i++){ 
    printf("%02x\n",*ptrByte); 
    ptrByte++; 
  }  
 
 
  ptrByte = (unsigned char *)&smaller; 
  printf("SMALLER(%lu bytes):\n",sizeof(smaller_t)); 
  for (int i=0; i<sizeof(smaller_t);i++){ 
    printf("%02x\n",*ptrByte); 
    ptrByte++; 
  }  

  ptrByte = (unsigned char *)&packed; 
  printf("PACKED(%lu bytes):\n",sizeof(packed_t)); 
  for (int i=0; i<sizeof(packed_t);i++){ 
    printf("%02x\n",*ptrByte); 
    ptrByte++; 
  }  


  return 0; 
} 

  •char-int-char


•int-char-char


•gcc __packed__



  •big •smaller •packed

Wasted Space for Padding is highlighted (platform dependent). The last two bytes of smaller are garbage values, evidenced by the difference in two successive runs.

Compile

>gcc -Wall -std=c88 ./test.c

First Call

./a.out
BIG: (12 bytes)
01
00
00
00
ff
ff
ff
ff
01
00
00
00
SMALLER (8 bytes):
ff
ff
ff
ff
01
01
5e
57
PACKED(6 bytes):
01
ff
ff
ff
ff
01

Second Call

./a.out
BIG: (12 bytes)
01
00
00
00
ff
ff
ff
ff
01
00
00
00
SMALLER (8 bytes):
ff
ff
ff
ff
01
01
c9
50
PACKED(6 bytes):
01
ff
ff
ff
ff
01
 
 
 
 
 
padding




padding



padding

Example Code Examining Bytes of a Union

#include <stdio.h> 
#include <stdlib.h> 
 
typedef union dummy_tag1 {  
  signed char c1; 
  int i1; 
} T ; 
 
int main(){ 
 
  T myUnion; 
  unsigned char * ptrByte; //var. for pringting bytes 
 
  printf("sizeof(unsigned char):%d byte\n",
                        sizeof(unsigned char)); 
  printf("sizeof(int):%d bytes)\n",sizeof(int)); 
  printf("sizeof(T):%d bytes\n",sizeof(T)); 
 
  myUnion.i1 = 0; //clear all the b 
  printf("Cleared Bytes of Union Variable:\n"); 
  ptrByte = (unsigned char *)&myUnion; 
  for (int i=0; i<sizeof(T);i++){ 
    printf("%02x\n",*ptrByte++);    ptrByte++; 
  }  
 
  myUnion.c1 = -1; 
  printf("After setting member c1 to -1:\n"); 
  ptrByte = (unsigned char *)&myUnion; 
  for (int i=0; i<sizeof(T);i++){ 
    printf("%02x\n",*ptrByte);    ptrByte++; 
  }
 
  myUnion.i1 = -1; 
  printf("After setting member i1 to -1:\n"); 
  ptrByte = (unsigned char *)&myUnion; 
  for (int i=0; i<sizeof(T);i++){ 
    printf("%02x\n",*ptrByte);    ptrByte++; 
  }  
  return 0; 
} 









assignment as int to 0


assignment as char to -1 (0xFF)


assignment as int to -1

Compile:

$ gcc -Wall -std=c99 ./test.c 

Run

$ ./a.out 
sizeof(unsigned char):1 byte 
sizeof(int):4 bytes) 
sizeof(T):4 bytes 
Cleared bytes of union variable: 
00 
00 
00 
00 
After setting member c1 to -1: 
ff 
00 
00 
00 
After setting member i1 to -1: 
ff 
ff 
ff 
ff