前言
最近在看《Operating Systems - Three Easy Pieces》
发现自己C编程能力是真的弱啊,甚至读不懂。
一 代码段内容:
cpu.c
文件
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "usage: cpu <string>\n");
exit(1);
}
char *str = argv[1];
while (1) {
printf("%s\n", str);
Spin(1);
}
return 0;
}
一开始报错 #include "common.h"
行。
- 系统自带的头文件用尖括号括起来,这样编译器会在系统文件目录下查找。
#include <xxx.h> - 用户自定义的文件用双引号括起来,编译器首先会在用户目录下查找,然后在到C++安装目录(比如VC中可以指定和修改库文件查找路径,Unix和Linux中可以通过环境变量来设定)中查找,最后在系统文件中查找。
在本书的github
厂库中找到了官方给出的代码,里面有common.h
文件。
#ifndef __common_h__
#define __common_h__
#include <sys/time.h>
#include <sys/stat.h>
#include <assert.h>
double GetTime() {
struct timeval t;
int rc = gettimeofday(&t, NULL);
assert(rc == 0);
return (double) t.tv_sec + (double) t.tv_usec/1e6;
}
void Spin(int howlong) {
double t = GetTime();
while ((GetTime() - t) < (double) howlong)
; // do nothing in loop
}
#endif // __common_h__
Makefile
中命令如下:
all: cpu mem threads io
clean:
rm -f cpu mem threads io
cpu: cpu.c common.h
gcc -o cpu cpu.c -Wall
mem: mem.c common.h
gcc -o mem mem.c -Wall
threads: threads.c common.h common_threads.h
gcc -o threads threads.c -Wall -pthread
io: io.c common.h
gcc -o io io.c -Wall
二 代码段理解
2.1 cpu.c
文件
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
stdio.h
是标准io库
stdlib.h
是标准库文件,包含了常用的系统函数。比如内存申请malloc()
用引号标识的common.h
是用户自定义的头文件
common.h
的文件内容定义如下:
#ifndef __common_h__
#define __common_h__
#include <sys/time.h>
#include <sys/stat.h>
#include <assert.h>
double GetTime() {
struct timeval t;
int rc = gettimeofday(&t, NULL);
assert(rc == 0);
return (double) t.tv_sec + (double) t.tv_usec/1e6;
}
void Spin(int howlong) {
double t = GetTime();
while ((GetTime() - t) < (double) howlong)
; // do nothing in loop
}
#endif // __common_h__
可以看到在这个文件中,定义了Spin
函数。就是不断的循环,直到发现距离调用该函数的过去了1秒。
具体的在下一部分对common.h
的分析中解释
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "usage: cpu <string>\n");
exit(1);
}
char *str = argv[1];
while (1) {
printf("%s\n", str);
Spin(1);
}
return 0;
}
这一部分中关注了从来没注意过的main()
函数参数问题。参考资料
通常我们在写主函数时都是void main()或int main() {…return 0;},但ANSI-C(美国国家标准协会,C的第一个标准ANSI发布)在C89/C99中main()函数主要形式为:
- int main(void)
- int main(int argc,char *argv[]) = int main(int argc,char **argv).
其参数argc和argv用于运行时,把命令行参数传入主程序.其中ARG是指arguments,即参数.具体含义如下:
(参照Arguments to main和C++ Primer7.2.6节)
- int argc:英文名为arguments count(参数计数)
count of cmd line args,运行程序传送给main函数的命令行参数总个数,包括可执行程序名,其中当argc=1时表示只有一个程序名称,此时存储在argv[0]中. - char **argv:英文名为arguments value/vector(参数值)
pointer to table of cmd line args,字符串数组,用来存放指向字符串参数的指针数组,每个元素指向一个参数,空格分隔参数,其长度为argc.数组下标从0开始,argv[argc]=NULL.
argv[0] 指向程序运行时的全路径名
argv[1] 指向程序在DOS命令中执行程序名后的第一个字符串
argv[2] 指向执行程序名后的第二个字符串
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "usage: cpu <string>\n");
exit(1);
}
为什么判断条件是 argc!=2
呢?
因为main
函数有一个默认参数:程序运行时的全路径名
然后是运行时后面跟的字符串
如果跟了两个参数,那么argc
实际上是3
wby@ubuntu:~/Documents/ostep-code-master/intro$ ./cpu a b
usage: cpu <string>
下半段代码中,我们发现使用了printf
函数,但是上一段中使用的是fprintf
函数,它们的区别在哪里呢?
char *str = argv[1];
while (1) {
printf("%s\n", str);
Spin(1);
}
return 0;
Difference between fprintf, printf and sprintf? ——Stack Overflow
fprintf writes formatted text to the output stream you specify.
fprintf将文本输出到用户指定的输出流中
printf is equivalent to writing fprintf(stdout, …) and writes formatted text to wherever the standard output stream is currently pointing.
(相当于使用fprintf将文本输出到标准输出流中)
sprintf writes formatted text to an array of char, as opposed to a stream.
将文本输出到指定字符串中,可以用来将大量数字数据转为字符串
stdin
is a pointer to the standard input stream, stdout
is a pointer to the standard output stream, and stderr
is a pointer to the standard error output stream. In an interactive session, the three usually refer to your console, although you can redirect them to point to other files or devices:
一般而言,标准输入流,标准输出流,标准错误输出流都会指定到你的命令台中。但你也可以重新指定他们指向不同的文件或设备
$ myprog < inputfile.dat > output.txt 2> errors.txt
In this example, stdin now points to inputfile.dat, stdout points to output.txt, and stderr points to errors.txt.
通过上述的代码,标准输入流现在指向inputfile.dat文件…
实际使用如下:
./cpu a 2>errors.txt
在errors.txt
中内容为:
usage: cpu <string>
但是在我用./cpu a b >output.txt
的时候有问题,控制台中不显示输出,output.txt
中也没有任何内容。为什么呢?是因为输出流不断刷新,所以给弄没了吗?
2.2 common.h文件
#ifndef __common_h__
#define __common_h__
#include <sys/time.h>
#include <sys/stat.h>
#include <assert.h>
double GetTime() {
struct timeval t;
int rc = gettimeofday(&t, NULL);
assert(rc == 0);
return (double) t.tv_sec + (double) t.tv_usec/1e6;
}
void Spin(int howlong) {
double t = GetTime();
while ((GetTime() - t) < (double) howlong)
; // do nothing in loop
}
#endif // __common_h__
这些条件编译用的宏,指在如果没有__common_h__
这个标识符的时候,才进行编译。
参考:Why are #ifndef and #define used in C++ header files?
#ifndef __common_h__
#define __common_h__
....
#endif // __common_h__