Introduction to Computer and Program Design Lesson 4 struct and memory management James C.C. Cheng Department of Computer Science National Chiao Tung University
enum l Enumerators Grouping the constant integers enum LEVEL{ LV_EASY, LV_MID, LV_HARD, LV_GOD }; Don t forget the semicolon enum LEVEL lv; // In C++, the keyword enum is not neccesary lv = LV_EASY; cout << lv << endl; // 0 lv = LV_GOD; cout << lv << endl; // 3 lv = LEVEL::LV_HARD; cout << lv << endl; // 2 lv = 1; // Error lv = (LEVEL)1; // lv = LEVEL::LV_MID; cout << lv << endl; // 1 enum LEVEL lv2 = LV_GOD; // The follwing statements cause compiler errors in C++ lv+=1; ++lv; lv = lv + LV_MID; lv = lv + lv2; 2
enum l Enumerators enum LEVEL{ LV_EASY = 2, LV_MID, LV_HARD = 10, LV_GOD, LV_NORMAL = 0, LV_ULTIMATE }; enum LEVEL lv = LV_MID; printf("%d\n", lv); // 3 lv = LV_GOD; printf("%d\n", lv); // 11 lv = LV_ULTIMATE; printf("%d\n", lv); // 1 3
Struct l Consider a situation as follows: float BMI(float w_kg, float h_m){ return (w_kg / (h_m * h_m); } #define MAX_N 3 char* name [MAX_N][8] = {0}; float w [MAX_N] = {0.0f}, float h [MAX_N] = {0.0f}; int n = 0, i=0; We have to use three do{ // Input the personal information independent data printf("name:");scanf("%s", &name[n]); array to record the if(name[n][0]>0){ personal information printf("weight(kg):"); scanf("%f", &w[n]); printf("height(m):"); scanf("%f", &h[n]); getchar(); ++n; } else break; }while(n < MAX_N); // Showing the personal information and calculating the BMI for(i=0; i<n; ++i) printf("[%d]name:%s, w:%f, h:%f, BMI:%f\n", i, name[i], w[i], h[i], BMI(w[i], h[i])); 4
Struct l The struct can help us to group many data items to descript a complicated object struct Person{ char name[8]; float w, h; }; struct Person A[MAX_N]; // In C++, the keyword struct is not neccesary do{ // Input the personal information memset(a[n].name, 0, 8); // Clear the string printf("name:");scanf("%s", A[n].name); if(a[n].name[0]>0){ printf("weight(kg):"); scanf("%f", &A[n].w); printf("height(m):"); scanf("%f", &A[n].h); getchar();++n; } else break; }while(n < MAX_N); // Showing the personal information and calculating the BMI for(i=0; i<n; ++i) printf("[%d]name:%s, w:%f, h:%f, BMI:%f\n", i, A[i].name, A[i].w, A[i].h, BMI(A[i].w, A[i].h)); 5
Struct l Member selection operator x.m x is an object of a data type T m is a name of a member of T p->m p is a pointer m is a name of a member of T struct Person x; // In C++, the keyword struct is not neccesary sprintf(x.name, "James"); x.w = 65.0f; x.h = 1.785f; // Using. operator to access each member struct Person* px = &x; // In C++, the keyword struct is not neccesary printf("name:%s, w:%f, h:%f, BMI:%f\n", px->name, px->w, px->h, BMI(px->w, x->h)); // Using -> operator to access each member 6
Struct l If you want to pass an argument of a struct, you should pass its address or reference DO NOT use call-by-value to pass a struct to a function! void SetPerson(struct Person* px, const char *name, float w, float h){ sprintf(px->name, "%s", name); px->w = w; px->h = h; } void SetPerson(struct Person x, const char *name, float w, float h){ sprintf(x.name, "%s", name); x.w = w; x.h = h; } void PrintPerson(const struct Person* px){ printf("name:%s, w:%f, h:%f, BMI:%f\n", px->name, px->w, px->h, BMI(px->w, px->h)); } SetPerson(&x, "Bill", 90.0f, 2.0f); PrintPerson(&x); 7
Struct l Word alignment How many byte does a struct occupy? struct Person{ char name[8]; float w, h; }; printf("%d\n", sizeof(struct Person)); // 16= 8+ 4 + 4 struct Point3D{ int x, y, z; }; printf("%d\n", sizeof(struct Point3D)); // 12 Byte struct EmptyBox{ }; printf("%d\n", sizeof(struct EmptyBox)); // 0 byte in C, but 1 byte in C++ 8
Struct l Word alignment struct Person{ char gender; char name[8]; float w, h; // M : Male; F : Female }; printf("%d\n", sizeof(struct Person)); // Is the answer 17? struct Person{ char gender; // M : Male; F : Female char name[8]; float w, h; short age; }; printf("%d\n", sizeof(struct Person)); // Is the answer 19? struct Person{ char gender; // M : Male; F : Female char name[8]; short age; float w, h; }; printf("%d\n", sizeof(struct Person)); // Is the answer 19? 9
Struct l Word alignment Word: The unit of memory accessing 4 byte in 32-bit systems; 8 byte in 64-bit systems Alignment: For efficient memory accessing, some holes are added in a struct to satisfy the memory addressing. address Data struct Person{ char gender; char name[8]; float w, h; short age; }; Word 1 Word 2 Word 3 Word 4 Word 5 Word 6 Holes 10
Struct l Word alignment How to disable word alignment? Using the #prgma pack #pragma pack(push, 1) // change to 1 byte alignment and store the original setting struct Person{ }; #pragma pack(pop) // restore the original setting printf("%d\n", sizeof(struct Person)); // 19 byte address Data struct Person{ char gender; char name[8]; float w, h; short age; }; Word 1 Word 2 Word 3 Word 4 Word 5 11
Struct l Bit filed We can access each bit of member variable in a struct The member variables must be integers struct Port{ unsigned short data:8; unsigned short address:4; unsigned short flaga:1; unsigned short flagb:1; unsigned short flagc:1; unsigned short flagd:1; }; // A B C D Addr(4 bit) Data (8 bit) struct Port io; unsigned short *pn = (unsigned short *)&io; *pn = 0; // Initialization io.data = 0xFF; io.flagd = 1; printf("%d\n", *pn); // 33023 = 32768 + 255 = 0x8000 + 0x00FF 12
Struct l Struct assignment The datatype of l-value & r-value must be the same The struct should contain no any dynamic-size member Person x1, x2; sprintf(x1.name, "James"); x1.w = 65.0f; x1.h = 1.785f; x2 = x1; x2.w = 80.0f; PrintPerson(&x1); // James, 65.0, 1.785 PrintPerson(&x2); // James, 80.0, 1.785 13
Struct l Dynamic-size member struct Person{ char* name; float w, h; }; void InPerson(struct Person* px){ char buf[256] = {0}; if(px->name) free(px->name); // Release data px->name = NULL; printf("name:"); scanf("%s", buf); if(buf[0] > 0){ px->name = (char *)calloc(1, strlen(buf)+1 ); sprintf(px->name, "%s", buf); printf("weight(kg):"); scanf("%f", &px->w); printf("height(m):"); scanf("%f", &px->h); getchar(); } } struct Person A[MAX_N]; memset(a, 0, sizeof(struct Person) * MAX_N); // Clear all data do{ // Input the personal information InPerson(&A[n]); if(a[n].name) ++n; else break; }while(n < MAX_N); // Showing the personal information and calculating the BMI for(i=0; i<n; ++i) PrintPerson(&A[i]); 14
Struct l Dynamic-size member We need to design a function to copy a Person void CopyPerson(struct Person* px1, const struct Person* px2){ if(px1->name) free(px1->name); px1->name = NULL; if(px2->name ){ px1->name = (char *)calloc(1, strlen( px2->name ) + 1 ); sprintf(px1->name, "%s", px2->name ); px1->w = px2->w; px1->h = px2->h; } } What if we copy a Person by the assignment operator? Person x1, x2 InPerson(&x1); x2 = x1; x1.name[0] = X ; PrintPerson(&x1); PrintPerson(&x2); //? 15
Struct l Initialization Initializer list Student x = {0};! Student y = {"James", 75.2f, 175.6f};! // Notice the order! struct Student{! char name[8];! float w, h;! };! Uniform initialization C++11 and GCC Visual C++ 2010, 2012 not support Student x{0};! Student y{"james", 75.2f, 175.6f};! // Notice the order! 16
Union l A union object can contain only one of its members at a time. l The size of the union is at least the size of the largest member. union NewInt{ int nvalue; unsigned char btvalue[4]; }; NewInt x; x.nvalue = 256; printf("%x, %X, %X, %X\n", x.btvalue[0], x.btvalue[1], x.btvalue[2], x.btvalue[3]); // 0, 1, 0, 0 printf("%d\n", sizeof(x)); // 4 byte 17
Union l It usually combines union and struct in most cases union Vector3D{ double data[3]; struct{double x, y, z;}; struct{double r, g, b;}; struct{double u, v, w;}; }; printf( %d\n, sizeof(union Vector3D)); // 24 byte union Vector3D vec; vec.x = 0; vec.y = 10, vec.b = 30; printf( %f, %f, %f\n, vec.u, vec.v, vec.w); for(i=0;i<3; ++i) printf( %f,, vec.data[i]); 18
Dynamic memory allocation l malloc Syntax: #include <stdlib.h> void* malloc( size_t n ); where the size_t is the same as unsigned int malloc() returns a pointer to a chunk of memory of n bytes, or NULL if there is an error. The memory pointed to will be on the heap, not the stack, so make sure to free it when you are done with it. The returned pointer must be typecast l free Syntax: #include <stdlib.h> void free( void* ptr ); free() deallocates the space pointed to by ptr, freeing it up for future use. ptr must be NULL or used in a previous call to malloc(), otherwise a runtime error will occur on free(). 19
Dynamic memory allocation l calloc Syntax: #include <stdlib.h> void* calloc (size_t unit, size_t n ); calloc() returns a pointer to a chunk of memory of unit * n bytes with elements initialized t zero, or NULL if there is an error. The returned pointer must be typecast 20
Dynamic memory allocation l Example: char *pc = (char *)malloc(10); // 10 characters int *pi = (int *)malloc(sizeof(int) * 10); // 10 integers double *pd = (double *)malloc(80); // 10 doubles if( pc!= NULL && pi!= NULL && pd!= NULL){ // Check the allocation for(int i=0; i<10; ++i) pd[i] = pi[i] = pc[i] = i + 65; for(int i=0; i<10; ++i) printf("%c, %d, %f\n", pc[i], pi[i], pd[i]); // A~J } free(pc); free(pi); free(pd); l free a NULL pointer int *p; // Non-NULL free(p); // Runtime error! p = NULL; free(p); // OK! 21
Dynamic memory allocation int n = 1024 * 1024 * 128; // 128 MB double rtime; clock_t clk0, clk1; char *pc = (char *)malloc(n); // clk0 = clock(); for(int i=0; i<n; ++i) pc[i] = 0; clk1 = clock(); rtime = (double)(clk1 - clk0) / (double)clocks_per_sec; printf("iterative clearing time: %f\n", rtime); // The time is proportional to n free(pc); clk0 = clock(); pc = (char *)calloc(1,n); // re-allocating with initialization clk1 = clock(); rtime = (double)(clk1 - clk0) / (double)clocks_per_sec; printf("calloc time: %f\n", rtime); // it's constant time if the hardware supports calloc free(pc); 22
Dynamic memory allocation l Memory leaks and dangling Normal allocation Pointer Memory Pointer Memory leaks Memory Pointer Memory dangling Memory 23
Dynamic memory allocation l Memory Leaks There is no any pointer to point a allocated memory space Executing the system monitor to watch the memory usage Using two threads to execute the following program If the memory usage approach to the limitation, just stop the program char key = 0, end = 0; do{ // Create 64M byte for input buffer int n = 1024 * 1024 * 64; char *pc =(char *)malloc(n); memset(pc, 0, n); // Clear all data scanf("%s", pc); key = pc[0]; end = pc[1]; }while(key!= 'q' && key!='q' end!= 0); If the OS does not provide Garbage Collection, the allocated memory will never be released. Notice that the timing for releasing by garbage collection is when the program has been terminated. 24
Dynamic memory allocation l Memory Dangling A pointer points an unallocated memory space char *pc; *pc = 100; // Dangling pc = (char *)malloc(12); int *pi = (int *)pc; free(pi); *pc = 50; // Dangling 25
Dynamic memory allocation l Memory Manipulation Functions memset, memory setting: #include <memory.h> or #include <string.h> void* memset( void *dest, int c, size_t count ); Ø dest: the destination pointer Ø c: set the value, c & 0xFF, to each byte of dest Ø count: the number of byte to set Ø returns the value of dest int n = 3; int *p = (int *)malloc(sizeof(int)*n); memset(p, 0, sizeof(int)*n); for(int i=0; i<n; ++i) printf("%d, 0x%0X\n", p[i], p[i]); memset(p, 255, sizeof(int)*n); for(int i=0; i<n; ++i) printf("%d, 0x%0X\n", p[i], p[i]); memset(p, 65537, sizeof(int)*n); for(int i=0; i<n; ++i) printf("%d, 0x%0X\n", p[i], p[i]); memset((int *)memset(p, 0, 12) + 1, 255, 4) ; for(int i=0; i<n; ++i) printf("%d, 0x%0X\n", p[i], p[i]); 26
Dynamic memory allocation l Memory Manipulation Functions The needed time of memset int n = 1024 * 1024 * 128; // 128 MB double rtime; clock_t clk0, clk1; char *pc = (char *)malloc(n); clk0 = clock(); for(int i=0; i<n; ++i) pc[i] = 0; clk1 = clock(); rtime = (double)(clk1 - clk0) / (double)clocks_per_sec; printf("iterative clearing time: %f\n", rtime); // The needed time is proportional to n clk0 = clock(); memset(pc, 0, n); clk1 = clock(); rtime = (double)(clk1 - clk0) / (double)clocks_per_sec; printf("memset time: %f\n", rtime); /* The needed time is still proportional to n but less than iterative method */ free(pc); 27
Dynamic memory allocation l Memory Manipulation Functions memcpy, memory copy: #include <memory.h> or #include <string.h> void* memcpy(void *dest, const void *src, size_t count ); dest: the destination pointer src: the source pointer count: the number of byte to copy returns the value of dest If the source and destination overlap, this function does not ensure that the original source bytes in the overlapping region are copied before being overwritten. char s1[] = "Hello! My friend!"; // Why use char[]? const char *s2 = "Hi! Guys."; // Why use const char*? memcpy(s1+7, s2+4, 6); printf("%s\n", s1); // Hello! Guys. memcpy(s1+5, s1+7, 4); printf("%s\n", s1); // Maybe HelloGuysys., maybe not 28
Dynamic memory allocation l Memory Manipulation Functions memmove, memory move: #include <string.h> void *memmove( void *dest, const void *src, size_t count ); dest: the destination pointer src: the source pointer count: the number of byte to move returns the value of dest It is similar to memcpy, but memmove ensures the copy of overlapping region. char s1[] = 0123456789"; memcpy(s1 + 2, s1, 5); printf("%s\n", s1); // 0101234789 29
Dynamic memory allocation l Memory Manipulation Functions memcmp, memory compare: #include <memory.h> or #include <string.h> int memcmp( const void *buf1, const void *buf2, size_t n ); Ø buf1 and buf2: the pointers of memory Ø n: the number of byte to compare Ø Return Value: relationship of first n bytes of buf1 and buf2 < 0: buf1 less than buf2 0: buf1 identical to buf2 > 0: buf1 greater than buf2 char first[] = "12345678901234567890"; char second[] = "12345678901234567895"; printf( %d\n, memcmp( first, second, 19 ) ); // 0 printf( %d\n, memcmp( first, second, 20 ) ); // -1 printf( %d\n, memcmp( first + 2, second, 18 ) ); // 1 30
Dynamic memory allocation l Dynamic multi-dimension array int m=0, n=0,i,j; scanf("%d %d", &m, &n); int **pp = (int **)malloc(sizeof(int *) * m); for(i=0; i<m; ++i) pp[i] = (int *)malloc(sizeof(int) * n); for(i=0; i<m; ++i) for(j=0; j<n; ++j) pp[i][j] = i * 10 + j; for(i=0; i<m; ++i){ for(j=0; j<n; ++j) printf("%2d, ", pp[i][j]); printf("\n"); } /* Do not forget to free the allocated memory in reverse order of dimension */ for(i=0; i<m; ++i) free(pp[i]); free(pp); 31
Linked Lists l Linked List It consists of a sequence of data items such that in each item there is a pointer or a reference to link the next item. The memory addresses of elements may not be adjacent struct Node{ int data; Node *next; }; Node* NewNode(int data){ Node *p = (Node *)calloc(sizeof(node), 1); p->data = data; return p; } Node *phead = NewNode(0), *p = phead; for( int i=1; i<5; ++i, p = p->next) data next p->next = NewNode(i); // Creating the list p = phead; while(p!= NULL) { printf("%d\n", p->data); Node *ptmp = p; p = p->next; 0 1 2 3 4 free(ptmp); // Release each item } 32
Linked Lists l Doubly-Linked List Each node has two pointers, one points the next node and the other points the previous node. struct Node{ int data; Node *next, *prev;}; Node* NewNode(int data){ Node *p = (Node *)calloc(sizeof(node), 1); } p->data = data; return p; Node *phead = NewNode(0); Node *p = phead; for( int i=1; i<5; ++i, p = p->next){ p->next = NewNode(i); p->next->prev = p; } while(p!= NULL) { printf("%d\n", p->data); Node *ptmp = p; p = p->prev; } p = phead; while(p!= NULL) { printf("%d\n", p->data); Node *ptmp = p; p = p->next; free(ptmp); // Release } 0 1 2 3 33
Arrays vs. Linked Lists l Performances Random access Push back Array: O(1) Dynamic Array: O(1) Doubly-Linked list: O(n) Doubly-Linked list: O(1) Random insertion Pop back Array: O(n) Dynamic Array: O(1) Doubly-Linked list: Doubly-Linked list: O(1) Search time + O(1) Random remove Push front Dynamic Array: O(n) Array: O(n) Doubly-Linked list: O(1) Doubly-Linked list: Search time + O(1) Pop front Dynamic Array: O(n) Doubly-Linked list: O(1) where n is the number of data elements 34
Arrays vs. Linked Lists l Performances Resize (from n to m) Array: O(m) Doubly-Linked list: O(m) Clear Array: O(n) n m Copy assignment Array: O(n) Doubly-Linked list: O(n) = Doubly-Linked list: O(n) Concatenation Array: O(n) Doubly-Linked list: O(1) Swap Array: O(n) Doubly-Linked list: O(1) 35