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.
struct example{ type ex1; type ex2; };```
struct point{ int x; //x-coordinate int y; //y-coordinate };
struct point p1,p2;
p2.x, p2.y
p1.x
can be used like any other int
// 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; }
struct point p1 = {-5,7};
struct point{ int x, y;} startpoint, endpoint;
typedef struct point{ int x, y; } POINT_t;
struct point
, or just POINT_t
struct point endpoint;
POINT_t startpoint;
typedef enum months{} MONTHS_e;
Contents of struct variable may be copied to another struct variable using assignment (=)
POINT_t p1, p2; p1.x=15; p1.y = -12; p2 = p1; // same as p2.x = p1.x; p2.y = p1.y
Assignment represents copying a block of memory with multiple variables
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
A data element in a struct may be another struct
E.g line composed of points
typedef struct line{ POINT_t start,end; } LINE_t;
Q?: Given declarations below, how do you access x and y coordinates of line?
LINE_t line, line1, line2;
line.start.x = 13;
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
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
Since struct is a variable type, arrays of structs may be created like any other type
LINE_t lines[5];
Code to loop through and print each lines start point:
for(int i = 0; i<5; i++) printf(%d,%d\n”,lines[i].start.x, lines[i].start.y);
/* 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; }
Structs may contain arrays
typedef struct month{ int nrDays; char name[3+1]; }MONTH_t; MONTH_t january = {31,”JAN”};
Note: january.name[2]
is ‘N’
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
When saving space in memory or a communications message is important, we need to pack lots of information into a compact space
Struct syntax can be used to define “variables” which are as small as 1 bit in size
struct weather{ unsigned int temperature : 5; unsigned int windSpeed : 6; unsigned int isRaining : 1; unsigned int isSunny : 1; unsigned int isSnowing : 1; };
Bit fields are referenced like any other struct member
#include <stdio.h> struct weather{ unsigned int temperature : 5; unsigned int windSpeed : 6; unsigned int isRaining : 1; unsigned int isSunny : 1; unsigned int isSnowing : 1; }; int main(){ struct weather todaysWeather; todaysWeather.temperature = 0x9; todaysWeather.isSnowing = 0U; todaysWeather.windSpeed = 5U; todaysWeather.isSunny = 1U; todaysWeather.isRaining = 1U; if(todaysWeather.isRaining){ printf("%s\n", "Take your umbrella"); } for(int i=0; i<16;i++){ printf("[%2d]: %d\n", i,(((*(((char *)&todaysWeather)+(i/8)))>>(i%8))&0x1)); } }
Take your umbrella
[ 0]: 1
[ 1]: 0
[ 2]: 0
[ 3]: 1
[ 4]: 0
[ 5]: 1
[ 6]: 0
[ 7]: 1
[ 8]: 0
[ 9]: 0
[10]: 0
[11]: 1
[12]: 1
[13]: 0
[14]: 0
[15]: 1
Warning
Do not assume that bit allocation is high-to-low vs low-to-high, or where the unused space is. Such code is not portable.
Almost everything about bit fields is implementation specific, Machine and compiler specific
Type of fields may be _Bool
, signed int
, unsigned int
, or some other implementation-defined type
⚠️ for int
alone, whether it is signed or unsigned is implementation specific
signed
or unsigned
Bit fields do not have their own addresses
&todaysWeather.windSpeed
results in error: cannot take address of bit-field ‘windSpeed’
union ex{ type member1; type member2; };
struct
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); } }
typedef struct tag { char a8; int b16; } T;
sizeof(T)
is NOT NECESSARILY THE SAME AS sizeof(char) + sizeof(int)
#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.
typedef struct __attribute__((__packed__)) dummy_tag3 {It may be inefficient to use this option, requiring extra work to access and modify the value of interest
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
#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