Hey folks, All the slides and handouts for Section A are available at http://www-2.cs.cmu.edu/afs/cs/academic/class/15213-f02/www/section_a.html For this week, I have moved my office hours to Tuesday, 4-5 in NSH 2504. I would like to apologize for this morning's hurried description of the C union construct. Let me clarify. (You should ignore the single sheet handout from class.) As far as the processor is concerned, memory is just one big sequence of bytes. You can load and store bytes, words (2 bytes), longs (4 bytes), doubles (8 bytes), ... And that's basically it. Programming languages like C allow you to provide some "structure" to that raw sequence of bytes. In particular, the C "struct" construct allows you to group a bunch of fields of different sizes into one unit. And as we discussed, the compiler has the job of choosing the exact layout in memory of a structure. Let's look at Problem 7 from the fall 2002 exam. GCC on IA32/Linux would layout OldSensorData as: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |code |/////|start |raw |//|data | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ NewSensorData would layout as: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |code |start|raw |//|sense|ext |/////|data | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ These structures tell us how to interpret the memory, that is it tells us the location of each field. Now we can define a union of these two structures: typedef union { OldSensorData oldData; NewSensorData newData; } UnionSensorData; A UnionSensorData object is 24 bytes long (max of the size of its two members). Remember as far as the processor is concerned, that is just a sequence of 24 bytes. Using the union one can instruct easy switch between interpreting the data as a OldSensorData struct or a NewSensorData struct. They both occupy the same bytes (except for the last 4 bytes of NewSensorData). Lets consider the following function, which allocates a UnionSensorData object and writes into its fields using the OldSensorData interpretation of the bytes. void foo() { UnionSensorData unionData; /* this zeros out all the space allocated for unionData */ bzero((void*)unionData, sizeof(unionData)); /* start writing into the structure using the OldSensorData interpretation */ unionData.oldData.code = 0x104f; unionData.oldData.start = 0x80501ab8; unionData.oldData.raw[0] = 0xe1; unionData.oldData.raw[1] = 0xe2; unionData.oldData.raw[2] = 0x8f; unionData.oldData.raw[-5] = 0xff; unionData.oldData.data = 1.5; ... After this sequence we would see the following bytes memory allocated to unionData. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |code |/////|start |raw |//|data | |4f 10|00 ff|b8 1a 50 80|e1 e2 8f|00| "1.5" | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ Now interpret that byte sequence as a NewSensorData. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |code |start|raw |//|sense|ext |/////|data | |4f 10|00 ff|b8 1a 50 80 e1|e2|8f 00| "1.5" |00 00 00 00| +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ (Luckily, we are not going to look at the "1.5" floating point number, so I am not going to covert it to a hex representation!) Now read fields from newData: unionData.newData.start -> 0xff00 unionData.newData.raw[0] -> 0xb8 unionData.newData.raw[2] -> 0x50 unionData.newData.raw[4] -> 0xe1 unionData.newData.sense -> 0x008f Okay, now you have seen how to unions work. Remember the main point of this exercise is to practice struct layout and byte orderings. Best wishes on the exam, Andrew