370 likes | 603 Views
Structured Data Types. A structure can be used to combine data of different types into a single (compound) data value . A Java class is a generalization of a C struct .
E N D
Structured Data Types • A structure can be used to combine data of different types into a single (compound) data value. • A Java class is a generalization of a C struct. • A structure is an aggregation of basic and structured data elements, possibly including pointers, but unlike a class, it contains no embedded methods (functions).
Structured Data Types • Like all data types, structures must be declared and defined. • C has three different ways to define a structure • variable structures • tagged structures • type-defined structures
Structured Data Types • A variable structure definition defines a struct variable. struct { unsigned char red; unsigned char green; Member names unsigned char blue; } pixel; variable nameDON’T FORGET THE SEMICOLON
Structured Data Types • A tagged structure definition defines a type. We can use the tag to define variables, parameters, and return types. structpoint_typestructure tag { double x; Member names double y; }; DON’T FORGET THE SEMICOLON • It is important to be aware that structpoint_typeis the name of a user defined structure type. It is not the name of a variable. • To declare a variable, use structpoint_tpoint1;
Structured Data Types • Example 2 (tagged structure definition) structpixel_typestructure tag { unsigned char red; unsigned char green; Member names unsigned char blue; }; DON’T FORGET THE SEMICOLON • Again, it is important to be aware that structpixel_typeis the name of a user defined structure type. It is not the name of a variable. • To declare a variable, use: structpixel_typepixel;
Structured Data Types • Example 3 (tagged-defined structure) structstudentRecord_tstructure tag { intstudent_number; char name[31]; member names char grade; };
typedefstatement • To enhance the readability of a program, C provides the “typedef” statement as a way to define new data types. An example of the typedef statement is: typedefintboolean; • This defines the new data type “boolean” as being equivalent to the existing type “int”. After defining this equivalence, the programmer can declare variables that will be used to represent a TRUE or FALSE value as follows: boolean flag; booleandoneTest; • Declaring a variable as boolean as opposed to int can help the reader understand the role of the variable in the program.
Structured Data Types • Another common use of typedef is to define a data type that is equivalent to a structure. For example, consider the “pixel_type” structure that was defined earlier. We could define a new data type called “pixel_t” as follows: typedefstruct { Member names unsigned char red; unsigned char green; unsigned char blue; } pixel_t; new type name • To declare a variable of the new type, use: pixel_t pixel; • These structure variable definitions create member variables red, green and blueassociated with the structure.
typedefstatement • In this case we are equating the name “pixel_t” to the structure definition. Now in a program instead of having to say: structpixel_type pixel; • to declare the structure variable “pixel”, we can instead simply say: pixel_tpixel; • Functionally there is NO difference – the advantage comes through readability and a little less typing.
Structured Data Types • Example 2 (typed-defined structure) typedefstructpoint_type{Member names double x_coordinate; double y_coordinate; } point_t; new type name • To declare variables of the type, use: point_tpoint1; point_tpoint2; • These structure variable definitions create member variables x_coordinateand y_coordinated associated with the structure.
Structured data types • Member variables of a struct are accessed using the dot operator. pixel1.red = 200; pixel1.green = 200; pixel1.blue = 0; point1.x = 10.0; // type is double point1.y = 5.5; // type is double student.student_number= 1111; // type is int student.grade= ‘A’; // type is char • These variables may be used exactly like any other variables. 11
Pointers to structures • To declare a pointer to a pixel_tuse: pixel_t *pixptr; • Before using the pointer we must always make it point to something: pixptr= (structpixel_t*)malloc(sizeof(pixel_t));
Pointers to structures • To set or reference components of the pixel_tto which it points use: (*pixptr).r = 250; // make Mr. *pixptr magenta (*pixptr).g = 0; (*pixptr).b = 250; • Warning: the C compiler is very picky about the location of the parentheses here. • Perhaps because of the painful nature of the above syntax, an alteranative “short hand” notation has evolved for accessing elements of structures through a pointer: pixptr->r = 0; // make Mr. pixptr-> cyan pixptr->g = 250; pixptr->b = 250; • This shorthand form is almost universally used.
Example typedefstructstudent_type { int id; char grade; }student_t; int main( ) { student_t student; student.id = 2201; student.grade = ‘A’; fprintf(stdout, “id: %d, grade: %c\n”, student.id, student.grade); return 0; } 14
Initializing Structures • At declaration time, members of a struct can be initialized in a manner similar to initializing array elements. pixel_t pixel = {255, 0, 100}; • The sequence of values is used to initialize the successive variables in the struct. The order is essential. • It is an error to have more initializers than variables. • If there are fewer initializers than variables, the initializers provided are used to initialize the data members. The remainder are initialized to 0 for primitive types.
Use of sizeof() with structures • The sizeof() operator should always be used in dynamic allocation of storage for structured data types and in reading and writing structured data types. However, it is somewhat easy to do this incorrectly.
Structures as parameters to functions • A struct, like an int, may be passed to a function. • The process works just like passing an int, in that: • The complete structure is copied to the stack • The function is unable to modify the caller's copy of the variable
Structures as parameters to functions #include <stdio.h> typedefstructs_type { int a; double b; } sample_t; void funct(sample_t x) { fprintf(stdout, "x.a = %d\n", x.a); fprintf(stdout, "x.b= %lf\n", x.b); x.a = 1000; x.b = 55.5; } int main() { sample_t y; y.a = 99; y.b = 11.5; funct(y); fprintf(stdout, "y.a = %d\n", y.a); fprintf(stdout, "y.b = %lf\n", y.b); return 0; }
Structures as parameters to functions Sample Run: [11:26:47] rlowe:~ [142] ./a.out x.a = 99 x.b= 11.500000 y.a = 99 y.b = 11.500000
Structures as parameters to functions • The disadvantages of passing structures by value are that copying large structures onto the stack • is very inefficient and • may even cause program failure due to stack overflow. typedefstruct { int w[1024 * 1024]; } sample_t; /* passing a struct of type sampleType above will cause */ /* 4 Terabytes to be copied onto the stack. */ sample_tfourMB; for(i = 0; i < 1000000; i++) { slow_call(fourMB); }
Passing the address of a struct • A more efficient way is to pass the address of the struct. • Passing an address requires that only a single word be pushed on the stack, regardless of how large the structure is. • Furthermore, the called function can then modify the structure.
Passing the address of a struct #include <stdio.h> typedefstruct { int a; double b; } sample_t; /* Use the * operator. funct modifies the struct */ void funct (sample_t *x) { fprintf(stdout, "x->a = %d\n", x->a); // note the use of -> operator fprintf(stdout, "x->b = %lf\n", x->b); x->a = 1000; x->b = 55.5; } int main() { sample_t y; y.a = 99; y.b = 11.5; /* use the address operator, &, in the call */ funct(&y); fprintf(stdout, "y.a = %d\n", y.a); fprintf(stdout, "y.b = %lf\n", y.b); return 0; }
Passing the address of a struct Sample run: [11:39:29] rlowe:~ [169] ./a.out x->a = 99 x->b = 11.500000 y.a = 1000 y.b = 55.500000
Passing the address of a struct • What if you do not want the recipient to be able to modify the structure? • In the prototype and function header, use the * operator. • Use the const modifier void funct(constsample_t*x) ;
Using the const modifier #include <stdio.h> typedefstructs_type { int a; double b; } sample_t; void funct(constsample_t *x) { fprintf(stdout, "x.a = %d\n", x->a); fprintf(stdout, "x.b = %d\n", x->a); x->a = 1000; x->b = 55.5; } int main( ) { sample_t y; y.a = 99; y.b = 11.5; /* to pass the address use the & operator */ funct(&y); fprintf(stdout, "y.a = %d\n", y.a); fprintf(stdout, "y.b = %d\n", y.b); return 0; } • The above code will generate a compile-time error.
Using the const modifier • [11:34:04] rlowe:~ [147] gcc struc5.c • struc5.c: In function 'funct': • struc5.c:12: error: assignment of read-only location • struc5.c:13: error: assignment of read-only location
Arrays within structures • An element of a structure may be an array typedefstruct { char name[25]; double payRate; inthoursWorked[7]; } timeCard_type; timeCard_typemyTime; • Elements of the array are accessed in the usual way: myTime.hoursWorked[5] = 6;
Arrays of structures • We can also create an array of structure types: pixel_tpixelMap[400 * 300]; student_t roster[125]; • To access an individual element of the array, pixelMap[20].red = 250; roster[50].gpa = 3.75;
Arrays of structures containing arrays • We can also create an array of structures that contain arrays timeCard_type employees[50]; • To access an individual element employees[10].hoursWorked[3] = 10;
Structures containing structures • It is common for structures to contain elements which are themselves structures or arrays of structures. In these cases, the structure definitions should apear in "inside-out" order. • This is done to comply with te usual rule of not referencing a name before it is defined.
Structures containing structures typedefstruct { unsigned char red; unsigned char green; unsigned char blue; } pixel_t; typdefstruct { intnumRows; intnumCols; pixel_tpixelData[400 * 300]; } image_t;
Structures as return values from functions • Scalar values (int, float, etc) are efficiently returned in CPU registers. • Historically, the structure assignments and the return of structures was not supported in C. • But, the return of pointers (addresses), including pointers to structures, has always been supported.
Structures as return values from functions typedefstruct { int a; double b; } sampleType; sample_t *funct ( ) { sample_t s; s->a = 1000; s->b = 55.5; return (&s); } int main() { sample_t *y; y = funct( ); fprintf(stdout, "y->a = %d\n", y->a); return 0; } rlowe[160] ./a.out returnParam.c: In function 'funct': returnParam.c:8: warning: function returns address of local variable
Structures as return values from functions • The reason for the warning is that the function is returning a pointer to a variable that was allocated on the stack during execution of the function. • Such variables are subject to being wiped out by subsequent function calls. • It is possible for a function to return a structure. • This facility depends upon the structure assignment mechanisms which copies one complete structure to another. • This avoids the unsafe condition associated with returning a pointer, but • incurs the possibly extreme penalty of copying a very large structure
Structures as return values from functions #include <stdio.h> typedefstructsample_type { int a; double b; } sample_t; sample_tfunct ( ) { sample_t s; s.a = 1000; s.b = 55.5; return s; } int main() { sample_t y; sample_t z; y = funct(); z = y; printf("%d %d\n", y.a, z.a); return 0; }
Summary • Passing/returning instances of structures potentially incurs big overhead. • Passing/returning addresses incurs almost no overhead • Accidental modifications can be prevented with const • Therefore, it is recommended that you never pass nor return an instance of a structure unless you have a very good reason for doing so. • This problem does not arise with arrays. • The only way to pass an array by value in the C language is to embed it in a structure • The only way to return an array is to embed it in a structure.