Slide Set 4 for ENCM 335 in Fall 2018 Steve Norman, PhD, PEng Electrical & Computer Engineering Schulich School of Engineering University of Calgary September 2018
ENCM 335 Fall 2018 Slide Set 4 slide 2/49 Contents The sizeof operator Computer Arithmetic Pointer Arithmetic Pointer Addition Pointer subtraction Null pointer values Pointer comparison Initialization of Arrays and Pointers Initializers Involving String Constants
ENCM 335 Fall 2018 Slide Set 4 slide 3/49 Outline of Slide Set 4 The sizeof operator Computer Arithmetic Pointer Arithmetic Pointer Addition Pointer subtraction Null pointer values Pointer comparison Initialization of Arrays and Pointers Initializers Involving String Constants
ENCM 335 Fall 2018 Slide Set 4 slide 4/49 The sizeof operator sizeof is an operator that can be used to determine how many bytes of memory are needed to hold the 1 s and 0 s belonging to a variable, function parameter, or other data object. Here are the allowable ways to make an expression with sizeof: sizeof expression sizeof( expression ) sizeof( type ) The first two above expressions are slightly different-looking ways to say the same thing. The last two expressions look like function calls, but they are not function calls.
ENCM 335 Fall 2018 Slide Set 4 slide 5/49 A sizeof expression produces an integer that is not of type int. The type is actually size_t, which is an unsigned integer type with enough range to represent the size of the largest variable or array you could create with whatever C programming system you are using. For most current C programming systems for laptops, desktops, and servers, size_t is a 64-bit unsigned integer type. To print the value of a sizeof expression with printf, use %zd. What would be the output of the program on the next slide, first assuming a typical desktop from around the year 2000, then assuming a typical desktop system of 2018?
#include <stdio.h> int main(void) { char c = A ; int i = 42; double d[3]; char *p; int *q; double *r; } printf("one: %zd\n", sizeof c); printf("two: %zd\n", sizeof(c)); printf("three: %zd\n", sizeof(char)); printf("sizeof(i): %zd\n", sizeof(i)); printf("sizeof(d): %zd\n", sizeof(d)); printf("pointer sizes: %zd, %zd, %zd\n", sizeof p, sizeof q, sizeof r); printf("sizeof a sizeof: %zd\n", sizeof(sizeof c)); return 0;
ENCM 335 Fall 2018 Slide Set 4 slide 7/49 Outline of Slide Set 4 The sizeof operator Computer Arithmetic Pointer Arithmetic Pointer Addition Pointer subtraction Null pointer values Pointer comparison Initialization of Arrays and Pointers Initializers Involving String Constants
ENCM 335 Fall 2018 Slide Set 4 slide 8/49 Computer arithmetic The term computer arithmetic refers generally to the systems by which computers do things such as addition, subtraction, multiplication, and division of numbers; inequality comparison of numbers using operators such as <, <=, >, and >=. Two main types of computer arithmetic are integer arithmetic and floating-point arithmetic.
ENCM 335 Fall 2018 Slide Set 4 slide 9/49 An example of integer arithmetic in C is the addition in this fragment: int a = 42, b = 9, c; c = a + b; With most current C systems, int is 32-bit two s-complement, so the addition can be done with a 32-bit integer adder, as discussed in ENEL 353.
ENCM 335 Fall 2018 Slide Set 4 slide 10/49 The C (and C++) double type is an example of a floating-point type. Here is an example of floating-point arithmetic: double d = 3.7, e = 0.2, f; f = d + e; Here 64-bit patterns approximating 3.7 and 0.2 will be added using a circuit called a floating-point adder, a circuit which is quite different from and much more complicated than an integer adder.
ENCM 335 Fall 2018 Slide Set 4 slide 11/49 Outline of Slide Set 4 The sizeof operator Computer Arithmetic Pointer Arithmetic Pointer Addition Pointer subtraction Null pointer values Pointer comparison Initialization of Arrays and Pointers Initializers Involving String Constants
ENCM 335 Fall 2018 Slide Set 4 slide 12/49 Pointer arithmetic In addition to integer and floating-point arithmetic, C and C++ support a third form of arithmetic, called pointer arithmetic. As you might guess, pointer arithmetic is arithmetic involving memory addresses. More specifically, pointer arithmetic is arithmetic involving addresses of array elements. It s crucial to understand that all addition and subtraction involving pointers in some way takes array element size into account.
ENCM 335 Fall 2018 Slide Set 4 slide 13/49 Array element organization in memory To understand pointer arithmetic, it s important to know this fact: In C and C++, it s guaranteed that arrays will be in contiguous chunks of memory, with element 0 at the lowest address of all the element addresses, element 1 at the next lowest address, and so on. Let s make sketches to show how the following arrays are organized, assuming the given addresses for elements with index 0. char a[5]; // &a[0] is 7800 int b[4]; // &b[0] is 7808 double c[3]; // &c[0] is 7824 (We ll assume sizes for int and double that are most common in C systems in 2018.)
ENCM 335 Fall 2018 Slide Set 4 slide 14/49 To repeat: In C and C++, it s guaranteed that arrays will be in contiguous chunks of memory, with element 0 at the lowest address of all the element addresses, element 1 at the next lowest address, and so on. Let s write a formula for the address of element i of an array, given the address of element 0 and the size of an element.
ENCM 335 Fall 2018 Slide Set 4 slide 15/49 Outline of Slide Set 4 The sizeof operator Computer Arithmetic Pointer Arithmetic Pointer Addition Pointer subtraction Null pointer values Pointer comparison Initialization of Arrays and Pointers Initializers Involving String Constants
ENCM 335 Fall 2018 Slide Set 4 slide 16/49 Pointer addition In C and C++ it can make sense to add together a pointer and some kind of integer. It never makes sense to add two pointers together. Pointer addition generally does this: Start with the address of an array element, and generate the address of some other element in the same array. (We ll see later that there is a slight extension to this rule.) Here s a very brief summary of addition with pointers: If p is a pointer and k is some kind of integer, then *(p + k) means exactly the same thing as p[k].
ENCM 335 Fall 2018 Slide Set 4 slide 17/49 Here s another way to say the same thing: If p is a pointer and k is some kind of integer, then p + k means exactly the same thing as &p[k]. For the example below, let s draw blob-and-arrow diagrams for points (1) and (2), then another diagram for point (2), assuming that the address of x[0] is 9008 and that the size of a double is 8 bytes. int main(void) { double x[4] = { 1.5, 2.5, 3.5, 4.5 }; double *y; y = x + 2; // (1) *(y + 1) += 3.0; // (2) return 0; }
ENCM 335 Fall 2018 Slide Set 4 slide 18/49 Pointer addition with ++ or += These are allowed, and do what you expect if you know what pointer + integer means. For example... void copy1(char *dest, const char *src); void copy2(char *dest, const char *src); int main(void) { char a[4], b[4]; copy1(a, "foo"); copy2(b, "bar"); return 0; } //... program continues on next slide...
// functions like strcpy, but without return values void copy1(char *dest, const char *src) { int i; for (i = 0; src[i]!= \0 ; i++) dest[i] = src[i]; dest[i] = \0 ; // (1) return; } void copy2(char *dest, const char *src) { for ( ; *src!= \0 ; src++, dest++) *dest = *src; *dest = \0 ; // (2) return; } Let s draw diagrams for points (1) and (2) to help us understand what src++ and dest++ do.
ENCM 335 Fall 2018 Slide Set 4 slide 20/49 In copy2 the expression src++ modifies the contents of the parameter src. The type of src is const char *. Why is src++ allowed within the function definition of copy2?
ENCM 335 Fall 2018 Slide Set 4 slide 21/49 A goofy example to help understand the rules int main(void) { int x; x = 12["0123456789ABCDEF"]; return 0; } What value does the assignment give to x, and why? (It helps to know that expression 1 + expression 2 has to mean exactly the same thing as expression 2 + expression 1 ). (Note: Don t put weird things like this in production code!)
ENCM 335 Fall 2018 Slide Set 4 slide 22/49 Outline of Slide Set 4 The sizeof operator Computer Arithmetic Pointer Arithmetic Pointer Addition Pointer subtraction Null pointer values Pointer comparison Initialization of Arrays and Pointers Initializers Involving String Constants
ENCM 335 Fall 2018 Slide Set 4 slide 23/49 Pointer subtraction: Part I Let s look at expressions of the form or pointer - integer pointer -- Assuming that the value of integer is positive, the idea in the two expressions is similar: Starting from an element in the middle or near the end of an array, go backwards, or in other words, generate the address of an element closer to element 0. For the example program on the next slide, let s make a diagram for point (1), and determine the program output.
ENCM 335 Fall 2018 Slide Set 4 slide 24/49 #include <stdio.h> int main(void) { double x[5] = { 1.1, 2.2, 3.3, 4.4, 5.5 }; double *a, *b; a = x + 4; b = a - 2; a--; // (1) printf("%f %f %f %f\n", *a, a[1], *b, *(b - 1)); return 0; } Suppose that the address of x[0] is 6000, and that the size of a double is 8 bytes. What are the numbers in a and b at point (1)?
ENCM 335 Fall 2018 Slide Set 4 slide 25/49 Pointer subtraction: Part II There is another form of subtraction involving pointers: pointer1 - pointer2 For this to make sense the two pointers must be of exactly the same type, and the values of the pointers must be addresses related to a single array. The result of the subtraction is the distance between two array elements, measured not in bytes but in number of array elements. What are the values of b and c at point (1) on the next slide?
ENCM 335 Fall 2018 Slide Set 4 slide 26/49 int main(void) { double x[4] = { 1.1, 2.2, 3.3, 4.4 }; double *a; int y[5] = { 10, 20, 30, 40, 50 }; int b, c; a = &x[3]; b = a - &x[1]; c = y - &y[4]; // (1) } return 0; To solve this problem, we DON T we need to know the size in bytes of an int or a double. Why not?
ENCM 335 Fall 2018 Slide Set 4 slide 27/49 Outline of Slide Set 4 The sizeof operator Computer Arithmetic Pointer Arithmetic Pointer Addition Pointer subtraction Null pointer values Pointer comparison Initialization of Arrays and Pointers Initializers Involving String Constants
ENCM 335 Fall 2018 Slide Set 4 slide 28/49 Null pointer values So far we ve seen examples of pointer expressions having the following kinds of values: some unpredictable address, as a result of the pointer variable being uninitialized and not yet assigned-to; the address of a variable that is not an array element; the address of an array element. There are a few other possible kinds of values for pointer expressions. One very important kind of value for a pointer expression is the null pointer value, which indicates that a pointer definitely does not point to any data at all.
ENCM 335 Fall 2018 Slide Set 4 slide 29/49 To give the null pointer value to a pointer variable, you can use either 0, which, unlike other int constants, is also a pointer constant, or NULL, which is defined in <stdio.h>, <stdlib.h>, and a few other library header files. Let s make a diagram for point (1) in the program on the next slide. Let s then go over a few different graphical ways to indicate a null pointer value.
ENCM 335 Fall 2018 Slide Set 4 slide 30/49 #include <stdlib.h> int main(void) { int a, b[3]; int *p1, *p2, *p3, *p4, *p5; p1 = &a; p2 = b; p4 = 0; p5 = NULL; // (1) // Use the pointers here... return 0; }
ENCM 335 Fall 2018 Slide Set 4 slide 31/49 Practical uses for null pointers There are many practical uses. Probably most common: A function is supposed to search for some data, and return the address of that data. To indicate that the search failed, the function can return a null pointer. Another common use: Avoiding the mysterious and inconsistent program behaviour that can result from use of uninitialized pointers. See the next slide...
ENCM 335 Fall 2018 Slide Set 4 slide 32/49 int main(void) { int a = 33; int *p; } *p = 42; //... return 0; int main(void) { int a = 33; int *p; p = 0; *p = 42; } //... return 0; Both programs are defective! What will be the difference in behaviour between the two programs?
ENCM 335 Fall 2018 Slide Set 4 slide 33/49 Outline of Slide Set 4 The sizeof operator Computer Arithmetic Pointer Arithmetic Pointer Addition Pointer subtraction Null pointer values Pointer comparison Initialization of Arrays and Pointers Initializers Involving String Constants
ENCM 335 Fall 2018 Slide Set 4 slide 34/49 Pointer comparison Consider the expression pointer1 op pointer2. If op is one of <, <=, >, or >=, the expression is only guaranteed to be meaningful if the values of both pointers are addresses related to a single array, and indicates where two array elements are located relative to each other. For example, what does this mean? pointer1 < pointer2
ENCM 335 Fall 2018 Slide Set 4 slide 35/49 // Draw diagrams for points (1) and (2). int main(void) { int a[5] = {100, 200, 300, 400, 500}; int *p, *q, temp; p = a; q = a + 4; // (1) while (p < q) { temp = *p; *p = *q; *q = temp; p++; q--; } } // (2) return 0;
ENCM 335 Fall 2018 Slide Set 4 slide 36/49 Pointer comparison with == and!= This expression... pointer1 == pointer2... has a value of 1 if both pointers point to the same thing, and also has a value of 1 if both pointers are null. Otherwise the value is 0. So what does!= mean when placed between two pointer expressions?
ENCM 335 Fall 2018 Slide Set 4 slide 37/49 Pointers pointing just past the last element of an array It is allowable and sometimes useful to generate the address of an array element one element past the last actual element of an array such an address can safely be used in pointer comparisons. It is not safe to try to use such an address to access data! The above two points are illustrated on the next slide...
ENCM 335 Fall 2018 Slide Set 4 slide 38/49 int main(void) { int a[3]; int *p = a, *beyond = a + 3; // (1) while (p!= beyond) { // Comparison is SAFE. *p = 789; p++; } // (2) // The commented-out statement below is NOT SAFE! // *p = 456; } return 0; Let s draw diagrams for points (1) and (2), then make a few related notes.
ENCM 335 Fall 2018 Slide Set 4 slide 39/49 Do you need to know about pointer arithmetic? Reasons for a NO answer: Anything you can do with pointer arithmetic in C can be done just as efficiently using square brackets ([ and ]) and indexes. Reasons for a YES answer: You will be tested on pointer arithmetic on the Quiz #2, the midterm, and the final exam. You will be expected to be comfortable with pointer arithmetic when you start ENCM 369. There is a lot of pointer arithmetic in real-world C code. Iterator types, used with C++ container types, are much easier to understand if you already understand C pointer arithmetic.
ENCM 335 Fall 2018 Slide Set 4 slide 40/49 Outline of Slide Set 4 The sizeof operator Computer Arithmetic Pointer Arithmetic Pointer Addition Pointer subtraction Null pointer values Pointer comparison Initialization of Arrays and Pointers Initializers Involving String Constants
ENCM 335 Fall 2018 Slide Set 4 slide 41/49 Initialization versus assignment The = operator in C can be used for initialization and assignment. These two things are not exactly the same. Let s write down carefully what the difference is. The distinction is not very important when we work with variables of simple types like int and double let s do a quick example of that. The distinction is important when we look at initialization and assignment involving pointers, arrays, and/or string constants.
ENCM 335 Fall 2018 Slide Set 4 slide 42/49 Initialization of pointers Consider this code fragment: int a, b; int *p = &a; int *q; q = &b; *q = 99; Let s write down exactly what each of the three above uses of = mean. What does this mean? int *p = 0; How about this? int *p = 42;
ENCM 335 Fall 2018 Slide Set 4 slide 43/49 Initializers for arrays Initializers for array variables usually involve { and }, which are called left brace and right brace. (There is another kind of initializer for arrays of char we ll get to that special case soon, but not right away.) There are two main cases to consider: number of elements in array is not explicitly specified; number of elements in array is explicitly specified.
ENCM 335 Fall 2018 Slide Set 4 slide 44/49 Number of elements not explicitly specified This is pretty easy to explain with a couple of examples: int x[ ] = {10, 20, 30, 40}; double y[ ] = {1.5, 2.5, 3.5}; How many elements will x and y have, and what will their initial values be?
ENCM 335 Fall 2018 Slide Set 4 slide 45/49 Number of elements explicitly specified Now we need to know some not-totally-obvious rules... int a[3] = {111, 222, 333}; int b[4] = {444, 555}; double c[4] = {1.0}; int d[3] = {1000, 2000, 3000, 4000}; How many elements will a, b, c and d have, and what will their initial values be?
ENCM 335 Fall 2018 Slide Set 4 slide 46/49 Outline of Slide Set 4 The sizeof operator Computer Arithmetic Pointer Arithmetic Pointer Addition Pointer subtraction Null pointer values Pointer comparison Initialization of Arrays and Pointers Initializers Involving String Constants
ENCM 335 Fall 2018 Slide Set 4 slide 47/49 Initializers involving string constants Before we discuss this kind of initializer, let s review a rule about function parameter types, and think about an apparently similar situation for variables. As a function parameter type declaration, type foo[ ] means exactly the same thing as type *foo. What is the type of foo here? So, do var1 and var2 have the same type in these two variable declarations? type var1[ ] = initializer 1 ; type *var2 = initializer 2 ;
ENCM 335 Fall 2018 Slide Set 4 slide 48/49 Let s draw a diagram for point (1). Then, let s write down a few remarks about the variables s and t. int main(void) { char *s = "ick"; char t[ ] = "meh"; // (1) //... more code... return 0; }
ENCM 335 Fall 2018 Slide Set 4 slide 49/49 Does it seem to you that programming in C and C++ requires knowledge of an unnecessarily large number of unnecessarily weird rules? Dennis Ritchie, the late designer of C, and Bjarne Stroustrup, the original designer of C++ and still an important figure in the C++ community, have essentially admitted in essays, interviews, and books that their languages have features they ve wished they could take back. On the other hand, it s pretty clear from the record that both designers were proud of the many things they got right. That s justified by the wide adoption and continued use of C and C++!