⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 07.txt

📁 一個計算機系教授的上課講義 主要是教導學生使用C語言編寫程序
💻 TXT
字号:
CS 1355
Introduction to Programming in C
Monday 2006.10.2 (Week 4)
Lecture notes (at http://r638-2.cs.nthu.edu.tw/C/notes/07.txt)

Chapter 4: C Program Control

- case sensitivity
- for loop
- switch statement

Case sensitivity
- in C, upper case and lower case letters are different.
  if, If, IF, and iF are all different!
  => if is a keyword, but If, IF, iF are identifiers
  => different upper/lower case letters are considered different.
- other programming language may be case-insensitive

For-loop:
- another way to write loops
  => while loop: condition test
  => for loop: 3 parts
    - initialization
    - condition test (just like while loop)
    - loop control update

For example: repeat 10 times

for (int i = 0; i < 10; i++) {
	printf("%d\n", i);
}

This is equivalent to

int i = 0; /* initialization */
while (i < 10) /* condition test*/ {
	printf("%d\n", i);
	i++;  /* update */
}

General structure:

for (/* initialization */ ; /* condition test */; /* update */) {
	statement
}

So, why write it as a for loop?
- because it is easy to forget initialization or update
- particularly useful for "counter-controlled loops"

Important point:
- the initialization, condition test, and update are all expressions!!!
  Specifically, 
  i = 0   is called an "assignment expression"
  i < 10  is a comparison expression
  i++     is a post-increment expression
- An expression has a value

Assignment operator as an expression
- assignment ( = ) is a binary operator
- an asignment expression has the value being assigned!!
  - it is a "right associative" operator,
  - the precedence of assignment expression is lower than 
    all other arithmetic operators
- so, 
	i = 0
  first evaluates the RHS as 0,
  assigns 0 to i,
  then the (i = 0) expression has the value 0.
- This means, you can do multiple assignments
	int x, y, z;
	x = y = z = i = 0;
  => This is evaluated in the order
	x = (y = (z = (i = 0)))
     that is, i = 0, then z = 0, then y = 0, then x = 0
     hence right associative.

Comma operator as an expression
- a comma is a binary operator,
  left associative,
  lower precedence than the assignment operator
- if you have the comma expression
	x , y
  (where x and y are expressions)
  then it evalutes x, and then evaluate y, and
  the expression has the value of y
Example
   int  i = (1, 2, 3, 4, 5);
   printf("%d\n", i);
 => this will print 5, because it is the last expression in the
    comma-separated list of expressions

Combining comma and assignment expressions:

	int a, b, c;
	a = 3, b = 2, c = 4;

What does it do???
- Answer: comma (,) has lower precedence than assignment (=)
  So, it evaluates
	(a = 3), (b = 2), (c = 4)
  Instead of
	a = (3, (b = (2, (c = 4))))
	a = b = c = 4

Why use comma-separated assignments?
Answer: multiple assignments in FOR loops

Example: Fibonacci number
f(0) = 1
f(1) = 1
f(2) = f(0) + f(1) = 2
f(3) = f(2) + f(1) = 3
f(4) = f(3) + f(2) = 5
f(5) = f(4) + f(3) = 8
f(6) = f(5) + f(4) = 13
f(7) = f(6) + f(5) = 21

Solution: 
- use 
  - one variable f_1 for f(n-1), initialized to 0
  - one variable f_2 for f(n-2), initialized to 0
  - one variable f_n for f(n)
  - each loop, 
    set f_2 = f_1,
    set f_1 = f_n
    set f_n = f_2 + f_1,

int main() {
	int i, n;
	scanf("%d", &n);
	int f_1 = 0, f_2 = 0, f_n = 1; 
	for (i = 0; i < n; i++) {
		f_2 = f_1;
		f_1 = f_n;
		f_n = f_1 + f_2;
	}
	printf("f(%d) = %d\n", n, f_n);
}

You could also write it as

	int i, n, f_2, f_1, f_n;
	scanf("%d", &n);
	for (i = 0, f_1 = 0, f_2 = 0, f_n = 1;
			/* initialize multiple variables */
		 i < n; /* test the loop condition */
		 i++, f_2 = f_1, f_1 = f_n, f_n = f_1 + f_2
		 	/* update multiple variables */) {
		 /* nothing more to do inside the loop in this case */
	}
	printf("f(%d) = %d\n", n, f_n);

This works the same, although somewhat contrived.

Other uses of FOR loops
- step down from 10 to 1
	for (i = 10; i >= 1; i--) {
		printf("%d\n", i);
	}
- go from 10, 15, 20, 25, 30, 35, ... 100
	for (i = 10; i <= 100; i += 5) {
		printf("%d\n", i);
	}
- step from 1, 2, 4, 7, 11, 16, 22, 29, 37, 46, 56, 67, 79, ...
	scanf("%d", &n);
	for (i = 1, f_n = 1; i < n; f_n += i++) {
	}

Example from book: computing compound interest

#include <stdio.h>
#include <math.h>
int main() {
	double amount;
	double principal = 1000.0;
	double rate = 0.05;
	int year;
	printf("%4s%21s\n", "Year", "Amount on Deposit");
	for (year = 1; year <= 10; year++) {
		amount = principal * pow(1.0 + rate, year);
		printf("%4d%21.2f\n", year, amount);
	}
}

How this works
- double: a double-precision floating point (64 bits, rather than 32 bits)
- <math.h>: another standard library, with all math routines
  (check /usr/include/math.h)
- %s   -- format for string
  %4s     format for string, right align (pad space on the left)
  %-4s    format for string, left align (pad space on the right)

- %f      -- floating point
  %21.2f  -- 21 positions total, 2 digits after decimal

Question: 
- last time, %f was for float type;
- but this time, we use double type instead of float type.
  Is this ok??
Answer:
- yes. In fact, 
  C compiler converts all float to double type when 
  passing it as a parameter!!
- so, both float and double are treated as double.


=============
SWITCH statement
- it's somewhat like if/else-if/else-if/.../else
- restriction:
  - compare one expression to see if it is equal to a constant

int main() {
	char letter;
	scanf("%c", &letter);
	switch (letter) {
	case 'A': 
		printf("score is 90..100\n");
		break;
	case 'B':
		printf("score is 80..89\n");
		break;
	case 'C':
		printf("score is 70..79\n");
		break;
	case 'D':
		printf("score is 60..69\n");
		break;
	case 'F':
		printf("score is 0..59\n");
		break;
	default:
		printf("%c is not a valid letter grade\n", letter);
	}
}

How it works:
- this is similar to
	if (letter == 'A') {
		printf("score is 90...100\n");
	} else if (letter == 'B') {
		printf("score is 80...89\n");
	} else if (letter == 'C') {
		...
	} else {
		printf("%c is not a valid letter grade\n", letter);
	}
  However, there are some differences.
1) you should have a "break" statement at the end of each case.
	- break => exits the switch statement.
	- actually, break can also exit a for-loop or a while-loop, too.
	  the meaning of break depends on the context (the innermost
	  statement that encloses it)
2) Restrictions on the case value
	case 'A':
	case 'B':
	...
  - the cases must be constant scalar values!! (int, char)
  - CANNOT be string!!
  - CANNOT be floats!!
  - CANNOT be a variable or non-constant expression!

Example:
	case x + 22:
 - if x is a variable, then this is bad!!
 - but if x is another integer literal, then it is ok

3) default:
   - it will match all other cases,
     similar to an "else" statement.

Fundamental difference between an if-statement and a switch-statement:
- switch is like a goto statement!!
- meaning: it jumps to the matching case as a label
  - several cases can label the same location
  - it continues stepping sequentially, until it reaches a break!
So, what if we took out the break statements?
	switch (letter) {
	case 'A': 	printf("score is 90..100\n");
	case 'B':	printf("score is 80..89\n");
	case 'C':	printf("score is 70..79\n");
	case 'D':	printf("score is 60..69\n");
	case 'F':	printf("score is 0..59\n");
	default:	printf("%c is not a valid letter grade\n", letter);
	}

This means
	if (letter == 'A') {
		printf("score is 90..100\n");
		printf("score is 80..89\n");
		printf("score is 70..79\n");
		printf("score is 60..69\n");
		printf("score is 0..59\n");
		printf("%c is not a valid letter grade\n", letter);
	}
	...
  (because cases are only labels, not "else if" statements!)
  this is probably not what you want!!
  So, remember to put break;

But, why do it this way?
- Reason: it can be useful for multiple matches.
- For example
	switch (letter) {
	case 'A':
	case 'a':
			printf("score is 90..100\n");
			break;
	case 'B':
	case 'b':
			printf("score is 80..89\n");
			break;
	...
	}

- You can have several labels that mark the same spot.
  This is saying
	if letter is either 'A' or 'a', then do the same thing
- in terms of IF statements,
	if (letter == 'A' || letter == 'a') {
		printf("score is 90..100\n");
	} else if ...


Example from the book

1	#include <stdio.h>
2	int main() {
3		int grade, aCount = 0, bCount = 0, cCount = 0, 
4			dCount = 0, fCount = 0;
5		while ( (grade = getchar()) != EOF) {
6			switch(grade) {
7			case 'A': case 'a': ++aCount; break;
8			case 'B': case 'b': ++bCount; break;
9			case 'C': case 'c': ++cCount; break;
10			case 'D': case 'd': ++dCount; break;
11			case 'F': case 'f': ++fCount; break;
12			case '\n': case '\t': case ' ': /* ignore blanks */
13				break;
14			default: /* catch all other characters */
15				printf("invalid grade %c\n", grade);
16				break; /* not necessary */
17			}
18		}
19		printf("A: %d\nB: %d\nC: %d\nD: %d\nF: %d\n",
20			aCount, bCount, cCount, dCount, fCount);
21	}

How to run this:
% ./a.out
A BACDFD ADa bacaFA
ABDDFFA ffaab
^D
A: 11
B: 4
C: 2
D: 5
F: 6
%
- enter letters, 
  A a B b C c D d F f   => these are valid letters
  ok to include blank, tab, return
- when finished, in the unix shell environment, type
  [Return] Control-D
- Note: Control-D means hold down the [Control] key, then type [D]
  Also written as ^D
- In DOS environment, it's ^Z
  => but in unix shell, ^Z means "stop-job"
     suspends a program ("pause")
     later can resume by typing "fg" or "%" in the shell prompt

- You can also redirect input from a file
% ./a.out < file
=> No need to type ^D in this case!

End-of-file is like a univeral sentinel

So.. how does this work:
1) grade is decalred as an int, not a char!!
2) while ((grade = getchar()) != EOF) 
  two parts:
  - grade = getchar() 
    => getchar() is a routine defined in <stdio.h>
       it reads the next character from standard input
    The return value of getchar() is the ASCII code for the character.
    (see lecture 3 about character literals!)
    But if we reach end-of-file, then it returns -1
    => not a valid ASCII code!
    => This is why it is important for grade to be an int, not a char,
       because int is 4 bytes (can represent EOF), 
       but char is 1 byte (can represent only ASCII code, not EOF)
    EOF = End-of-File
    The name EOF is defined in <stdio.h> (see the file /usr/include/stdio.h)
#define EOF (-1)
   - (grade = getchar()) itself is an assignment expression!!
     it has the value of grade after assignment.
     It can be used for comparison purpose
   - So, we can compare
	((grade = getchar()) != EOF)
   This is like

4.5	grade = getchar();
5	while (grade != EOF) {
		...
17.5		grade = getchar();
18	}

   Writing it with assignment expression is shorter, no need for 
   duplicate statement

Actually, scanf() returns an integer, too!
- it returns the number of items successfully scanned 
  according to the %d %f %c etc format,
- it also returns EOF when reaching end-of-file

Example:
- prompts user for two ints,
- adds them, until end-of-file
#include <stdio.h>
int main() {
        int x, y, z;
        int n;
        while (( printf("x y? "), (n = scanf("%d%d", &x, &y))) != EOF) {
                z = x + y;
                printf("%d + %d = %d\n", x, y, z);
        }
}

Note: 
- printf is also an expression!!
  it is the LHS of a comma operator;
  (n = scanf()...) is the RHS of a comma operator
  => comma operator evaluates both LHS and RHS, and returns RHS as its value!
- so, the while statement tests (LHS, RHS) != EOF
  or effectively RHS != EOF

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -