//========================================================================= #define PROGRAMMER "SOLUTIONS, NoFrills" #define PROG_CODE "soln" #define COURSE "ECE-1021" #define YEAR (2003) #define TERM "Fall" #define SECTION (0) #define ASSIGNMENT "HW #6C" #define REVISION (0) #define TITLE "The Mysterious Disappearing String" #define SUBTITLE "Extra Credit" #define EMAIL "wbahn@eas.uccs.edu" #define FILENAME "06c0soln.c" //========================================================================= // PROBLEM: // // The following program appears not to work properly. Of course, it is // doing precisely what it has been told to do, but it's behavior is not // obvious. // // So what "should" it do? It should be a loop that sets a string pointer // to point to a string and then removes the last character by replacing // replacing it with a null terminator. // // But since we set the string pointer to point to the original string // each and every time through the loop, each pass through the loop should // produce identical results (the original string with the last character // removed). // // Why doesn't it? If you can adequately explain why this program behaves // the way it does, you can extra credit on this homework assignment. Your // explanation should be written as though a student came to you with this // problem and you were charged with explaining it to them in sufficient // detail so that they understand what it happening. // // SOLUTION: // // Let's look at what it "seems" the loop should be doing: // // 1) Setting the string pointed to by "fred" to be a specific string. // 2) Print that string. // 3) Determine the length of that string. // 4) Shorten the string by one character by replacing the last character // with a null terminator. // // The actual performance of the loop is consistent with this except that // it appears that Step 1 is not being performed. However, if we comment // out that line that sets "fred", then all bets are off and the program // might start off with a random string (which then proceeds to disappear) // or it might produce a General Protection Exception or Access Violation. // So that line IS doing something, at least on the first pass through the // loop. In fact, if we move it out of the loop and place it prior to the // loop, the code produces identical results to what was originally seen. // // So the key to understanding the problem is to understand exactly what // that line of code is doing. To do this, let's be sure that we are not // making faulty assumptions (as we almost must be doing) and explicitly // state what task that line is performing. // // fred = "The mysterious disappearing string."; // // We know that "fred" is defined to be of type "char *" meaning that the // value stored in "fred" is a memory address - presumably the memory // address of the start of a string of characters that is ended with a // NULL terminator. That means that the right hand side of the assignment // operator must evaluate to a memory address, presumably the address of // the first character in the given string. // // So just where IS this string stored in memory? // // Notice that no memory appears to have been allocated for it. We only // allocated the memory for the pointer "fred", not for the string that // "fred" would eventually point to. Therefore, the compiler must have // done this for us. But how? // // At compile time, all constants are evaluated by the compiler as far // as possible. If you have the line y = (3*4+1); then the compiler will // evaluate the constants in that expression and the code produced that // actually runs will be the same as the code produced had the line simply // been y = 13;. The same is true for string constants (aka, string // literals). When the compiler sees a string literal, it allocates memory // for it, places it in that memory location, and replaces the occurance // of that string in the code with a pointer to the memory where the that // string was placed. // // With this in mind, let's look again at what our troublesome line is // doing: // // fred = "The mysterious disappearing string."; // // The compiler allocates 36 bytes of memory, say at location 524, and // stores the string starting at that location. It does this ONE time, as // part of evaluating a constant expresssion at compiler time. // The right hand side of that expression - that constant expression - // then evaluates to 524 - at compile time - and every time through the // loop "fred" is made to point to memory location 524 on the assumption // that the data - the constant data - stored there hasn't changed. // // And there is where the problem lies - our code is going out and is // actually changing the value of an expression that we told the compiler // was a constant expression - by placing a string with quotes in our code // we promised the compiler that it was a string that would never change, // just as we promised that the value (3*4+1) would never change when we // used it. The compiler accepted our promise and compiled the code under // the assumption that we would hold up our end of the bargain. As long as // we do hold up our end, the compiler knows that there is a block of // memory that it can trust to hold that string, therefore there is no // need for it to do anything other than use a pointer to that block of // memory anytime it is asked to use that particular string. // // But if we do not hold up our end of the bargain, if we go out and alter // the values in a block of memory that we promised not to alter, then we // forfeit the right to expect the program to behave in an easily // predictable manner. //== INCLUDE FILES ======================================================== #include // printf() #include // strlen() #include // rand() //== TOP LEVEL SUPPORT FUNCTION PROTOTYPES ================================ void PrintHeader(void); //== MAIN FUNCTION ======================================================== int main(void) { char *fred; int i; PrintHeader(); printf("\n"); // Print a blank line do { fred = "The mysterious disappearing string."; printf("%s\n", fred); i = strlen(fred); fred[i-1] = '\0'; } while(i>0); return(0); } //== SUPPORT FUNCTIONS ==================================================== void PrintHeader(void) { printf("========================================" "=======================================\n"); printf("Course....... %s-%i (%s %i)\n", COURSE, SECTION, TERM, YEAR); printf("Programmer... %s (%s)\n", PROGRAMMER, PROG_CODE); printf("Assignment... %s (Rev %i) (Source Code in %s)\n", ASSIGNMENT, REVISION, FILENAME); printf("Description.. %s\n", TITLE); printf(" %s\n", SUBTITLE); printf("========================================" "=======================================\n"); return; }