![]() | Physics and Astronomy |
Back to top
Computational Physics - RevisionThe computer stores the values of its variables in its memoryAt the core of any computer are its processor unit (CPU) and its memory (RAM - for Random Access Memory). Memory is measured in bytes and you can think of it being arranged in a block with the first byte numbered one, the second two, etc..Typically it takes four bytes (but may be as little as two bytes on some systems) to store an int, so when the computer encounters a line like: int i, j;it reserves two chunks of four bytes each to store the values of these two variables: ------------------------------------------------- Byte number: | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | ------------------------------------------------- Used for: | <-------- i --------> | <-------- j --------> | -------------------------------------------------In this example and all the examples below we have assumed four byte ints and we have arbitrarily assumed that the compiler has decided to put i and j next to each other with i beginning in memory location 400. Notice how, as far as the computer is concerned, references to variable names such as i and j are just ways of referring to a particular part of the memory. Steps for the simple Assignment.eg j = i + 1;
Short cutsC programmers love to express things compactly. Some examples:
You can use the *= type notation for any of +, -, *, /, <<, >> or % . Experienced C programmers use the above notations all the time, both because they express a simple concept ("x equals y equals one", "multiply x by y") very simply and because these expressions arise so frequently that you very soon get to recognise them. You will never see a line like any of those in the left hand column in a 'real' C program and for this reason, we shall require you to use these shortcuts wherever they appear in a statement on their own. When they appear in combination or as part of a more complicated statement you should feel free to use either form, depending on which you feel is clearer. For example, either of: y *= x; x = y;or x = y *= x;is fine. Personally I feel the two line version is clearer. Use shortcuts to make things simpler and clearer, not more confusing. The % operatorIf i is a positive integer and j a strictly positive integer i % j gives the remainder:7 % 2 == 1 9 % 5 == 4 6 % 3 == 0It's best not used with negative numbers. ArraysArrays give you the ability to define a collection of variables all of the same type and to be able to refer to them all by the same name with a numerical subscript as in this rather silly example:main() { int i, iarray[8]; iarray[0] = 1; iarray[3] = 7; i = 2; iarray[2*i+1] = iarray[3]; }Notice a few things:
When you specify an array of length, say, ten the machine reserves a contiguous block of memory large enough to store the values of ten variables - forty bytes in this case. iarray[0] then refers to the first four byte chunk, iarray[1] to the second four byte chunk, and so on: ------------------------------------------------- Byte number: | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | ------------------------------------------------- Used for: | <---- iarray[0] ----> | <---- iarray[1] ----> | -------------------------------------------------This is why you can use an array element anywhere where you use a variable - to the computer they are both just ways of specifying a chunk of memory and it doesn't care how you did it. Multidimensional arraysArrays can have as many dimensions as you like as in this snippet:int a[3][2]; /* Two dimensional array of integers */ a[2][1] = -10;If the compiler decide to store the array starting at byte number forty in its memory the elements of the array would then stored in memory as: ------------------------------------------------------------------ | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | ------------------------------------------------------------------ | <--- a[0][0] ---> | <--- a[0][1] ---> | <--- a[1][0] ---> | etc. ------------------------------------------------------------------Notice the order it stores them in: just as the number 10 is followed by 11 not 21 so a[1][0] is followed by a[1][1] not a[2][0]. NB: statically-declared multidimensional arrays like these are used much less than dynamically allocated ones which we discuss in the next lecture. Characters and stringsIn C single characters are contained in single quotes ('a'), strings of characters in double quotes("abc"). The special value zero ('\0') means "end of string". ('\0' is very different from '0'!)Strings are stored as an array of characters: char string[4] = "abc";Notice that string is an array of length four, not three. Its elements have the following values:
If statementsAn if statement consists of a logical test followed by a block of statements that are only executed if the test is true (ie non-zero). The simplest if statement looks like this:if (i == j) { /* Statements here only get executed if i is equal to j */ }(The notation == for "logical equals" is slightly confusing: i = j means "make i equal to the current value of j", whereas i == j is a test for "is i equal to j?".) If the statement in parentheses after the 'if' is true the statements inside the curly brackets (called braces) are executed, if not they are ignored. It is possible to specify alternatives if the first statement is not true: if (i == j) { /* statements here */ } else if ( i < j ) { /* More statements here */ } else if (i == 0 && j > 1 ) { /* More statements here */ } else { /* Only executed if all the others fail */ }Note:
A common mistakeif (i = j) { /* statements here */ }This is perfectly legal, what does it do? Relational operatorsC provides the following relational operators:
if ( b*b > 4*a*c && a != 0 ) return (-b + sqrt(b*b - 4*a*c))/(2*a);In the above list operators on the top lines have higher precedence (bind more tightly) than the lower lines. Items on the same line have equal precedence. For example:
i == j && j < 0 || i != j && j > 0is equivalent to: (i == j && j < 0) || (i != j && j > 0) C doesn't bother to have a separate data type for logical expressions, it just treats them as integers. The logical expressions above return the value one if they are true and zero if they are false. Conversely, C treats any integer expression with a value of zero as being false, any other value is treated as true. If the expression given is not an integer it is converted to one. The switch statementThe switch statement chooses between one of several (integer) alternatives.switch (i) { case 'a': printf("a\n"); break; case 'b': case 'c': printf("b or c\n"); break; case 'd': printf("d\n"); /* Fall through */ case 'e': printf("d or e\n"); break; default: printf("Something else\n"); }Something of a mis-feature is the fact that by default control falls through to the next case (as in the case 'd' above). Personally, if I want this to happen I comment it as above. LoopsThe while loopA while loop looks like this:while (expression) { /* Commands in here */ }Here expression is any integer expression, eg i+j or k != m, just as in the if statement above. When the program runs, if the value of the expression is zero the entire contents of the braces {} are skipped, if the value of the expression is anything except zero they are executed. In the latter case the compiler then goes back, re-evaluates the test expression and tries again. Naturally, if the contents of the loop do nothing to change the value of the controlling expression the loop will continue for ever!
Steps for the while loop The do .. while loopThe do .. while loop does the same as the while loop but in the opposite order in that the block of commands is executed before the test is made. Thus the contents are always run once even if the test is false.do { printf("Please enter an integer from 1 to %d\n", j); scanf("%d", &i); } while (i < 1 || i > j);The do .. while loop is not used very much, the above is the most common case.
Steps for the do .. while loopThe for loopThe for loop consists of an initialisation, a test and an update (which is executed after the statement block) all in one statement. We will show this loop with a single statement in the loop, allowing us to omit the optional braces:for (i = 0; i < 10; ++i) iarray[i] = i*i;This is a classic use of the for statement - doing something on each element of an array. Notice:
Steps for the for loopInfinite loopsEither of these gives you a loop that never finishes:while (1) { /* Infinite loop here */ }
for(;;) { /* Infinite loop here */ }The first is more intuitive but the second is the more commonly used. It uses the fact, not mentioned before, that any of the three elements of the for statement may be omitted and that if the logical test is omitted it defaults to TRUE. FunctionsA typical function in C looks something like:float fun1(int n, float x) { int i; float z, myarray[2*n]; /* Main body of function here */ return(z); }and can be called from another function as part of an expression as in this example: a = x + fun1(m, b); y = x + fun1(2, sin(theta) );This particular function returns a value (a float) but others do not. In C even the main routine is a function, called main.
Variables inside functions disappear when the function returnsNone of the variables defined within the function (in this case the variables i and z and the array myarray) existed before this function was called and when the function returns they will go away again and their values can never be recovered. If fun1 is called again later they will not start off with the values they had when they last left fun1.Variables defined inside functions are called automatic variables because they are automatically created and destroyed as needed. A classic bugchar *badfun(some args) { char string[12]; /* Write something to the string */ return string; } Functions receive copies of their argumentsIn the above example, when you called fun1 the values of m and b in the calling routine are copied to new temporary variables n and x inside fun1. When fun1 exits n and x will be lost along with i and myarray. This is more obvious in the second example where it's hard to even imagine how you could change the values of 2 and sin(theta).
#include <stdio.h> void addem(int i) { i = i + 2; printf("Inside addem i is now: %d\n", i); } int main() { int i; i = 317; printf("Before addem i is: %d\n", i); addem(i); printf("After addem i is: %d\n", i); }
Automatic variables are very convenient
Automatic variables have their limitations
Prototypes check function argumentsIt's surprisingly easy to get the arguments to a function wrong when calling it. For example you might miss one out, or get them in the wrong order. A little later you may add a new feature to the function which causes it to require another argument but you might forget to add this argument to one of the places where it is called.Fortunately, you can get the compiler to check that you have the right number of arguments and that they are in the right order:
Of course, this checking has one obvious limitation: if a function requires two arguments of the same type the compiler has no way of knowing if you have put them in the right order or not.
|