标准输入/输出
- C程序的输入,有若干形式
- 运行起来以后,通过标准输入,比如键盘输入
- 调用程序运行的时候,通过重定向,从文件获取输入
- 调用程序运行的时候,通过管道,从其他程序获取输入
- C程序的输出
- 运行起来以后,输出到标准输出,比如屏幕
- 调用程序运行的时候,通过重定向,输出到文件
- 调用程序运行的时候,通过管道,输出给其他程序
- 标准输入,输出函数
- int getchar(void)
- int putchar(int)
- printf
使用标准输入、输出函数的程序,都遵从以上形式。运行时的输入/输出来源/目的地,由运行方式决定
/* 包含标准输入输出的程序prog1, prog2,输入输出的形式 */
prog1 < infile /* 输入重定向 */
prog2 | prog1 /* 输入管道 */
prog1 > infile /* 输出重定向 */
prog1 | porg2 /* 输出管道 */
格式化输出 printf
- 格式字符串里包含普通字符和转换说明
- 普通字符原样输出
- 转换说明%开始,比较基本就不详细列出来了,挑几个我没见过的
- 可变的宽度和精度
- 字符串的宽度和精度
printf ("%*.*f", width, precise, num); /* 宽度和精度可以通过变量,在运行时确定 */
char s[] = "hwllo, world";
/* 用格式字符串输出以上的s[] */
%s "hello, world" /* 原始样式 */
%10s "hello, world" /* 右对齐,宽度10,超出宽度所以原始样式 */
%.10s "hello, wor" /* 精度10,前10个字符 */
%-10s "hello, world" /* 左对齐,宽度10,超出宽度所以原始样式 */
%.15s "hello, world" /* 精度15,精度超出实际长度,无变化 */
%-15s "hello, world " /* 左对齐,宽度15 */
%15.10s " hwllo, wor" /* 右对齐,宽度15,精度10 */
%-15.10s "hello, wor " /* 左对齐,宽度15,精度10*/
- printf函数原型,sprintf
int printf(char *format, arg1, arg2, ...);
int sprintf(char *string, char *format, arg1, arg2, ...); /* 将输出保存到一个字符串中 */
可变长参数列表的处理
通过printf, sprintf的声明(上面的是伪代码的声明),我们可以知道某些函数的参数类型和个数都不确定,如何处理这样的函数,通过一个实例给出可变参数表的处理方式,实例用于说明问题,本身并不完善
#include <stdarg.h> /* 遍历参数表,需要用到这里头文件里边的宏*/
/* minprint函数:带有可变参数表的简化的printf */
void minprintf(char *fmt, ...) /* 可变参数表的声明格式,可变部分必须是最后的一部分 */
{
va_list ap; /* va_list 类型的变量,该变量将依次引用各个参数 */
char *p, *sval;
int ival;
double dval;
va_start(ap, fmt); /* 步骤一,通过va_start将变量ap 指向第一个参数 */
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); /* 最后一步,结束的时候必须做的清理工作*/
}
格式化输入函数 scanf
int scanf(char *format, ...);
int sscanf(char *string, char *format, ...); /* 从一个字符串而不是标准输入读取输入 */
- 返回值是已经匹配上的输入项的个数
- 按照format中规定的格式扫描输入项,把结果保存到arg1, arg2 … 中去,arg1, arg2, …必须是指针
- format中可以含有:
- 空格或者制表位,处理过程中忽略
- 普通字符(不包括%),用于匹配输入流中下一个非空白字符,是有用的,决定了能够匹配上参数个数
- 转换说明,%开头的, 可以指定宽度
int i, j, k;
int count = 0;
count = scanf("%d/%dss$dtt", &i, &j, &k);
123/456ss789tt
count = 3
i = 123
j = 456
k = 789
count = scanf("%1d%2d%3d", &i, &j, &k);
123456789
count = 3
i = 1
j = 23
k = 456
文件访问
一般流程
- 读,写 一个文件之前,需要先打开这文件,获取到文件指针
- 文件指针的类型是 FILE
- 打开文件的函数,需要给出文静名和打开模式 两个参数
- 模式 Mode,有些系统还区分文本和二进制文件,二进制需要加上 b
- r 读, 如果没有这个文件,那么返回错误
- w 写,或者叫重写,文件原来的内容被清掉,如果没有这个文件,那么创建它
- a 追加,如果没有这个文件,那么创建它
- 模式 Mode,有些系统还区分文本和二进制文件,二进制需要加上 b
- 使用结束后需要关闭文件,通过关闭函数
#include <stdio.h>
FILE *fp;
FILE *fopen(char *name, char *mode);
int fclose(FILE *fp);
fp = fopen(name, mode);
fclose(fp);
- 读写函数
- getc 通过文件指针,读出下一个字符
- putc 通过文件指针,写入下一个字符
int getc(FILE *fp)
int putc(int c, FILE *fp)
stdin, stdout, stderr
- 开始运行一个C程序的时候,操作系统会自动提供3个文件指针给程序,分别是stdin, stdou和stderr
- 既然他们是文件指针,那么我们就可以当做文件来操作,包括读和写
- 通常stdin 是键盘,stdout stderr 是显示器
- 但是他们可以被重定向或者管道
/* 用getc, putc 实现getchar, putchar 同样的效果 */
#include <stdio.h>
int main()
{
int c;
while((c = getc(stdin)) != EOF)
putc(c, stdout);
return 0;
}
/* 那么getchar 和 putchar 其实可以通过宏定义实现 */
#define getchar() getc(stdin)
#define putchar(c) putc((c), stdout)
- 对文件的printf和scanf,可以通过fprintf和fscanf函数来完成
- 那么printf和scanf是不是就可以通过文件指针stdin,stdout 和fprintf fscanf来实现呢?也宏定义一下就可以了
int fscanf(FILE *fp, char *format, ...)
int fprintf(FILE *fp, char *format, ...)
一些文件操作相关函数
- int ferror(FILE *fp) 如果流fp中出现错误,那么feeror返回一个非0值
- int feof(FILE *fp) 如果指定文件到达末尾,返回一个非0值
- char *fgets(char *line, int maxline, FILE *fp) 从文件读一行出来,换行变成 ‘\0’ 保存到line中
- int fputs(char *line, FILE *fp) 将一个字符串写入到文件里,不包括’\0’
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;
}
int fputs(char *s, FILE *iop)
{
int c;
while(c = *s++)
putc(c, iop);
return ferror(iop)? EOF : 1;
}
其他常用函数
字符串操作函数
- 包含在 string.h中, s, t 是char *类型,c, n是int类型
- strcat(s, t) t接到s末尾
- strncat(s, t, n) t中的前n个,接到s末尾
- strcmp(s, t) st 分别返回负数,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中,函数返回值 int类型
- 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)
命令行执行函数
- system(char *s)
- 执行s字符串中的命令,然后继续程序运行
system("date");
存储管理函数
- malloc, calloc, free
- malloc和calloc成功都返回分配好的空间的起始位置指针,不成功返回NULL
- malloc不会初始化分配的空间
- calloc会初始化为0
- free释放由malloc/calloc 申请的空间,顺序没有要求
/* 声明 */
void *malloc(size_t, n)
void *calloc(size_t n, size_t size)
/* free的一个典型错误 */
for(p = head; p != NUL; p = p->next)
free(p);
/* 正确的方式 */
for(p = head; p != NULL; p = q)
{
q = p->next;
free(p);
}
数学函数
- 包含在math.h中,参数和返回值类型都是double类型
- sin(x)
- cos(x)
- atan2(y, x)
- exp(x) e的x次方
- log(x) e为底,x的对数,自然对数
- log10(x) 10为底,x的对数,常用对数
- pow(x, y) x的y次方
- sqrt(x) x的平方根
- fabs(x) x的绝对值
随机数发生器函数
- 包含在stdlib.h中
- rand() 0到RAND_MAX之间的随机整数
- srand(unsigned) 设置随机数种子