📄 29.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 + -