先看看三个概念:
标准输入在程序运行时由用户输入数据,程序运行产生的相关信息都由标准输出和标准错误显示。
这里有必要解释一下stdin stdout和stderr:
当我们启动一个C语言程序时,操作系统负责打开3个文件,并将这3个文件的指针提供给该程序。这3个文件分别为标准输入、标准输出和标准错误,相应的文件指针分别为stdin、stdout、stderr。
我们也可以在程序中指定输入文件获得数据,但是如果我们不确定输入文件是什么,该怎么编程呢?实际上,我们可以先假设程序使用标准输入,在实际运行时在运用输入的重定向,把键盘输入替换为文件输入。同理,也可以进行输出的重定向。
或者当程序的输出的内容太多时,我们也可以把标准输出重定向。
看下重定向的命令:
输出重定向命令 > 、>>。它们都将标准输出替换为文件输出。
它们的区别是:前者清空的文件的内容再写;后者保留文本原先的内容,以追加的方式写入。
输入重定向命令 < 。
它将键盘输入替换为文本输入。
重定向命令的用法:
假设程序的名字为prog:
prog < infile 程序prog从infile中读取字符
prog > infile
prog >> infile 程序prog将标准输出定向到infile中
下面用实例(windows下)来说明如何使用这些重定向命令。
//prog.c 将命令行参数打印出来
int main(int argc, char *agrv[])
{
int i;
for (i = 1;i < argc;i++)
{
printf("%s\n", agrv[i]);
}
return 0;
}
//anotherprog.c 将输入的名字前后加上“Hello”和“!”并打印。
#include <stdio.h>
int main ()
{
char name[100];
while (gets(name) != NULL)
{
printf("Hello %s !\n", name);
fprintf(stderr, "test stderr\n");//测试stderr的效果
}
return 0;
}
将以上两个文件放到同一目录下,打开命令提示符到该目录,用cl命令(如果不能使用cl命令,请先配置环境变量)分别将它们编译——cl prog.c 和 cl anotherprog.c。
prog May John Mike Joe | anotherprog > out.txt
将运行两个程序otherprog 和 prog,并将程序prog的标准输出通过管道重定向到程序anotherprog 的标准输入上。其中prog的命令行参数为May John Mike Joe,而且anotherprog的标准输出重定向到out.txt。
执行后会自动生成out.txt,里面会有anotherprog打印的信息。
anotherprog < out.txt > outfile.txt
运行anotherprog,标准输入重定向为out.txt,输出到outfile.txt
anotherprog < out.txt >> outfile.txt
和上条命令类似,不过这次不会清空outfile.txt,它保留文本原来的信息,并打印信息添加到后面。
上面这个例子可以很清楚的看到stderr也标准输出的不同——即使对标准输出进行了重定向,写到stderr中的输出通常也显示在屏幕上。
printf函数不多说了,直接看示例代码很清楚,还有其他的转换可以现查现用。
int main()
{
char *s = "test%test";
printf(":%s:\n", "hello, world");
printf(":%10s:\n", "hello, world");
printf(":%.10s:\n", "hello, world");
printf(":%-10s:\n", "hello, world");
printf(":%.15s:\n", "hello, world");
printf(":%-15s:\n", "hello, world");
printf(":%15.10s:\n", "hello, world");
printf(":%-15.10s:\n", "hello, world");
//宽度和精度可以用*表示,它们的值通过转化下个参数计算。
printf(":%*s:\n", 15, "hello, world");
printf(":%.*s:\n", 10, "hello, world");
printf(":%*.*s:\n", 15, 10, "hello, world");
printf(s); /* 如果字符串s含有字符% ,输出将出错。*/
printf("%c", '\n');
printf("%s", s); /* 正确*/
return 0;
}
sprintf函数执行的转换和函数printf相同,但它将输出保存在一个字符串中。
scanf是用来输入数据的,也是很常用的了。
当scanf函数扫描完其格式串,或者碰到某些输入无法与格式控制说明匹配的情况时,该函数终止。同时,成功匹配并赋值的输入项的个数将作为函数值返回。
在scanf的格式串中,空格或制表符在处理过程中将被忽略。此外,在读取输入值时,它将跳过空白符(空格、制表符、换行符等等)。
如果读取格式不固定的输入,最好每次读入一行,然后再用sscanf将合适的格式分离读入,它返回值也是成功读取的项数。
观察printf的使用可知,printf函数的传人参数是不定的,这可以用变长参数表去实现。
在vs2005中键入printf可以看到它的声明形式为:
int printf(__in_z __format_string const char * _Format, ...);
带有变长参数表的函数必须至少含有一个有名参数,而且变长参数要放到最后,但是我们应该怎么处理这些连名字都没有的变长参数表呢?下面通过编写一个接受变长参数的求和函数说明。
#include <stdarg.h>//包含一组宏定义,它们对如何遍历参数表进行了定义。
void cal(int *sum, ...)
{
int i = -1; //给i赋非零值。
va_list ap; //该变量将依次引用各参数。
*sum = 0;
va_start(ap, sum);//宏va_start将ap初始化为指向第一个无名参数的指针。
while (i)
{
i = va_arg(ap, int); //假设没项都是int类型。
*sum += i;
}
va_end(ap); //结束时的清理工作。
}
int main()
{
int sum = 0;
cal(&sum, 4, 555, 323, 23);
printf("%d\n", sum);
return 0;
}
每次调用va_arg,该函数都将返回一个参数,并将ap指向下个参数。va_arg使用一个类型名来决定返回的对象类型、指针移动的补偿。最后在函数返回之前调用va_end,以完成一些必要的清理工作。
stdio.h有这样一段:
#define fgetc(_stream) (--(_stream)->_cnt >= 0 \
? 0xff & *(_stream)->_ptr++ : _filbuf(_stream))
#define putc(_c,_stream) (--(_stream)->_cnt >= 0 \
? 0xff & (*(_stream)->_ptr++ = (char)(_c)) : _flsbuf((_c),(_stream)))
#define getc(_stream) fgetc(_stream)
#define getchar() getc(stdin)
#define putchar(_c) putc((_c),stdout)
我们可以看到这些常用的函数都是用宏定义的,这样可以避免函数调用产生的开销。而且可以清楚的表面getchar与getc 和 putchar与putc的关系。
gets函数在读取字符串时将删除结尾换行符('\n'),而puts函数在写入字符串时将在结尾添加一个换行符。
当一个程序正常终止时,程序会自动为每个打开的文件调用fclose函数。但是当文件指针不再需要时就应该释放,因为大多数操作系统都限制了一个程序可以同时打开的文件数。
函数system(char *s)执行包含在字符串s中的命令,然后继续执行当前程序。s的内容很大程度与所用的操作系统相关。比较常见的有system("pause"); system("date");
在本章的最后,还第一次看到了calloc函数,它和malloc函数功能类似,都是用于动态分配存储块的。它与malloc不同的是calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不初始化,里边数据是随机的垃圾数据。malloc或calloc函数返回的指针满足正确的对齐要求。
#include <stdlib.h>
int main ()
{
int n = 10;
int *ap = (int *)calloc(n, sizeof(int));
int *bp = (int *)malloc(sizeof(int) * n);
return 0;
}