C语言学习——输入与输出
标准输入/输出
最简单的输入机制是使用getchar函数从标准输入中(一般为键盘)一次读取一个字符:
int getchar(void)
可以使用符号<来实现输入重定向,它将把键盘输入替换为文件输入:如果程序prog中使用了函数getchar,则命令行
prog <infile
将使得程序prog从输入文件infile(而不是从键盘)中读取字符。
命令行
otherprog | prog
将运行两个程序otherprog和prog,并将程序otherprog的标准输出通过管道重定向到程序prog的标准输入上。
函数
int putchar(int)
用于输出数据。
// 将指定字符放入标准输出
// 返回写入字符或EOF
如果程序prog调用了函数putchar,那么命令行
prog >输出文件名
将把程序prog的输出从标准输出设备重定向到文件中。
使用输入/输出库函数的每个源程序文件必须在引用这些函数之前包含下列语句:
#include <stdio.h>
许多程序只从一个输入流中读取数据,并且只向一个输出流中输出数据。
对于这样的程序,只需要使用函数getchar、putchar和printf实现输入/输出即可,并且对程序来说以及足够了。
格式化输出——printf函数
输出函数printf将内部数值转换为字符的形式。
// 按format整合参数,将结果输出到标准输出
// 返回实际输出字符个数
int printf(char *format, arg1, arg2, ...);
格式字符串包含两种类型的对象:普通字符和转换说明。
每个转换说明都由一个百分号字符(即%)开始,并以一个转换字符结束。
在字符%和转换字符中间可能依次包含下列组成部分:
- 负号,用于指定被转换的参数按照左对齐的形式输出。
- 数,用于指定最小字段宽度。转换后的参数将打印不小于最小字段宽度的字段。
- 小数点,用于将字段宽度和精度分开。
- 数,用于指定精度,即指定字符串中要打印的最大字符数、浮点数小数点后的位数、整型最少输出的数字数目。
- 字母h或l,字母h表示将整数作为short类型打印,字母l表示将整数作为long类型打印。
printf函数基本的转换说明
字符 | 参数类型;输出形式 |
---|---|
d,i | int类型;十进制数 |
o | int类型;无符号八进制数(没有前导0) |
x,X | int类型;无符号八进制数(没有前导 0x 或 0X],10~15分别用abcdef或ABCDEF表示 |
u | int类型;无符号十进制 |
c | int类型;单个字符 |
s | char*类型;顺序打印字符串中的字符,直到有点 '\0’或已打印了由精度指定的字符数为止 |
f | double类型;十进制小数[-] m.dddddd,其中d的个数由精度指定(默认值为6) |
e,E | double类型;[-]m.dddddde+/-xx 或 [-]m.ddddddE+/-xx,其中d的个数由精度指定(默认值为6) |
g,G | double类型;如果指数小于-4或大于等于精度,则用 %e 或 %E格式输出,否则用%f格式输出。尾部的0和小数点不打印 |
p | void *类型;指针(取决于具体实现) |
% | 不转换参数,打印一个百分号% |
在转换说明中,宽度或精度可以用星号*表示,这时,宽度或精度的值通过转换下一参数(必须为int类型)来计算。
不同转换说明产生的不同结果:
// 将结果放入string
int sprintf(char *string, char *format, arg1, arg2, ...);
变长参数表
函数printf的正确声明形式:
int printf(char *fmt, ...)
省略号表示参数表中参数的数量和类型是可变的。省略号只能出现在参数表的尾部。
minprintf函数的声明:
void minprintf(char *fmt, ...)
编写函数minprintf的关键在于如何处理一个甚至连名字都没有的参数表。
标准头文件<stdarg.h>中包含一组宏定义,它们对如何遍历参数表进行了定义。
va_list类型用于声明一个变量,该变量将依次引用个参数。
参数表必须至少包括一个有名参数,va_start将最后一个有名参数作为起点。
每次调用va_arg,该函数都将返回一个参数,并将ap指向下一个参数。
va_arg使用一个类型名来绝对返回的对象类型、指针移动的步长。
最后,必须在函数返回之前调用va_end,以完成一些必要的清理工作。
#include <stdarg.h>
/* minprintf: minimal printf with variable argument list */
void minprintf(char *fmt, ...)
{
va_list ap; /* points to each unnamed arg in turn */
char *p, *sval;
int ival;
double dval;
// 将ap指向fmt后的参数的起始地址
va_start(ap, fmt); /* make ap point to 1st unnamed arg */
for (p = fmt; *p; p++)
{
if (*p != '%')
{
putchar(*p);
continue;
}
switch (*++p)
{
case 'd':
// 参数1包含了,一个参数起始地址的信息
// 参数2包含了,该起始地址处参数的类型信息
// va_arg的作用
// 返回此起始地址处存储的指定类型的值
// 将参数1改写为指向此处参数的下一个参数的起始地址
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); /* clean up when done */
}
格式化输入——scanf函数
输入函数scanf对应于输出函数printf,它在与后者相反的方向上提供通用的转换功能。
具有变长参数表的函数scanf的声明形式如下:
int scanf(char *format, ...)
从标准输入中读取字符序列,按照format中的格式说明对字符序列进行解释,并把结果保存到其余的参数中。
另外一个输入函数sscanf,它用于从一个字符串(而不是标准输入)中读取字符序列:
int sscanf (char *string, char *format, arg1,arg2, ...)
按照格式参数format中规定的格式扫描字符串string,并把结果分别保存到arg1、arg2、… 这些参数中。这些参数必须是指针。
格式串通常都包含转换说明,用于控制输入的转换。
格式串可能包含下列部分:
- 空格或制表符,在处理过程中将被忽略。
- 普通字符(不包括%),用于匹配输入流中下一个非空表符字符。
- 转换说明,依次由一个%、一个可选的赋值禁止字符 * 、一个可选的数值(指定最大字段宽度)、一个可选的h、l或L字符(指定目标对象的宽度)以及一个转换字符组成。
转换字符指定对输入字段的解释。对应的参数必须是指针,这也是C语言通过值调用语义所要求的。
scanf函数的基本转换说明
字符 | 参数类型;输出形式 |
---|---|
d | 十进制整数 int* |
i | 整数 int*,这整数可能是8进制或16进制的[分别以0或0x/0X开头] |
o | 8进制整数[有或没有前导0均可] int* |
u | 无符号10进制数 unsigned int* |
x | 16进制整数[有或没有前导0均可] unsigned int* |
c | 字符 char*。下个输入字符被放入指示位置。空白也为作为结果读入,而非跳过。要读下个非空白字符用 %1s |
s | 字符串 char*。指向一个字符数组。足够长以容纳字符串和’\0’ |
e,f,g | 浮点数,可选的符号/小数点/指数 float* |
% | 字符% |
d/i/o/u/x 前面可加前缀h指示参数为short*,或l指示参数为long*
e/f/g的前面可加前缀l,指示参数为double*
// 25 Dec 1988
int day, year;
char monthname[20];
scanf("%d %s %d", &day, monthname, &year);
// mm/dd/yy
int day, month, year;
scanf("%d/%d/%d", &month, &day, &year);
scanf函数忽略格式串中的空格和制表符。
此外,在读取输入值时,它将跳过空白符(空格、制表符、换行符等等)。
while (getline(line, sizeof(line)) > 0)
{
if (sscanf(line, "%d %s %d", &day, monthname, &year) == 3)
printf("valid: %s\n", line); /* 25 Dec 1988 form */
else if (sscanf(line, "%d/%d/%d", &month, &day, &year) == 3)
printf("valid: %s\n", line); /* mm/dd/yy form */
else
printf("invalid: %s\n", line); /* invalid form */
}
scanf函数可以和其他输入函数混合使用。
无论调用那个输入函数,下一个输入函数的调用将从scanf没有读取的第一个字符处开始读取数据。
文件访问
启动一个C语言程序时,操作系统环境负责打开3个文件,并将这三个文件的指针提供给该程序
3个文件分别是标准输入,标准输出,标准错误,
相应的文件指针分别为stdin, stdout, stderr
#include <stdio.h>
FILE *fp;
FILE *fopen(char *name, char *mode);
int getc(FILE *fp)
int putc(int c, FILE *fp)
#define getchar() getc(stdin)
#define putchar(c) putc((c), stdout)
int fscanf(FILE *fp, char *format, ...)
int fprintf(FILE *fp, char *format, ...)
int fclose(FILE *fp)
stdin/stdout/stderrs是FILE*类型常量
错误处理——stderr 和 exit
int ferror(FILE *fp)
如果流fp中出现错误,则函数ferror返回一个非0值。
int feof(FILE *fp)
函数fefo(FILE*)与ferror类似。如果指定的文件到达文件结尾,它将返回一个非0值。
行输入和行输出
char *fgets(char *line, int maxline, FILE *fp)
fgets函数从fp指向的文件中读取下一个输入行(包括换行符),并将它放在字符数组line中,它最多可读取maxline-1个字符。
读取的行将以 '\0’结尾保存到数组中。
输出函数fputs将一个字符串(不需要包含换行符)写入到一个文件中:
int fputs(char *line, FILE *fp)
如果发生错误,该函数将返回EOF,否则返回一个非负值。
库函数gets和puts的功能与fgets和fputs函数类似,但他们是对stdin和stdout进行操作。
gets函数在读取字符串时将删除结尾的换行符(’\n’),而puts函数在写入字符串时将在结尾添加一个换行符。
fget,fput从文件读取,放入文件,函数自身未对字符作额外干预.get,put对字符做了额外干预,get额外做了删除得到字符末尾\n,put额外做了在字符末位增加\n再放入,导致了两者的不一致
/* fgets: get at most n chars from iop */
char *fgets(char *s, int n, FILE *iop)
{
register int c;
register char *cs;
cs = s;
while (--n > 0 && (c = getc(iop)) != EOF)
if ((*cs++ = c) == '\n')
break;
*cs = '\0';
return (c == EOF && cs == s) ? NULL : s;
}
/* fputs: put string s on file iop */
int fputs(char *s, FILE *iop)
{
int c;
while (c = *s++)
putc(c, iop);
return ferror(iop) ? EOF : 0;
}
/* getline: read a line, return length */
int getline(char *line, int max)
{
if (fgets(line, max, stdin) == NULL)
return 0;
else
return strlen(line);
}
其他函数
字符串操作函数
字符串函数strlen、strcpy、strcat和strcmp,它们都在头文件<string.h>中定义。
s与t为char * 类型, c与n为 int类型。
strcat(s,t) 将t指向的字符串连接到s指向的字符串的末尾
strncat(s,t,n) 将t指向的字符串中前n个字符连接到s指向的字符串的末尾
strcmp(s,t) 根据s指向的字符串小于(s<t)、等于(s==t)或大于(s>t)t指向的字符串的不同情况,分别返回负整数、0或正整数
strncmp(s,t,n) 同strcmp相同,但只在前n个字符中比较
strcpy(s,t) 将t指向的字符串复制到s指向的位置
strncpy(s,t,n) 将t指向的字符串中前n个字符复制到s指向的位置
strlen(s) 返回s指向的字符串的长度
strchr(s,c) 在s指向的字符串中查找c,若找到,则返回指向它第一次出现的位置的指针,否则返回NULL
strrchr(s,c) 在s指向的字符串中查找c,若找到,则返回指向它最后一次出现的位置的指针,否则返回NULL
字符类别测试和转换函数
头文件<ctype.h> 中定义了一些用于字符测试和转换的函数。
isalpha(c) 若c是字母,则返回一个非0值,否则返回0
isupper(c) 若c是大写字母,则返回一个非0值,否则返回0
islower(c) 若c是小写字母,则返回一个非0值,否则返回0
isdigit(c) 若c是数字,则返回一个非0值,否则返回0
isalnum(c) 若isalpha(c)或isdigit(c),则返回一个非0值,否则返回0
isspace(c) 若c是空格、横向制表符、换行符、回车符、换页符或纵向制表符、则返回一个非0值
toupper(c) 返回c的大写形式
tolower(c) 返回c的小写形式
ungetc函数
int ungetc(int c, FILE *fp)
该函数将字符c写回到文件fp中。如果指向成功,则返回c,否则返回EOF。
每个文件只接收一个写回字符,比较受限制。
命令执行函数
函数system(char*s)执行包含在字符串s中的命令,然后继续执行当前程序。
s的内容在很多程度上与所用的操作系统有关。
存储管理函数
函数malloc和calloc用于动态地分配存储块。
函数malloc的声明如下:
void *malloc(size_t n)
当分配成功时,它返回一个指针,
该指针指向n字节长度的未初始化的存储空间,否则返回NULL。
函数calloc的声明为:
void *calloc(size_t n, size_t size)
当分配成功时,它返回一个指针,该指针指向的空闲空间足以容纳由n个指定长度的对象组成的数组,否则返回NULL。
数学函数
头文件<math.h>中声明了20多个数学函数。
sin(x) x的正弦函数,其中x用弧度表示
cos(x) x的余弦函数,其中x用弧度表示
atan2(y,x) y/x的反正切函数,其中,x和y用弧度表示
exp(x) 指数函数 e^x
log(x) x的自然对数(以e为底),其中,x>0
log10(x) x的常用对数(以10为底),其中,x>0函数
pow(x,y) 计算x^y的值
sqrt(x) x得平方根(x >= 0)
fabs(x) x的绝对值
随机数发生器函数
函数rand()生成介于0和RAND_MAX之间的伪随机整数序列。
其中RAND_MAX是在头文件<stdlib.h>中定义的符号常量。
下面是一种生成大于等于0单小于1的随机浮点数的方法:
#define frand() ((double) rand() / (RAND_MAX+1.0))
函数srand(unsigned)设置rand函数的种子数。
学习参考资料:
《C程序设计语言》第2版 新版