Week 9 - Chapter 8 Arrays Review Quiz 3 ------------- Review Chapter 7 Homework ------------------------- Arrays ------ There are two major kinds of variables in C, and in other programming languages. These are: scalar - a single unit, a variable with one data item aggregate - a variable with more than one data item Different languages call these aggregate variables different things, such as lists in Perl. In C, these are called "arrays". Why even both with arrays? Much in life is organized like an array, and using this same concept can make programs easier to work with. Also, it is easier to have one aggregate variable to store a single value for, say, each day in a year, than it is to have 365 separate scalar variables. We can store any number of things in an array. 10 ints, 20 chars, 1000 longs. The only thing that really limits us is size of memory. To declare an array, there are four parts: int a[x]; The first thing is the type of data each member of the array will hold, such as int, or short, or float. The next thing is the name of the array - the name rules are the same as for any other variable. I'll use "a" in many of my examples. The next thing is the square brackets. This is really the first time we've used these characters in C. Lastly, the count of the number of members to create, noted here by "x". This number must be a constant value. For now, let's assume that x is 10. When you are addressing a single, scalar member of the array, you need to use the square brackets again to access the data: a[4] = 12; This tells the computer to modify the 5th element in the array. Why 5th? Because arrays count from 0 - very important point. So in: int a[4]; the valid values are a[0], a[1], a[2], and a[3]. By default, these values are all read/write and can be changed at any time. Arrays and for loops -------------------- Arrays and for loops seem to be perfectly matched for each other, and in many cases, when we need to process all or some of the data stored in an array, using a for loop is the best way to go. This is in no small part because arrays are stored with sequential numbering of the members, and for loops can iterate through a list of numbers one at a time, either backwards or forwards. for (i=0; i<10; i++) total += a[i]; Assuming that there are 10 members of the array, we have just accumulated a total value for the entire array. It is that simple. One very important point about arrays: C does no bounds checking. What this means is that even though you create an array with 10 members, C does not check to be sure you only access those 10 members. Here's a perfect example of a common bug: for (i=0; i<=10; i++) total += a[i]; This loop is almost exactly the same, but the < has changed to a <=. What's the effect? We look at the members of a from 0 to 10, inclusive. The actual members of a are numbered 0 through 9. By accessing a[10], we access data that is garbage, and could also belong to another variable. The best way to visualize this is to look at a memory map: ( a ) (b) +--+--+--+--+--+--+--+--+--+--+--+ | 0| 1| 2| 3| 4| 5| 6| 7| 8| 9| | +--+--+--+--+--+--+--+--+--+--+--+ As you can see, a is declared to have 10 members, 0 through 9. All that a[10] does is refer to a point is memory 10 blocks away from the beginning of a. To C, this is perfectly legal, though hardly ideal. If the members of a all have small values, and b has a large value (or vice versa), adding it to our total can greatly throw off our calculation. If we are averaging, we will probably divide by 10, not 11, and greatly influence our result. Worse is if we do something like this: for (i=0; i<=10; i++) a[i] = 0; Here, we are setting each member of the a array to 0, but we went out-of-bounds by one, and ended up setting b to 0, too. Bounds checking, then, is a very important debugging skill. With a for loop, we can easily process an array forwards, as seen above, or backwards. So long as the iterator variable is set up properly: for (i=9; i>=0; i--) printf("%d\n",a[i]); This code prints the values of a, from the end to the beginning. Each piece of the for loop setup plays a part here: the initialization starts at 9 instead of 10 - that's a manual adjustment. The test is for i>=0, to allow all members, including member 0, to be accessed. And lastly, the change is for i-- instead of i++. Initialization -------------- Arrays can be declared just like any variable, and assigned to later, such as in a for loop, just like any variable. But we can also initialize arrays, and C has some interesting tricks when it comes to array initialization. First, to initialize a normal array, simply declare it, then place the initial values in a comma-separated list surrounded by curly braces: int a[10] = { 0,1,2,3,4,5,6,7,8,9 }; a[0] will be 0, a[1] will be 1, and so on. If you leave parts of the list out, that's OK. This is the one place where C guarantees that the members that are not set, will be set to 0: int a[10] = { 9,8,7 }; In this case, we have values for a[0], [1], and [2], but for none of the other seven members. These are all automatically set to 0.We can use this to our advantage to quickly set all members of an array, of any size, to 0: int a[10] = { 0 }; This short-cut is used all the time, to initialize arrays of all sizes to 0's. It is a good one to remember and use often. Like with scalars, if you do not assign or initialize values to the members of an array, those members have garbage data. sizeof and arrays ----------------- We touched on sizeof before, and we'll return to it again for a moment now: the sizeof operator, when given the name of an array, will return the number of bytes used by the entire array. Likewise, given a single scalar member of the array, it will return the size of that one member. This can be useful: num = sizeof(a) / sizeof(a[0]); num is now the number of members of the a array, 10. Multi-dimensional arrays ------------------------ Most of the arrays we will use are single-dimension. In other words, there is only one number in square brackets after the array name. But in many instances, it is of great convenience to have an array with multiple dimensions. There might be lots of reasons for this. You might have an array of floats to store a year's worth of temperature readings, taken once per hour for a year. This array could be: float temp[8760]; or float temp[365][24]; Both use up the exact same amount of room, but accessing the data in the second can be a lot more intuitive. For example, to get the temperature at 4 am each day: for (i=0; i<365; i++) printf("%.2f\n",temp[i][4]); To get this with the single-dimension array would require a little more math than necessary - and we try to avoid math when possible. Be sure that you use the correct syntax when accessing a member of a multi- dimensional array. The temptation might be to use a spreadsheet format, such as temp[0,4], but you must remember that each member must be addressed in its own set of square brackets. Initializing Multi-dimensional arrays ------------------------------------- Initializing is very similar to a single dimension array. There are several ways it can be done, and how you do it is a personal choice. int a[2][6] = { { 0, 1, 2, 3, 4, 5 }, { 5, 4, 3, 2, 1, 0 } }; Here we have 2 lists of 6 numbers - each list is enclosed in curly braces, and all lists are separated by commas and enclosed in more curly braces. The nice thing about this method is that it follows the initialization rules of arrays: int a[2][6] = { { 0 }, { 5, 4, 3, 2 } }; Here, all members of the [0] part are set to 0, while the [1][0] through [1][3] have explicit values, and [4] and [5] are set to 0. You can also refer to the members as if they are in one big array: int a[2][6] = { 0, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 0 }; Here, the programmer must be sure to set the rows and columns properly, because the following does not work as expected: int a[2][6] = { 0, 5, 4, 3, 2 }; Here, we intended to initialize just as with the extra curly braces, but the members are not lined up properly to do that. Instead, [0][0] is 0, [0][1] is 5, and so on. Constant arrays --------------- We've not encountered this keyword before, except in our keyword list, though it has usefulness outside of arrays. However, the const keyword is probably most often used with arrays to do a few things. First, it is a visual indication that an array is being established that will not change as the program runs. And it is an instruction to the compiler that changes to the data are illegal. One example is the number of days in a month. With the exception of February, the number of days in a month never change. So we can create an array like this: const int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; Now, these are protected from changes. The following will create an error at compile time: days[2] = 29; //illegal Only during initialization can values in a const array be changed. If the values are not initialized, they are garbage and will remain garbage throughout the running of the program. One other useful const array uses strings, which we will see more of in several weeks: const char daynames[7][4] = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; Note that I used 4 instead of 3 ... this will be explained when we do cover strings. Some things you cannot do ------------------------- There are just two things that you cannot do, so far, with arrays. The first is that while arrays can be thought of as variables in many ways, there are things that you cannot do with arrays that you can do with scalars. First is assignment. You cannot assign one array to another: int a[5] = { 0, 1, 2, 3, 4 }, b[5]; b = a; //illegal The second is evaluation: if (a == b) { printf("a and b have all the same values\n"); } This test will not generate a compiler error, but it cannot ever possibly be true. To so these things, do for loops: for (i=0; i<5; i++) b[i] = a[i]; for (i=0; i<5; i++) if (a[i] != b[i]) break; if (i == 5) printf("a and b are the same\n"); Lastly, arrays cannot have negative indices. When using user input, be sure to test the value: while(1) { printf("Enter the day number to view:"); scanf("%d",&i); // Don't want negative indices nor out-of-bounds indices if (i < 0 || i > 6) { printf("Enter a number from 0 to 6 (0=Sunday, 6=Saturday)\n"); } else break; } printf("That day is: %s\n",daynames[i]);