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

📄 29.txt

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

File redirection: 
- this is from the command line shell, not part of C
Redirect standard output to a file
% ./a.out > outfile   (overwrites outfile if any)
% ./a.out >> outfile  (appends to outfile)

Redirect standard input from a file:
% ./a.out < infile     (read standard input from the file)

% ./a.out << sentinel   This is NOT I/O redirection!
This is used for scripting, where you provide the input 
as part of the command.
sentinel is the end marker.
sentinel

you tell the shell interpreter what sentinel you want,
and it treats subsequent lines as standard-input
until but not including sentinel.
% sort -n << abc
? 2346
? 1101
? 1
? 700
? 950
? abc
1
700
950
1101
2346
%
You can think of sentinel as "defining your own quotation mark"
in a multi-line quote.


Variable length argument list
- functions have prototypes, declares return type and type of each parameter
- what about printf()?  what does its prototype look like?
  printf() is a function with a variable-length argument list
  - you can call 
	printf("hello world");
	printf("My name is %s", x); 
  - The argument list is as long as the user wants, as long as you have
    the corresponding %s %d etc
  - The function prototype for printf is
	int printf(const char *format, ...);
    => yes, ... (named "elipsis") 
       is part of ANSI C syntax to indicate variable number of
       arguments after the formatting string
    => there may be several required parameters, 
       and then variable-length list comes afterwards.
       example,
	int srintf(char* buffer, const char* format, ...);
	int fprintf(FILE*, const char* format, ...);

- How they do it?
  - write it in assembly code: not limited by C; or,
  - use stdarg.h
- you might want to do your own variable-length argument list
  e.g, average(), max(), and(), or(), ...
  pretty much anything yo can pass an array to.
  => these are called "reduction operator" functions
	
Rules on using variable-length argument list (varying number of arguments)
- Declare your function prototype 
- You have to figure out how many arguments get passed!
  - user might need to tell you at the time they call, using an explicit
    count
  - you might have to use a formatting string like printf()
    => as long as you have % arguments, printf() will try to format it!
    => printf() cannot check how many arguments you have actually passed
    => in fact, typechecking is not possible!
- stdarg.h provides several macros for iterating through the
  arguments in the variable-length argument list.

	va_list  is the type of "iterator"
		 (which "generates" one item at a time)
	va_start is an "initializer function" for the iterator
	va_arg   returns the next item in the list and "auto-increments"
	va_end   is the "clean up" function

Example use: average()
Prototype
	double average(int count, double n, ...);
- The parameter "count" means the user must tell the function
  how many parameters are being passed.
  If wrong, it's the user's own responsibility!
- double n   -- means we expect at least one parameter of type double
  after the count. Otherwise, somewhat meaningless
- what comes after double n is anybody's guess.
  The compiler cannot check for the data type or number of arguments.
Example call
	double a[] = { 10, 20, 30, 40, 50 };
	double A = average(5, a[0], a[1], a[2], a[3], a[4]);
Note that 
	5 tells average() how many doubles there are.
	at least one double is required

Another example: total:

#include <stdio.h>
#include <stdarg.h>
double total(int count, double d, ...) {
        double total = 0;
        int    j;
        va_list ap; /* this declares the iterator (like a queue?) */

        for (va_start(ap, d), j = 0; j < count; j++) {
        	/* va_start() is like initialize the queue with the list */
                total += va_arg(ap, double);
                /* va_arg() is like "dequeue", but we must tell it 
                   what data type we want, then it will increment
                   the pointer by sizeof() item
                 */
        }
        return total;
}
int main() {
        double a[] = { 5.0, 10.0, 15.0, 20.0  };
        printf("Total of %lf, %lf, %lf, %lf = %lf, expecting %lf\n",
                a[0], a[1], a[2], a[3], total(4, a[0], a[1], a[2], a[3]),
                a[0]+a[1]+a[2]+a[3]);
}

--------
command line arguments
- from parameters to main()
int main(int argc, char *argv[]) {
}

Note: the shell performs processing/parsing first.
So, what you type might be different from what you see.
- I/O redirection
- file name matching
- shell variable expansion

% grep -i pattern *.c | sort | uniq | head  

four programs will be run!
- grep will get "grep", "-i", "pattern", and all of the
  file names ending in .c as its argv[ ].
  *.c is file name matching.
- sort, uniq, head each gets only its program name as the only
  member of argv[ ]

File matching by shells (csh, bash, ...)
*      zero or more characters that match the file
	example:  mv *.h include/  => moves all .h files to include/ directory

[a-z]  the character set consisting of a, b, .. z
       => the file actually has to exist!
       example:  mv hello.[ch]  world/  => move hello.c, hello.h to world/,
       		although it is ok if either one is missing.

{abc,def,ghi}  the set of strings that match "abc", "def", "ghi"
	=> this is an enumeration, not a file pattern match!
{a-z} is not "a", "b", "c", .. "z", but it is just the string "a-z"

What about blanks?
- use quotes
- use \   (backslash-space)
  (usually the tab will perform file completion for you interactively)

Quick way to tell what the actual argv values are
- use the "echo" command
% echo hi
hi
% echo {abc,def}.{123,456}
abc.123 abc.456 def.123 def.456
%

Separate compilation
- stage one: compile the files separately 
  cc -c file1.c
  => this creates file1.o, where .o is an "object file"
  => if you want debugging information, you can add -g flag
- stage two: linking
  cc *.o -o myExecutable
  the -o means "output file should be named as..."

Calling a function defined in another file:
- provide its prototype, e.g., 
	void usage(char *programName);
 => normally, we collect the prototype names into header (.h) files
- similarly, typedef is commonly given in (.h) files
What if you want to use a global variable defined in another file?
- declare
	extern int  globalVariableName;
  This is similar to the function prototype
At link time, the linker will check to make sure all names are defined
exactly once.
- function names and global variable names are resolved globally
   within one executable.
- you are not allowed to define a global name twice!

"file-static" names
- add to the front of functions or variables
example: file "morse.c"

static char symbol[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ ";
static char *code[] = {
        "-----",    /* 0 */      ".----",    /* 1 */    "..---",    /* 2 */
        "...--",    /* 3 */      "....-",    /* 4 */    ".....",    /* 5 */
        "-....",    /* 6 */      "--...",    /* 7 */    "---..",    /* 8 */
        "----.",    /* 9 */ ...
};

Even though originally we declared these as globals,
it is a good idea to declare them as "static global"
because only the file "morse.c" should need to know about these tables.
Otherwise anyone can just say 
	extern char symbol[ ]
and actually get to use the tables!
Conversely, anyone who wants to declare their own global named symbol
would get a duplicate identifier error at linking time.

Similarly, some functions might be better declared as static
to remove their exposure to outside files, so that nobody else
except those inside morse.c can call them.

Program exit:
can call 
	#include <stdlib.h>
	exit(0); /* normal */
	exit(1); /* exit with status code #1, your own meaning */
	atexit(callback); /* where callback is a function pointer */
	=> gets called when you exit
	purpose: close file, flush data to disk, save state, etc

Volatile type:
- qualifier on variables
- e.g., 
	volatile int   x;
  purpose? 
  It is "more than a variable":
  - could be an I/O device
  - could be a shared variable, modifiable by someone else
  - so, every read/write should be performed, 
    don't "optimize away" apparently useless operations.
Example
	x = 10;
	x = 5;
	x = 2;
   => might look like the first two assignments are useless,
      but for I/O, this is significant (actually changes values on hardware)

Floating point constants:
	add u for unsigned,		e.g., 12u
	add l or L for long		e.g., 345L
	might be easier to read to do type coersion explicitly
	(unsigned) 12

Signal handling
- what is a signal?
  means many things!
hardware: electrical signal (voltage), radio signal, ...
software: "event that requires special handling"
=> something that happens when the program is running

example: (see /usr/include/signal.h)

     No    Name         Default Action       Description
     1     SIGHUP       terminate process    terminal line hangup
     2     SIGINT       terminate process    interrupt program
     3     SIGQUIT      create core image    quit program
     4     SIGILL       create core image    illegal instruction
     5     SIGTRAP      create core image    trace trap
     6     SIGABRT      create core image    abort program (formerly SIGIOT)
     7     SIGEMT       create core image    emulate instruction executed
     8     SIGFPE       create core image    floating-point exception
     9     SIGKILL      terminate process    kill program
     10    SIGBUS       create core image    bus error
     11    SIGSEGV      create core image    segmentation violation
     12    SIGSYS       create core image    non-existent system call invoked
     13    SIGPIPE      terminate process    write on a pipe with no reader
     14    SIGALRM      terminate process    real-time timer expired
     15    SIGTERM      terminate process    software termination signal
     16    SIGURG       discard signal       urgent condition present on
                                             socket
     17    SIGSTOP      stop process         stop (cannot be caught or
                                             ignored)
     18    SIGTSTP      stop process         stop signal generated from
                                             keyboard
     19    SIGCONT      discard signal       continue after stop
     20    SIGCHLD      discard signal       child status has changed
     21    SIGTTIN      stop process         background read attempted from
                                             control terminal
     22    SIGTTOU      stop process         background write attempted to
                                             control terminal
     23    SIGIO        discard signal       I/O is possible on a descriptor
                                             (see fcntl(2))
     24    SIGXCPU      terminate process    cpu time limit exceeded (see
                                             setrlimit(2))
     25    SIGXFSZ      terminate process    file size limit exceeded (see
                                             setrlimit(2))
     26    SIGVTALRM    terminate process    virtual time alarm (see
                                             setitimer(2))
     27    SIGPROF      terminate process    profiling timer alarm (see
                                             setitimer(2))
     28    SIGWINCH     discard signal       Window size change
     29    SIGINFO      discard signal       status request from keyboard
     30    SIGUSR1      terminate process    User defined signal 1
     31    SIGUSR2      terminate process    User defined signal 2
     32    SIGTHR       terminate process    thread interrupt
To use signal:
- register the types of signals you want to handle
e.g, 
	signal(SIGINT, handler);
=> this tells the operating system you want to handle 
  SIGINT (interrupt) type of signal,
  (you can use bitwise-OR to specify multiple types!)
=> when handling, it calls your "handler" function (function pointer)

void handler(int signalValue) {
	handling action for the signal...
}

Once you handle the signal, then your program can continue normal execution.
Otherwise, the system might kill it!

(advanced topic, for system programming course later)

⌨️ 快捷键说明

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