资讯详情

C语言标准库深度整理

文章目录

    • 引言
    • stdio.h
      • 常量和指针
      • 打开文件
      • FILE结构体
      • 读写和定位文件
      • 其他操作文件和路径
      • get和put
      • printf
    • stdlib.h
      • 内存分配
      • 系统交互
      • system函数
      • 字符串函数
      • 数学函数和算法
    • string.h
    • ctype.h
    • math.h
    • 时间与货币
      • time.h
      • locale.h
    • 单功能库
      • assert.h
      • stdarg.h
      • stddef.h
      • setjmp.h
    • 常量库

引言

C语言标准库是定义在15个头文件中的内置函数和常量,为C语言提供了最基本的功能

即标准输入输出,涵盖文件读写的全过程。

所谓标准输入输出,直观点是键盘和显示屏:键盘将标准输入传输到终端,终端将内容打印在显示屏上。关于基于字符串的人机交互,stdio.h主要提供两组函数,一组是getput,另一组是printscan

fopen打开文件,到fread, fwrite读写文件,再到fflush最后,强制写入缓存fclose关闭文件;如果需要准确定位文件的字节位置,可以通过fseekfgetpos

即是standard library,其他类别的标准库主要分为内存、系统、字符串转值和数学算法四个部分。

:提供查询、比较、复制字符串等功能

:判断数据类型

:提供最基本的数学运算

:C标准库为不同国家提供本地化头函数,具体体现在时间和货币上。C标准库本地化描述了不同地区的时间和货币表达,并分别包装在time.hlocale.h中。

有很多库十分简单,故统一归类到单一功能库下,包括

  • stdarg.h:支持可变参数
  • assert.h:用于报错
  • stddef.h:指针相减
  • setjmp.h:跳转

一些标准库只定义了一些宏,单并没有声明函数,因此将这些库统一归类为常量库。

  • 系统错误码errno.h
  • 信号signal.h
  • 浮点限制信息float.h
  • 整形限制信息limits.h

stdio.h

std是标准的,io即输入输出,合起来stdio是标准输入输出。

常量和指针

#define 功能 默认操作系统
EOF (-1) 文件结束符
BUFSIZ 1024 setbuf函数缓冲区字节数 __BUFSIZ__
FOPEN_MAX 20 系统可同时打开的文件数量 __FOPEN_MAX__
FILENAME_MAX 1024 最大长度的文件名 __FILENAME_MAX__
L_tmpnam FILENAME_MAX tmpnam创建的临时文件名的最大长度 __L_tmpnam__
TMP_MAX 26 tmpnam最多可以生成独立文件名称

stdio.h定义了三个FILE指针类型,

#define stdin (_REENT->_stdin) #define stdout (_REENT->_stdout) #define stderr (_REENT->_stderr) 

其中,_REENTreent.h_impure_ptr定义,而_impure_ptr则为_reent的指针。

_reent定义了三个FILE型的指针__FIE *_stdin, *_stdout, *_stderr;,分别代表标准输入、标准输出和标准错误。

打开文件

fopenfreopen均为C语言标准库stdio.h中的函数,分别用于打开和重新打开某个stream,二者均返回一个FILE指针。

  • FILE *fopen(const char *filename, const char *mode)
  • FILE *freopen(const char *filename, const char *mode, FILE *stream)

其中filename为文件路径,mode为文件打开模式,freopen中输入的的stream为现存的流,freopen将新打开的文件注入stream中,同时关闭旧文件。

其中mode的取值如下,第一列为常规模式,第二列为二进制模式,在二进制模式下,读取的是二进制文件,其他与常规模式相同。

mode 模式 说明
“r” “rb” 只读 打开的文件必须存在。
“w” “wb” 写入 创建空文件,若文件已存在,会删除原有内容。
“a” “ab” 追加 向文件末尾追加数据,若文件不存在,则创建文件。
“r+” “rb+” 更新 打开一个文件进行读写,该文件必须存在。
“w+” “wb+” 读写 创建一个用于读写的空文件。
“a+” “ab+” 打开一个用于读取和追加的文件

fopenfreopen的返回值为FILE指针,刚好可以通过stdio.h中的close进行关闭。

stdio.h中定义了三个单参函数用以调控文件的写入,其输入均为FILE *stream,若成功则返回1,失败则返回0。

返回类型 函数 功能
int fclose 关闭stream,刷新缓冲区
int fflush 刷新stream的输出缓冲区
int ferror 测试stream的错误标识符

如果在没有完成写入的情况下调用close,可能会致使数据丢失,故stdio.h中提供了fflush函数,用于强制将缓冲区的内容写入文件。而fclose则综合了二者的功能,先flush,再close。

FILE结构体

FILE是C语言标准库stdio.h中定义的一个结构体,用于数据缓存,一般写为

//stdio.h
typedef struct _iobuf
{ 
        
    char*  _ptr;        //文件输入的下一个位置
    int    _cnt;        //当前缓冲区的相对位置
    char*  _base;       //文件初始位置
    int    _flag;       //文件标志
    int    _file;       //文件有效性
    int    _charbuf;    //缓冲区是否可读取
    int    _bufsiz;     //缓冲区字节数
    char*  _tmpfname;   //临时文件名
} FILE;

其中,_bufsiz为缓冲区字节数,一般由宏来定义

#define BUFSIZ 1024

临时文件名_tmpfname可通过函数tmpnam进行设置,或者通过tmpfile创建二进制文件。

为了理解这些字段的含义,可以写一个小例子

//test.c
#include<stdio.h>

//这个函数用来打印FILE内部的字段
void printFILE(FILE *fp){ 
        
    printf("fp->_ptr=%s\n",fp->_ptr);
    printf("fp->_cnt=%d\n",fp->_cnt);
    printf("fp->_base=%s\n",fp->_base);
    printf("fp->_flag=%d\n",fp->_flag);
    printf("fp->_file=%d\n",fp->_file);
    printf("fp->_charbuf=%d\n",fp->_charbuf);
    printf("fp->_bufsiz=%d\n",fp->_bufsiz);
    printf("fp->_tmpfname=%s\n",fp->_tmpfname);
    printf("-------------------------------------");
    printf("-------------------------------------\n");
}

int main(){ 
        
    FILE* fp = fopen("test.c","r"); //打开test.c文件,即本文件
    printFILE(fp);

    char buf[20];
    fread(buf,1,5,fp);      //读取fp中的数据
    printFILE(fp);

    fclose(fp);             //关闭fp
    printFILE(fp);
    return 0;
}

得到其输出如下,为了便于阅读,用///**/对输出进行注释

>gcc test.c
>a.exe
fp->_ptr=(null)     //打开文件后,由于未作操作,故该指针为空
fp->_cnt=0          //当前缓冲区相对位置为0
fp->_base=(null)    //文件初始位置也是一个空指针
fp->_flag=1         
fp->_file=3         
fp->_charbuf=0      //缓冲区文件
fp->_bufsiz=0       //缓冲区是空的
fp->_tmpfname=(null)   
--------------------------------------------------------------------------
// 下面的输出是在执行fread之后,
// 由于读取了5个字节,所以指针跳过了#incl
fp->_ptr=ude<stdio.h>

void printFILE(FILE *fp){ 
        
    /* 这一段将test.c中的内容原封不动地打印了出来 因为太长,所以省略不写了 */
    printFILE(fp);
    return 0;
}
LE(fp);
    return 0;
}

fp->_cnt=665        #当前缓冲区的相对位置是65
fp->_base=#include<stdio.h>     #指向文件初始位置的指针

void printFILE(FILE *fp){ 
        
    /* 这一段将test.c中的内容原封不动地打印了出来 因为太长,所以省略不写了 */
    printFILE(fp);
    return 0;
}
LE(fp);
    return 0;
}

fp->_flag=9
fp->_file=3
fp->_charbuf=0
fp->_bufsiz=4096        //缓冲区尺寸为4096
fp->_tmpfname=(null)    //并没有临时名字
--------------------------------------------------------------------------
// 关闭文件后,一切又变得未知,但缓冲区尺寸并未变化
fp->_ptr=(null)
fp->_cnt=0
fp->_base=(null)
fp->_flag=0
fp->_file=3
fp->_charbuf=0
fp->_bufsiz=4096
fp->_tmpfname=(null)
--------------------------------------------------------------------------

在上面的输出中,_flag值在打开文件后为1,读取文件后变为9,关闭文件后变为0.

_file则一直为3。

stdio.h中定义了一系列常量用于描述文件流的状态

#define __SLBF 0x0001 /* 行缓冲 */
#define __SNBF 0x0002 /* 无缓冲 */
#define __SRD 0x0004 /* 可读 */
#define __SWR 0x0008 /* 可写 */
#define __SRW 0x0010 /* 可读写 */
#define __SEOF 0x0020 /* 发现 EOF */
#define __SERR 0x0040 /* 发现 error */
#define __SMBF 0x0080 /* _buf来自内存(malloc) */
#define __SAPP 0x0100 /* fdopen()ed in append mode */
#define __SSTR 0x0200 /* 是一个sprintf/snprintf字符串 */
#define __SOPT 0x0400 /* 进行fseek()优化 */
#define __SNPT 0x0800 /* 不进行fseek()优化 */
#define __SOFF 0x1000 /* set iff _offset is in fact correct */
#define __SMOD 0x2000 /* true => fgetln modified _p text */
#define __SALC 0x4000 /* 动态分配字符串内存*/
#define __SIGN 0x8000 /* 在_fwalk是忽略本文件*/

_flag的值为1,表示启用行缓冲;为9,则是__SLBF | __SWR,说明可写。

文件读写和定位

stdio.h中定义了读、写还有查找的函数,其中fread用于将文件中的数据写入内存;fwrite将内存中的数据写入文件;fseek则操作文件指针,使之偏移,从而可以更加灵活地读取。

  • 读:size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
  • 写:size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
  • 移:int fseek(FILE *stream, long int offset, int whence)

其中,ptr为指向某个内存块的指针;stream为文件的数据流;nmemb为元素个数,size为每个元素的字节数;whence为添加偏移的位置,offset为相对于whence的偏移量。

stdio.h中为whence定义了宏,其含义如下

#define SEEK_SET 0 从0开始
#define SEEK_CUR 1 从当前位置开始
#define SEEK_END 2 从EOF开始

读写相对来说比较直观,接下来主要测试一下fseek,说明均在注释中。

//test.c
#include<stdio.h>
void printChar(char *buffer, int len){ 
        
	for(int i = 1; i < len; i++)
		printf("%c",buffer[i-1]);
	printf("\n-------------------------\n");
}
int main(){ 
        
    FILE* fp = fopen("test.c","r"); //打开test.c文件,即本文件

    char buf[20];
    fread(buf,1,20,fp);          //读取fp中的数据
    printf("printf the first 20 bytes:");
    printChar(buf,20);

    fseek(fp, 24, SEEK_CUR);    //从当前位置开始偏移
    fread(buf,1,20,fp);
    printf("fseek 24 from now:");
    printChar(buf,20);

    fseek(fp, 24, SEEK_SET);    //从0开始偏移
    fread(buf,1,20,fp);
    printf("fseek 24 from 0:");
    printChar(buf,20);

    fseek(fp, -20, SEEK_END);    //从结束位置向前偏移
    fread(buf,1,20,fp);
    printf("fseek 20 from END:"); 
    printChar(buf,20);

    fclose(fp);         //关闭fp
    return 0;
}

输出结果为

>gcc test.c

E:\Documents\00\1217>a.exe
printf the first 20 bytes://test.c
#include<s
-------------------------
fseek 24 from now:har *buffer, int le
-------------------------
fseek 24 from 0:.h>
void printChar(
-------------------------
fseek 20 from END:
    return 0;
}
r(
-------------------------

除了fseek可以对文件指针进行移动之外,fsetpos可以直接对文件指针进行定位。相应地,fgetpos可以获取文件指针的位置,二者声明为:

  • int fgetpos(FILE *stream, fpos_t *pos)
  • int fsetpos(FILE *stream, const fpos_t *pos)

二者的返回值均为设置之后的位置,关于输入参数,FILE *stream是大家非常熟悉的文件流,而fpos_t是一个结构体,代表相对位置,通常定义为

typedef struct
{ 
        
    unsigned long _off;
}fpos_t;

接下来做一个简单的测试

//pos.c
#include <string.h>
#include <stdio.h>
int main(void)
{ 
        
    fpos_t pos;
    FILE* fp = fopen("pos.c", "r"); //打开pos.c文件,即本文件
    fgetpos(fp, &pos);              //获取指针位置
    printf("file pointer : %ld\n", pos);
    
    fseek(fp,10,0);         // 移动指针位置
    fgetpos(fp, &pos);      // 获取指针位置并存入&pos所指向的对象
    printf("file pointer : %ld\n", pos);
    fclose(fp);
    return 0;
}

结果如下,可见getposfseek的单位是一致的,fseek移动了10个字节,则fgetpos也获取了位置10.

>gcc pos.c
>a.exe
file pointer : 0
file pointer : 10

结合fgetposfsetpos,可完成类似fseek的操作

//setpos.c
#include <string.h>
#include <stdio.h>
int main(void)
{ 
        
    fpos_t pos;
    FILE* fp = fopen("pos.c", "r");   //打开pos.c文件,即本文件
    printf("file pointer : %ld\n", fgetpos(fp, &pos));
    
    pos += 10;
    fsetpos(fp, &pos);      //设置指针位置
    printf("file pointer : %ld\n", fgetpos(fp, &pos));
    fclose(fp);
    return 0;
}

测试结果为

>gcc setpos.c
>a.exe
file pointer : 0
file pointer : 10

文件和路径的其他操作

输入为FILE *stream的单参函数,EOF为文件结束标识符。

返回类型 功能
void clearerr 清除stream的文件结束和错误标识符
int feof 返回stream的文件结束标识符,若未设置,则返回0
long int ftell 返回stream的文件位置,如果发生错误,则返回-1L,全局变量errno被设置为一个正值。
void rewind 设置文件位置为stream的开头
int fgetcgetc 从stream获取下一个字符,并把位置标识符往前移动

在打开文件后还没有做其他操作的时候,可以通过ssetvbuf来设置缓冲格式,其声明为

int setvbuf(FILE *stream, char *buffer, int mode, size_t size)

其中stream为文件流;buffer为分配给用户的缓冲;size为缓冲的字节数;mode为缓冲模式,有三种

类别 说明
_IOFBF 0 全缓冲 输出时,数据在缓冲填满时被一次性写入输入时,缓冲在请求输入且缓冲为空时被填充。
_IOLBF 1 行缓冲 输出时,在换行处或缓冲填满时写入数据输入时,缓冲至下一个换行符
_IONBF 2 无缓冲 不使用缓冲。I/O操作即时写入,忽略buffer和size

setbufsetvbuf的一个特例,其中mode_IONBFsizeBUFSIZ

stdio.h还提供了两个操作文件的函数,分别是删除文件remove和重命名文件rename,其声明分别为:

  • int remove(const char *filename)
  • int rename(const char *old_filename, const char *new_filename)

get和put

getput有互为反函数的感觉,例如getchar()从标准输入stdin获取一个字符,而putchar(int c)将字符c送到标准输出。

例如

#include <stdio.h>
int main ()
{ 
        
   char ch;
   printf("getchar: ");
   ch = getchar();
   printf("putchar: ");
   putchar(ch);
   return 0;
}

测试结果为

>gcc gets.c
>a.exe
getchar: a
putchar: a

为了便于对比阅读,下面令

#define FS FILE *stream
#define IC int char
#define CS char *str
#define CCS const char *str

则与getput有关的函数如下表所示

get fget put fput
getchar() putchar(IC)
getc(FS) fgetc(FS) putc(IC, FS) fputc(IC, FS)
gets(CS) fgets(CS, int n, FS) puts(CCS) fputs(CCS, FS)
ungetc(IC, FS)

其中,除fgetsgets的返回值为字符指针,其余均为整型,getchar()也会将输入的字符转为整型。

所有的getput函数均用于字符的输入输出,后缀c表示从文件输入或输出到文件,没有c则表示基于标准输入输出流。前缀f表示是否由宏来实现,例如getc通常由宏来实现且经过高度优化,故常作为首选,但目前来说与fgetc在速度相差无几。

ungetc把一个无符号字符推入到指定的流stream中,以便接下来被读取。

接下来做一些简单的示例

//fget.c
#include <stdio.h>
 
int main ()
{ 
        
    FILE *fp;
 
    fp = fopen("file.txt", "w+");
    //将字符逐一写入文件
    for(int

标签: 7b0bs41变送器ht2088变送器智能型压力变送器cyb

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台