c程序设计语言 by K&R(四)输入与输出

一、标准输入、输出

1. 简单的输入\输出机制

  • 从标准输入中一次读取一个字符:int getchar(void)
  • 将字符c送到标准输出中: int putchar(int)

2. 输入重定向

  • 如果程序prog中使用了getchar函数,那么 prog < infile,将使得程序prog从输入文件infile中读取字符。
  • 如果输入通过管道来自另一个程序,那么这种输入切换也是不可见的。如命令otherprog | prog将程序的otherprog的标准输出通过管道重定向到程序prog的标准输入中。

3. 输出重定向

  • 如果程序prog中使用了putchar函数,那么 prog > file, 将使得程序prog的标准输出重定向到file文件。
  • 如果系统支持管道,那么prog | anotherprog 将把程序的标准输出通过管道重定向到程序anotherprog的标准输入中。

pipe() System call

二、格式化输出——printf函数

1. 完整形式

int printf(char* format, arg1, arg2, arg3,....);
  • '-',负号。指定被转换的参数按左对齐的形式输出。
  • 数(小数点之前)。指定最小字段宽度。字符长度不足,则填充空格。
  • 小数点。将字段宽度和精度分开。
  • 数(小数点之后)。指定字符串中要打印的最大字符数、浮点数小数点后的位数、整形最少输出的数字数目。

2. 基本的转换说明

字符 输出形式
d,i int,十进制数
o int,无符号八进制数
x int,无符号十六进制数
u int,无符号十进制数
c int类型,单个字符
s char* 类型,直到'\0'或打印了精度指定的字符数
f double类型,十进制小数,[-]m.dddddd ,d个数由精度指定
e,E double类型,[-]m.ddddd E [+-] xxx ,d个数由精度指定
g,G double类型,如果指数小于-4或大于等于精度,则使用%e、%E输出,否则用%f输出
p void* 类型;指针
int main() {
    double a = 100;
    printf("%e\n", a);
    return 0;
}

输出为:

1.000000e+002

3. 字符串输出的精度控制

#include <stdio.h>
#include <stdlib.h>

int main(void) {
	char* s = "hello, world"; //12 char
	printf("-%s-\n",s); //用-指示宽度边界
	printf("-%10s-\n",s); 
	printf("-%.10s-\n",s);
	printf("-%-10s-\n",s); // - 左对齐
	printf("-%15s-\n",s);
	printf("-%-15s-\n",s);
	printf("-%15.10s-\n",s);
	printf("-%-15.10s-\n",s);

	int max = 5;
	printf("-%.*s-\n", max, s); //宽度或精度可通过星号*表示,其值可通过转换下一参数(必须为int类型)来计算。

	return 0;
}

输出

-hello, world-
-hello, world-
-hello, wor-
-hello, world-
-   hello, world-
-hello, world   -
-     hello, wor-
-hello, wor     -
-hello-

4. sprintf函数

int sprintf(char* string, char* format, arg1, arg2, ...);

按照format格式格式化序列参数arg1,arg2,...,将输出结果存放到string中。string的空间需要大到足以容纳结果。

三、 变长参数表

1. 几个va_宏定义

  • va_list : va_list ap; 用于声明一个变量ap(arg pointer),依次引用各参数。
  • va_start: va_start(ap, namedArg);将ap初始化为第一个无名参数的指针。使用ap之前,该宏必须被调用依次。参数表必须至少包含一个又名参数,va_start将最后一个有名参数作为起点。
  • va_arg:va_arg(ap, int)该函数将返回一个参数,并将ap指向下一个参数。va_arg使用一个类型名来决定返回的对象类型、指针移动的步长。
  • va_end:va_end(ap); 最后在函数返回之前,需调用va_end来完成一些必要的清理工作。

2. 实现一个miniprintf

void minprintf(char* fmt, ...){
    va_list ap;
    char* p, *sval;
    int ival;
    double dval;
    va_start(ap, fmt);

    for(p = fmt; *p; p++){
        if(*p != '%'){
            putchar(*p);
            continue;
        }
        switch(*++p){
            case 'd':
                ival = va_arg(ap, int);
                printf("%d", ival);
                break;
            case 'f':
                dval = va_arg(ap, double );
                printf("%f", dval);
                break;
            case 's':
                for(sval = va_arg(ap, char*); *sval; sval++){
                    putchar(*sval);
                }
                break;
            default:
                putchar(*p);
                break;
        }
    }
    va_end(ap);
}

3. 实现一个可变参数累加函数

long long int sum(int num, ...) 其中num为累加整数的数量。

#include <stdio.h>
#include <stdarg.h>

long long int sum(int num, ...){
    long long int result = 0;
    va_list ap;
    va_start(ap, num);
    while(num--){
        result += va_arg(ap, int);
    }
    va_end(ap);
    return result;
}
int main() {
    printf("%lld", sum(3,2,3,4));
    return 0;
}

输出为:

9

四、格式化输入——scanf函数

scanfsscanf 的所有参数都必须是指针。

1. scanf: 从标准输入中读取字符序列。

int scanf(char* format, ...)

  • 其返回值可以用来确定已匹配的输入项的个数。

  • 如果达到文件的末尾,返回EOF。

  • 如果返回0, 表示下一个输入字符与格式串中的第一个格式说明不匹配。

2. sscanf: 从一个字符串而不是标准输入中读取字符序列。

int sscanf(char* string, char* format, arg1, arg2...)

按照格式参数format中规定的格式扫描字符串string,并把结果保存到arg1, arg2, ...

五、文件访问

1. 文件指针:

  • 在读写一个文件之前,必须通过库函数fopen打开该文件。fopen利用文件名建立与操作系统必要的连接和通信,返回一个可用于文件读写操作的指针FILE*。它指向一个包含文件信息的结构,这些信息包括:缓冲区的位置、缓冲区当前字符的位置、文件的读写状态等、是否出错或是否已经达到文件末尾等。

FILE* fopen(char* name, char* mode);

  • name是文件名,mode是访问模式,包括读(r)写(w)追加(a)。某些系统用b来区分文本文件和二进制文件。

2. 对文件的读写方法

int getc(FILE* fp) : 从文件指针fp指向的文件中中获取一个字符。

int putc(int c, FILE* fp) : 向指针写入一个字符c

3. 启动c程序时,操作系统打开的文件

  • 启动一个c语言程序时,操作系统负责打开三个文件,并将这三个文件的指针提供给该c程序。三个文件分别是标准输入、标准输出和标准错误,对应的指针为stdinstdoutstderr,包含在<stdio.h>。

  • 通常,stdin指向键盘、stdoutstderr指向显示器。

  • 由前述可知,stdinstdout可以重定向到管道。

4. 文件的格式化输入、输出

可以使用fscanf()fprintf(),比scanf()printf()相比,多了一个文件指针参数FILE* fp作为第一个参数。

  • 格式化输入

int fscanf(FILE* fp, char* format, ...);

  • 格式化输出

int fprintf(FILE* fp, char* format, ...);

  • 实现简单的cat程序: 将多个文件的内容输出。

cat.c

#include <stdio.h>
void filecopy(FILE* ifp, FILE* ofp){
    int c;
    while((c = getc(ifp)) != EOF){
        putc(c, ofp);
    }
}

int main(int argc, char* argv[]) {
    FILE* fp;
    if(argc == 1){
        filecopy(stdin, stdout);
    }else{
        while(--argc > 0){
            if((fp = fopen(*++argv, "r")) == NULL){
                printf("cat: can't open %s\n", *argv);
                return 1;
            }else{
                filecopy(fp, stdout);
                fclose(fp);
            }
        }
    }
    return 0;
}

1.txt

hello 

2.txt

world 

编译运行:
gcc cat.c -o cat
./cat 1.txt 2.txt

输出:

hello
world

六、错误处理——stderr和exit

1. 刚刚实现的cat程序错误处理并不完善。

如果因为某种原因造成某个文件无法访问,相应的诊断信息要在输出末尾打印出来,输出到屏幕可以接受,但输出到另一个文件或重定向到另一个程序时,就无法接受了。为了更好地处理这种情况,另一个输出流stderr以与stdinstdout相同的方式分派给程序。即使对标准输出进行了重定向,写到stderr的输出通常也会显示在屏幕上。

对cat程序进行修改,将错误信息写到标准错误文件中。

#include <stdio.h>
#include <stdlib.h>
void filecopy(FILE* ifp, FILE* ofp){
    int c;
    while((c = getc(ifp)) != EOF){
        putc(c, ofp);
    }
}


int main(int argc, char* argv[]) {
    FILE* fp;
    char* prog = argv[0];
    if(argc == 1){
        filecopy(stdin, stdout);
    }else{
        while(--argc > 0){
            if((fp = fopen(*++argv, "r")) == NULL){
                fprintf(stderr, "%s:  can't open %s\n", prog, *argv);
                exit(1);
            }else{
                filecopy(fp, stdout);
                fclose(fp);
            }
        }
    }
    if(ferror(stdout)){
        fprintf(stderr, "%s: error writing stdout\n", prog);
        exit(2);
    }
    exit(0);
}

2. fprintf()函数将产生的诊断信息输出到stderr中

3. 使用exit()函数终止调用程序的执行。

在主程序main中,语句return expr相当于exit(expr)。但是exit函数的优点是可以在其他函数中调用。

4. ferror(FILE* fp)函数

int ferror(FILE* fp)

如果流fp中出现错误,该函数将返回一个非0值。

5. feof(FILE* fp)函数

如果指定的文件达到文件末尾,它将返回一个非0值。

七、行输入和行输出

1. fgets

char *fgets(char* line, int maxline, FILE* fp) 从fp指向的文件中读取下一个输入行,将它存在字符数组line中(以'\0'结尾),最多可读取maxline-1个字符。遇到文件结尾或发生错误,会返回NULL。

2. fputs

int fputs(char* line, FILE* fp) 将一个字符串line写入到fp指向的文件。发生错误时,返回EOF,否则返回一个非负值。

八、其它函数

1. 字符串

函数
strcat(s,t)
strcat(s, t, n)
srcmp(s, t)
strcmp(s, t, n)
strcpy(s, t)
strcpt(s, t, n)
strlen(s)
strchr(s, c) 在字符串s中查找c,若找到返回第一次出现的位置的指针,否则返回null
strrchr(s, c) 在字符串s中查找c,若找到返回最后一次出现的位置的指针,否则返回null。(反向查找)

2. 字符串测试和转换函数

函数
isalpha(c)
isupper(c)
islower(c)
isdigit(c)
isalnum(c)
isspace(c)
toupper(c)
tolower(c)

3.ungetc函数

int ungetc(int c, FILE* fp) 将字符c写回到文件fp中。如果执行成功,返回c,否则返回EOF。

4.命令执行函数

system(char* s)执行包含在字符串s中的命令,然后继续执行当前程序。比如system("date")将执行程序date, 在标准输出中打印当前的日期和时间。又比如system("pause")

5.存储管理函数

函数malloc和calloc用于动态分配内存。

  • malloc函数

void* malloc(size_t n), 分配成功时,返回一个指向n字节长度的未初始化的存储空间,否则返回NULL。

  • calloc函数

void* calloc(size_t n, size_t size),分配成功时,该指针指向足以容纳由n个指定长度为size的对象组成的数组,否则返回NULL。

  • 例子:

以下例子进行了类型转换。

int* ip;
ip = (int*)calloc(n, sizeof(int));

  • free(p)函数释放p指向的空间,p必须是此前通过调用malloc和calloc函数得到的指针。

6. 数学函数

sin(x)cos(x)atan2(x)exp(x)log(x)log10(x)pow(x, y)sqrt(x)fabs(x)

7.随机数发生器函数

rand()生成介于0和RAND_MAX之间伪随机整数序列。

原文地址:https://www.cnblogs.com/qiangz/p/15790651.html