690 likes | 811 Views
TL: More types. Programming Fundamentals 23 Feliks Klu ź niak. Upto now we have only seen arrays of integers and characters. But an array can have elements of any concrete type. For example: const size_of_NetID = 9 ; type NetID = string[ size_of_NetID ] ;
E N D
TL: More types Programming Fundamentals 23 Feliks Kluźniak TL: More types
Upto now we have only seen arrays of integers and characters. But an array can have elements of any concrete type. For example: constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; TL: More types
Upto now we have only seen arrays of integers and characters. But an array can have elements of any concrete type. For example: constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; TL: More types
Upto now we have only seen arrays of integers and characters. But an array can have elements of any concrete type. For example: constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; If we have a procedure that copies one string to another, we can write: copy_string( “abc099000”, our_class[ 0 ] ); TL: More types
Upto now we have only seen arrays of integers and characters. But an array can have elements of any concrete type. For example: constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; If we have a procedure that copies one string to another, we can write: copy_string( “abc099000”, our_class[ 0 ] ); We might also want to write out an element: while k != size( our_class[ j ] ) do write( stdout, our_class[ j ][ k ] ); k := k + 1 od TL: More types
Upto now we have only seen arrays of integers and characters. But an array can have elements of any concrete type. For example: constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; If we have a procedure that copies one string to another, we can write: copy_string( “abc099000”, our_class[ 0 ] ); We might also want to write out an element: while k != size( our_class[ j ] )do write( stdout, our_class[ j ][ k ] ); k := k + 1 od What is the value of this expression? TL: More types
Upto now we have only seen arrays of integers and characters. But an array can have elements of any concrete type. For example: constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; If we have a procedure that copies one string to another, we can write: copy_string( “abc099000”, our_class[ 0 ] ); We might also want to write out an element: while k != size( our_class[ j ] ) do write( stdout, our_class[ j ][ k ] ); k := k + 1 od The type of our_class[ j ] is WHAT? TL: More types
Upto now we have only seen arrays of integers and characters. But an array can have elements of any concrete type. For example: constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; If we have a procedure that copies one string to another, we can write: copy_string( “abc099000”, our_class[ 0 ] ); We might also want to write out an element: while k != size( our_class[ j ] ) do write( stdout, our_class[ j ][ k ] ); k := k + 1 od The type of our_class[ j ] is NetID, so the type of our_class[ j ][ k ] is WHAT? TL: More types
Upto now we have only seen arrays of integers and characters. But an array can have elements of any concrete type. For example: constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; If we have a procedure that copies one string to another, we can write: copy_string( “abc099000”, our_class[ 0 ] ); We might also want to write out an element: while k != size( our_class[ j ] ) do write( stdout, our_class[ j ][ k ] ); k := k + 1 od The type of our_class[ j ] is NetID, so the type of our_class[ j ][ k ] is char . TL: More types
Upto now we have only seen arrays of integers and characters. But an array can have elements of any concrete type. For example: constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; We can continue to build the hierarchy of data types, for example: type Classes = Class[] ; type Course = Classes[ 6 ] ; var cs1336 : Course; … write( stdout, cs1336[ i ][ j ][ k ] ) TL: More types
Upto now we have only seen arrays of integers and characters. But an array can have elements of any concrete type. For example: constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; We might want to associate each NetID in a class with the student’s age. This can be done by keeping the age in a separate array: type Ages = IntArray[ size_of_Class ] ; varour_ages : Ages ; … write_string( stdout, our_class[ k ] ); write( stdout, ‘ ‘ ); write_int( stdout, our_ages[ k ] ); write( stdout, ‘\n’ ) TL: More types
constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; type Ages = IntArray[ size_of_Class ] ; varour_ages : Ages ; Another way to do this is to declare an array of records. TL: More types
constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; type Ages = IntArray[ size_of_Class ] ; varour_ages : Ages ; Another way to do this is to declare an array of records. Unlike an array, which is a collection of elements of the same type, a record is a collection of fields of various types. For example: type Student = record id : NetID ; age : int end TL: More types
constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; type Ages = IntArray[ size_of_Class ] ; varour_ages : Ages ; Another way to do this is to declare an array of records. Unlike an array, which is a collection of elements of the same type, a record is a collection of fields of various types. For example: type Student = record id : NetID ; age : int end NOTE: The type of a field must be concrete. TL: More types
constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; type Ages = IntArray[ size_of_Class ] ; varour_ages : Ages ; Another way to do this is to declare an array of records. Unlike an array, which is a collection of elements of the same type, a record is a collection of fields of various types. For example: type Student = record id : NetID ; age : int end ; varst : Student TL: More types
constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; type Ages = IntArray[ size_of_Class ] ; varour_ages : Ages ; Another way to do this is to declare an array of records. Unlike an array, which is a collection of elements of the same type, a record is a collection of fields of various types. For example: type Student = record id : NetID ; age : int end ; varst : Student st: st.id[0]: st.id[1]: st.id[2]: st.id[3]: st.id[4]: st.id[5]: st.id[6]: st.id[7]: st.id[8]: st.age: TL: More types
constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; type Ages = IntArray[ size_of_Class ] ; varour_ages : Ages ; Another way to do this is to declare an array of records. Unlike an array, which is a collection of elements of the same type, a record is a collection of fields of various types. For example: type Student = record id : NetID ; age : int end ; varst : Student The fields of a record variable are accessed by using “dot notation”: copy_string( “abc099000”, st.id ) ; st.age := 19 st: st.id[0]: st.id[1]: st.id[2]: st.id[3]: st.id[4]: st.id[5]: st.id[6]: st.id[7]: st.id[8]: st.age: TL: More types
constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; type Ages = IntArray[ size_of_Class ] ; varour_ages : Ages ; type Student = record id : NetID ; age : int end ; varst : Student We can, of course, have an array of records. Here is a redefinition of our example: type ArrayOfStudent = Student [] ; type Class = ArrayOfStudent[ size_of_Class ] ; varour_class : Class; TL: More types
constsize_of_NetID = 9 ; typeNetID = string[ size_of_NetID ] ; typeArrayOfNetID = NetID [] ; constsize_of_Class = 60 ; type Class = ArrayOfNetID[ size_of_Class ] ; varour_class : Class; type Ages = IntArray[ size_of_Class ] ; varour_ages : Ages ; type Student = record id : NetID ; age : int end ; type ArrayOfStudent = Student [] ; type Class = ArrayOfStudent[ size_of_Class ] ; varour_class : Class; This is more convenient than the green example above. We keep everything in one place, need not remember that there are two related arrays, can write procedures that operate on records of type Student, and pass single elements as arguments: e.g., birthday( our_class[ k ] ) . TL: More types
The type of a record field can be any concrete type. We already saw that it can be an array. It can also be a record. For example: constmax_size_of_name = 100; typeNameStore = string[ max_size_of_name ]; type Name = record nm : NameStore; ln : int% length of name in nm end; type Student = record id : NetID; name : Name; age : int end TL: More types
typeNameStore = string[ max_size_of_name ]; type Name = record nm : NameStore; ln : int% length of name in nm end; type Student = record id : NetID; name : Name; age : int end Now we can write out a student’s data as follows: procwr_student ( ref out : output, refconstst : Student ) begin var j : int; write_string( out, st.id ); write( out, ‘ ‘ ); write_int( out, age ); write( out, ‘\n’ ); j := 0; while j != st.name.lndo % write just the name, not the extra characters in nm write( out, st.name.nm[ j ] ); j := j + 1 od end TL: More types
We have great flexibility in designing our data structures. If the data structures for our application are designed carefully and with thought, most of the code more or less “writes itself”. TL: More types
We have great flexibility in designing our data structures. If the data structures for our application are designed carefully and with thought, most of the code more or less “writes itself”. If the data structures are not designed well, the code can become quite hairy. TL: More types
We have great flexibility in designing our data structures. If the data structures for our application are designed carefully and with thought, most of the code more or less “writes itself”. If the data structures are not designed well, the code can become quite hairy. If things get too complicated, it might be a good idea to throw everything into the waste paper basket and redesign from scratch. TL: More types
We have great flexibility in designing our data structures. If the data structures for our application are designed carefully and with thought, most of the code more or less “writes itself”. If the data structures are not designed well, the code can become quite hairy. If things get too complicated, it might be a good idea to throw everything into the waste paper basket and redesign from scratch. Even the most experienced programmers have to do that sometimes! TL: More types
typeNameStore = string[ max_size_of_name ] ; type Name = record nm : NameStore; ln : int% length of name in nm end; type Student = record id : NetID; name : Name; age : int end This structure is not very satisfactory! We allocate the same amount of memory for the name of each student. TL: More types
typeNameStore = string[ max_size_of_name ] ; type Name = record nm : NameStore; ln : int% length of name in nm end; type Student = record id : NetID; name : Name; age : int end This structure is not very satisfactory! We allocate the same amount of memory for the name of each student. If max_size_of_name is small, it will often be too small. If it is large, it will often be too large, and memory will be wasted. Even a length of 100 will be too small in some cases, though too large in most. TL: More types
typeNameStore = string[ max_size_of_name ] ; type Name = record nm : NameStore; ln : int% length of name in nm end; type Student = record id : NetID; name : Name; age : int end This structure is not very satisfactory! We allocate the same amount of memory for the name of each student. If max_size_of_name is small, it will often be too small. If it is large, it will often be too large, and memory will be wasted. Even a length of 100 will be too small in some cases, though too large in most. One way to solve this problem is to allocate strings of the right size from a common pool of memory as the need arises, TL: More types
typeNameStore = string[ max_size_of_name ] ; type Name = record nm : NameStore; ln : int% length of name in nm end; type Student = record id : NetID; name : Name; age : int end This structure is not very satisfactory! We allocate the same amount of memory for the name of each student. If max_size_of_name is small, it will often be too small. If it is large, it will often be too large, and memory will be wasted. Even a length of 100 will be too small in some cases, though too large in most. One way to solve this problem is to allocate strings of the right size from a common pool of memory as the need arises, and to make the records hold addresses of such strings. Such addresses are known as pointers. TL: More types
typeNameStore = string[ max_size_of_name ] ; type Name = record nm : NameStore; ln : int% length of name in nm end; type Student = record id : NetID; name : Name; age : int end s.name: s.name.nm: 100 s.name: 3 ‘J’ ‘J’ ‘o’ ‘o’ ‘e’ ‘e’ unused s.name.ln: 3 TL: More types
typeNameStore = string[ max_size_of_name ] ; type Name = record nm : NameStore; ln : int% length of name in nm end; type Student = record id : NetID; name : Name; age : int end s.name: s.name.nm: 100 s.name: 3 ‘J’ ‘J’ ‘o’ ‘o’ ‘e’ ‘e’ unused Where and how do we get this memory? s.name.ln: 3 TL: More types
Most modern programming languages have very similar runtime models. The memory occupied by a TL program is organized like this: The program, translated to machine code Code The recursion stack (we will talk about this in a minute) The stack Free storage: room for the stack or the heap to grow Free space Storage for long-lived objects (we will talk about this in a few weeks) The heap TL: More types
So we can allocate memory from the heap as the program is running, and access the allocated fragments by using their addresses. TL: More types
So we can allocate memory from the heap as the program is running, and access the allocated fragments by using their addresses. Such addresses are called pointers . TL: More types
So we can allocate memory from the heap as the program is running, and access the allocated fragments by using their addresses. Such addresses are called pointers . TL being a strongly-typed high-level language, we must of course make sure that the use of pointers does not compromise type safety, i.e., that there is no danger, e.g., of mistaking a string for an integer array. TL: More types
So we can allocate memory from the heap as the program is running, and access the allocated fragments by using their addresses. Such addresses are called pointers . TL being a strongly-typed high-level language, we must of course make sure that the use of pointers does not compromise type safety, i.e., that there is no danger, e.g., of mistaking a string for an integer array. Like in many other languages (since the introduction of Pascal), we do this by restricting each pointer to be an address of a variable (“object”) of a particular type. TL: More types
So we can allocate memory from the heap as the program is running, and access the allocated fragments by using their addresses. Such addresses are called pointers . TL being a strongly-typed high-level language, we must of course make sure that the use of pointers does not compromise type safety, i.e., that there is no danger, e.g., of mistaking a string for an integer array. Like in many other languages (since the introduction of Pascal), we do this by restricting each pointer to be an address of a variable (“object”) of a particular type. In other words, each pointer has some type, which is associated with the type of the objects that can be referenced by the pointer. TL: More types
So we can allocate memory from the heap as the program is running, and access the allocated fragments by using their addresses. Such addresses are called pointers . TL being a strongly-typed high-level language, we must of course make sure that the use of pointers does not compromise type safety, i.e., that there is no danger, e.g., of mistaking a string for an integer array. Like in many other languages (since the introduction of Pascal), we do this by restricting each pointer to be an address of a variable (“object”) of a particular type. In other words, each pointer has some type, which is associated with the type of the objects that can be referenced by the pointer. The type of a pointer is declared as follows: type TP = T ^ ; TL: More types
So we can allocate memory from the heap as the program is running, and access the allocated fragments by using their addresses. Such addresses are called pointers . TL being a strongly-typed high-level language, we must of course make sure that the use of pointers does not compromise type safety, i.e., that there is no danger, e.g., of mistaking a string for an integer array. Like in many other languages (since the introduction of Pascal), we do this by restricting each pointer to be an address of a variable (“object”) of a particular type. In other words, each pointer has some type, which is associated with the type of the objects that can be referenced by the pointer. The type of a pointer is declared as follows: type TP = T ^ ; Here, TP is the name of the declared pointer type, and T is the name of some other type, which we call the referenced type of TP : values of type TP are references to (i.e., addresses of) values of the referenced type. TL: More types
So we can allocate memory from the heap as the program is running, and access the allocated fragments by using their addresses. Such addresses are called pointers . TL being a strongly-typed high-level language, we must of course make sure that the use of pointers does not compromise type safety, i.e., that there is no danger, e.g., of mistaking a string for an integer array. Like in many other languages (since the introduction of Pascal), we do this by restricting each pointer to be an address of a variable (“object”) of a particular type. In other words, each pointer has some type, which is associated with the type of the objects that can be referenced by the pointer. The type of a pointer is declared as follows: type TP = T ^ ; Here, TP is the name of the declared pointer type, and T is the name of some other type, which we call the referenced type of TP : values of type TP are references to (i.e., addresses of) values of the referenced type. The character ^ is similar to an arrow, which is often used to illustrate references. TL: More types
For example, typeStringP = string ^ declares the type of pointers to all strings (notice that string is a generic array type). TL: More types
For example, typeStringP = string ^ declares the type of pointers to all strings (notice that string is a generic array type). We can now modify our example: type Student = record name : StringP ; id : NetId; age : int end name: 3 id: ‘J’ ‘o’ age: ‘e’ 22 TL: More types
To access the name, we must dereference the pointer contained in the field name by applying the postfix operator ^ : n := size( s . name ^ ) ; s . name ^ [ 0 ] = ‘J’ TL: More types
To access the name, we must dereference the pointer contained in the field name by applying the postfix operator ^ : n := size( s . name ^ ) ; s . name ^ [ 0 ] = ‘J’ If we want to allocate storage for a name (or for any other array) we must use a built-in procedure called newa() , with two arguments. TL: More types
To access the name, we must dereference the pointer contained in the field name by applying the postfix operator ^ : n := size( s . name ^ ) ; s . name ^ [ 0 ] = ‘J’ If we want to allocate storage for a name (or for any other array) we must use a built-in procedure called newa() , with two arguments. For example, newa( s . name , 3 ) will create a name that is 3 characters long, and assign its address to s.name . NOTE: Only the size field of the name will be initialized! TL: More types
When a pointer does not refer to any value, it should have the value nil which stands for “no address”. TL: More types
When a pointer does not refer to any value, it should have the value nil which stands for “no address”. nil is a valid value for every pointer type. TL: More types
When a pointer does not refer to any value, it should have the value nil which stands for “no address”. nil is a valid value for every pointer type. A pointer with this value is called a nullpointer. TL: More types
When a pointer does not refer to any value, it should have the value nil which stands for “no address”. nil is a valid value for every pointer type. A pointer with this value is called a nullpointer. In TL an attempt to dereference a null pointer will cause immediate termination of the program, with helpful diagnostics. TL: More types
When a pointer does not refer to any value, it should have the value nil which stands for “no address”. nil is a valid value for every pointer type. A pointer with this value is called a nullpointer. In TL an attempt to dereference a null pointer will cause immediate termination of the program, with helpful diagnostics. In other languages things might be different, e.g., in C you are likely to get a core dump. TL: More types