0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

标准C函数库的用法

PoisonApple 来源:网络整理 2018-03-02 11:27 次阅读

C标准函数库

C标准函数库是所有符合标准的头文件的集合,以及常用的函数库实现程序,例如I/O 输入输出和字符串控制。不像 COBOL、Fortran 和 PL/I等编程语言,在 C 语言的工作任务里不会包含嵌入的关键字,所以几乎所有的 C 语言程序都是由标准函数库的函数来创建的。

每一个函数的名称与特性会被写成一个电脑文件,这个文件就称为头文件,但是实际的函数实现是被分存到函数库文件里。头文件的命名和领域是很常见的,但是函数库的组织架构也会因为不同的编译器而有所不同。标准函数库通常会随附在编译器上。因为 C 编译器常会提供一些额外的非 ANSI C 函数功能,所以某个随附在特定编译器上的标准函数库,对其他不同的编译器来说,是不兼容的。

标准C函数库的用法

本篇介绍若干常用的标准C函数的用法,主要介绍stdio(标准输入输出)、math(数字函数库)、time(时间函数库)、stdlib(标准函数库)string(标准字符串函数)等。

1.1 stdio.h

标准输入/输出函数,stdio即standard input/output。其中文件操作相关API在单独一章中介绍。

#include 《stdio.h》

char getchar(void); // 控制台输入一个字符

int putchar(int c); // 控制台输出一个字符

char *gets(char *s); // 控制台输入一个字符串

int puts(const char *s); // 控制台输出一个字符串

int printf(const char *format, 。。。); // 控制台格式化输出

int scanf(const char *format, 。。。); // 控制台格式化输入

int sprintf(char *s, const char *format, 。。。); // 字符串格式化输出

int sscanf(const char *s, const char *format, 。。。); // 字符串格式化输入

1.1.4 sprintf与sscanf

适用于string版本的格式化输入/输出,其目标不是控制台,而是一个字符串。这两个函数非常有用。

用sprintf格式化一个字符串,例如,

[cpp] view plain copychar buf [256];

sprintf (buf, “Name: %s, Age: %d, Height: %.2f \n”, “LiMing“, 30, 1.68 );

此代码将目标buf格式化为: Name: LiMing, Age 30, Height: 1.68

用sscanf从一个具有格式的字符串中提取固定字段,和scanf的用法类似,要求在格式上要严格匹配。以下代码从字符中提供取年月日的值。

[cpp] view plain copychar* src = ”2014-12-11“;

int year, month, day;

int n = sscanf ( src,

”%d-%d-%d“,

&year, &month, &day);

if( n == 3)

{

// 成功提取了所有字段

}

1.2 math.h

提供了一系列数学相关的函数,如三角函数、指数/对数、幂/根号等。

#include 《math.h》

double abs(double x); // 取绝对值

double cos(double x); // 余弦cos, 参数是弧度值

double sin(double x); // 正弦sin, 参数是弧度值

double tan(double x); // 正切 tan, 参数是弧度值

double ceil(double x); // 向上取整, 即不小于x的最小整数

double floor(double x); // 向下取整, 即不大于x的最大整数

double exp(double x); // 求ex

double log(double x); // ln(x), 以e为底的对数

double log10(double x); // lg(x), 以10为底的对数

double pow(double x, double y); // 求幂xy

double sqrt(double x); // 求平方根 x1/2

需要补充说明的是,这里所列的几乎所有函数都至少有2个版本,分别是double型和float型参数。例如,

[cpp] view plain copydouble sqrt(double x);

float sqrt(float x);

以下代码编译错误,

[cpp] view plain copydouble result = sqrt (16); // 编译错误

为什么会有编译错误呢?因为字面常量16是int型,在匹配函数时,sqrt(float)与sqrt(double)均被匹配,int可以隐式转换成float和double。必须要显示把参数强转为double或float。

如果要对一个整数调用sqrt函数,那么就必须显式的强转成double或float,例如,

[cpp] view plain copydouble result = sqrt ((double)16); // OK

float result = sqrt ((float)16); // OK

1.2.1 abs求绝对值

例如 ,

[cpp] view plain copydouble a = abs ( -12.34);

1.2.2 cos/sin/tan三角函数

其单位都是弧度值。例如,

[cpp] view plain copy#define PI 3.1415926535898

double ret = sin ( PI / 2); // sin (pi/2)结果应为1

1.2.3 ceil /floor取整

用于向上/向下取整。例如,

[cpp] view plain copydouble ci = ceil ( 12.87 ); // +13

double fi = floor(12.87); // +12

ci = ceil ( -12.87 ); // -12

fi = floor( -12.87); // -13

1.2.4 exp / log / log10 / pow / sqrt指数/对数/幂/平方根

点击查看此节内容,第16章

1.3 time.h

time.h中提供了时间/日期相关的函数。这里仅列出常用的几个函数。

#include 《time.h》

struct tm *localtime(const time_t *tod); // 当“秒值”转成“年月日时分秒”

time_t mktime(struct tm *tptr); // 将“年月日时分秒”转成“秒值”

time_t time(time_t *tod); // 取得当前时间

其中,需要介绍time_t和struct tm这两个类型。

1.3.1 time_t

time_t是一个typedef的类型,目前在各种操作系统上time_t类型都是一个整数类型,差不多就是

typedef long time_t;

这种类似的定义,在各种场合都可以认为它是int型。当然,保险起见也可以显式的强转一下。它的单位是秒。

[cpp] view plain copytime_t start = 1000;

time_t end = 1020;

printf(”time eclipse: %d seconds !\n“, (int)(end - start));

1.3.2 struct tm

tm是一个结构,它的定义是,

struct tm

{

int tm_sec; /* seconds after the minute - [0,59] */

int tm_min; /* minutes after the hour - [0,59] */

int tm_hour; /* hours since midnight - [0,23] */

int tm_mday; /* day of the month - [1,31] */

int tm_mon; /* months since January - [0,11] */

int tm_year; /* years since 1900 */

int tm_wday; /* days since Sunday - [0,6] */

int tm_yday; /* days since January 1 - [0,365] */

};

它用于表示日期/时间,有几个字段组成:年,月,日,时,分,秒,weekday和yearday。其中,

年:从1900开始算,tm_year = 114表示年份 1900 + 114 = 2014

月:范围是[0,11], tm_mon = 11表示月份 12

日:范围是[1,31], tm_mday = 24表示该月的第24天

时: 范围是[0,23],tm_hour = 13表示下午13时

分: 范围是[0,59], tm_min = 40表示第40分钟

秒:范围是[0,59], tm_sec = 40表示第40秒钟

tm_wday: 范围是[0,6],星期日是0,星期一是1,。。。 ,星期六是6

tm_yday: 范围是[0,365],tm_yday=299,表示当年的第300天

当tm_min和tm_sec都为0时,表示整点时间。如12:00:00。

例如,2014-12-11 11:47:12这个时间可以用下面的代码赋值:

[cpp] view plain copytm info;

info.tm_year = 2014 - 1900; // 2014年

info.tm_mon = 12 - 1; // 12月

info.tm_mday = 11; // 11日

info.tm_hour = 11; // 11时

info.tm_min = 47; // 47分

info.tm_sec = 12; // 12分

1.3.3 time取得系统当前时间

time函数可以取得系统当前时间,返回值是一个秒值。例如,

[cpp] view plain copytime_t now = time (NULL);

为什么一个整数秒值可以表示当前的时间呢?是这么规定的,time函数返回的是自1970-1-1 00:00:00这个时间点开始至当前时刻的时间差。它是一个比较大的整数,例如1418270153表示的是2014-12-11 11:55:53这个时刻。

利用time函数时可以计算程序运行了多少时间,如下面的代码:

[cpp] view plain copytime_t start = time (NULL);

。。。 DoSomething 。。。

time_t end = time (NULL);

printf(”Time cost : %d seconds“, end - start );

当然这个DoSomething要运行相当时间才行,因为time的粒度较大,返回是秒值。如果你的DoSomething只耗费了几毫秒的话,那么用time根本无法衡量。不过,可以成倍的量化一下,比较,将DoSomething连续运动10000次,看需要的总时间,然后再平均一下得出单次需要的时间。

[cpp] view plain copytime_t start = time (NULL);

for( int i=0; i《 10000; i++)

{

。。。 DoSomething 。。。

}

time_t end = time (NULL);

int avg = (end - start ) / 10000;

1.3.4 localtime()得到年月日时分秒

虽然用一个time_t整数来表示当前系统时间是比较方便的,但有时候还是希望能转化成年月日时分秒的形式来显示,毕竟肉眼无法直接看一个time_t值到底是哪一个日期。

localtime函数可以将time_t所表示的时间转化成年月日时分秒,例如,

[cpp] view plain copytime_t t = time(NULL);

tm info = *localtime(&t);

printf(”%04d-%2d-%02d %02d:%02d:%02d \n“,

info.tm_year + 1900,

info.tm_mon + 1,

info.tm_mday,

info.tm_hour,

info.tm_min,

info.tm_sec);

localtime的返回值是tm*类型,应该用一个tm变量将内容保存起来。

事实上,用time_t来记录时间更方便,只用一个整数(占4个字节)就表达了日期和时间信息。在保存和传输的时候,应该尽量用time_t类型。只是在最终显示的时候,用localtime转成人类易读的yyyy-mm-dd HH:MM:SS格式。

1.3.5 mktime构造时间

当已知了年月日时分秒信息,可以用mktime换算成time_t值。例如,把2014-12-11 11:47:12转成time_t值,

点击查看此节内容,第16章

1.4 stdlib.h

这一节将介绍stdlib.h里提供的API,下面列表其中主要的几个函数:

#include 《stdlib.h》

double atof(const char *s);

int atoi(const char *s);

int rand(void);

void srand(unsigned int seed);

int system(const char *s);

1.4.1 atoi / atof 字符串转成数字

例如,

[cpp] view plain copyint n = atoi(”1280“);

double f = atof(”12.80“);

其实完全可以用sscanf来完成相同的事情,例如,

[cpp] view plain copyint n;

double f;

sscanf(”1280“, ”%d“, &n);

sscanf(”12.80“, ”%f“, &f);

相比之下,使用atoi和atof更简洁一些。

1.4.2 rand / srand 随机数生成

在抽签、抽奖等涉及“随机事件”的应用场景中,需要随机数生成函数。由于计算机中并没有随机性和偶然性,想制造一个随机数实际上是一件比较困难的事情。要真正随机的数字,需要购置在非常昂贵的硬件,这些硬件利用某些自然科学的规则来生成随机数,因为价格昂贵,所以专业的随机数生成器只用于专业用途。我们这里介绍的适用普通PC机能够胜任的“伪随机数”生成函数,能够生成近似随机的数据就可以了。

rand函数用于生成随机数,该函数返回一个整数。调用以下代码测试一下:

[cpp] view plain copyfor(int i=0; i《10; i++)

{

printf(”%d \n“, rand());

}

控制台输出了10个完全没有规律的数字,因为完全无规律可循,所以称它为随机数。这里为了显示方便,只生成了10个随机数,实际上可以改成1000次、10000次试试,会发现它确实是杂乱无章、完全没有规律的出现的。

那么为什么说它是“伪随机数”呢?说明它没有真正的实现“随机”。可以这么验证一下,把相同的程序反复运行数次,会发现每次程序运行输出的结果都是相同的一个序列的数字。例如,第一次运行程序的时候输出13435 31833 5075 19863 30565 11677 1339 4096 31105 9088等10个随机数,当关闭程序再次运行时,输出的10个随机数还是这10个数。

为了解决这个问题,stdlib.h里提供了srand函数。srand函数用于为程序设置一个种子(seed),当种子不同时,程序产生的随机数序列也不同。srand只需要在程序开始时设置一次,例如,可以在main()函数开始时运行一次。种子怎么定呢?要保证程序每次运行时,这个种子的值不同,一般来说是取系统时间来作为种子的。

[cpp] view plain copyvoid main()

{

srand (time (NULL));

。。。 do something else 。。。

}

每次程序运行时,time(NULL)返回的时间是不同的,于是srand每次都是使用了不同的种子。在程序中再调用rand()来生成随机数时,会发现每次程序运行生成的随机数序列是不同的。

在实际应用中如何使用rand函数呢?例如,有一种彩票叫“七星彩”,每次生成的中奖号码是7个届于0~9之间的随机数字。可以用rand来随机生成一注号码。

[cpp] view plain copyvoid main()

{

srand (time (NULL));

int code[7]; // 一注号码为7个数字

for(int i=0; i《7; i++)

{

int r = rand () % 10; // 取模使每个数字界于0~9之间

code[i] = r;

}

}

注意,一般都要为rand()生的随机数指定一个区间。

生成[100,120]之间的随机数,

[cpp] view plain copyint r = 100 + rand () % 20;

生成[0,1]区间的随机小数,

double r = rand()/ (double)RAND_MAX;

其中, RAND_MAX在VC下的定义是32767,在其他操作系统下可能是其他值。

例题:给定10个数,要求从中每次随机选出5个数。

思路:首先,从10个数里面随机挑选出1个数,然后再从剩下的9个数里挑选1个,然后再从剩下的8个数里挑选1个。。。。。。

设计:用一个flags数组来表示哪个数已经被选中了,例如 flags[3] = 1表示第第4个数已经被选中,flags[9]=0表示第10个数未被选中。

点击查看此节内容,第16章

1.4.3 system 调用系统命令行

用system函数可以调用系统命令行,在windows下可以执行DOS命令行, 在linux下SHELL命令行。比如,删除d:\aaa.pdf这个文件,

system (”del /F /Q d:\\aaa.pdf“);

其实原则上并不限于DOS命令,所以的命令行都可以运行的。例如,调用浏览器打开一个网站,

[cpp] view plain copysystem (”explorer http://www.afanihao.cn“);

1.5 string.h

string.h中提供了一系统内存操作函数及字符串操作函数。在学习本节之前,一定要先学习第15章中,掌握字符串的意义。

#include 《string.h》

char *strcat(char *s1, const char *s2); // 拼接字符串

char *strchr(char *s, int c); // 查找字符

int strcmp(const char *s1, const char *s2); // 字符串比较

char *strcpy(char *s1, const char *s2); // 拷贝字符串

char *strstr(char *s1, const char *s2); // 查找子串

size_t strlen(const char *s); // 计算长度

int memcmp(const void *s1, const void *s2, size_t n); //按内存比较

void *memcpy(void *s1, const void *s2, size_t n); // 按内存拷贝

void *memmove(void *s1, const void *s2, size_t n); // 移动数据

void *memset(void *s, int c, size_t n); // 按字节填充内存

1.5.1 strcpy拷贝字符串

strcpy(a,b)用于将字符串b拷贝到目标缓冲区,

如,

[cpp] view plain copychar buf[128];

strcpy(buf, ”LiMing“); // 目标缓冲区内容拷贝为”LiMing“

1.5.2 strcat拼接字符串

strcat(a,b)用于将字符串b拼接于字符串a,也就是说把字符串拷贝到目标字符串的末尾。此函数要求目标缓冲区足够大,

[cpp] view plain copy#include 《string.h》

void main()

{

char a [128] = ”hello“;

char b [] = ”world“;

strcat(a, b); // 目标a结果为”helloworld“

}

1.5.3 strcmp比较字符串

关于字符串比较的意义在第15章已经讲述。strcmp(a,b)用于比较字符串,当返回为0时表示完全相等,小于0时表示a《b,大于0时表示a》b。

[cpp] view plain copyint ret = strcmp(”Jack“, ”Jacky“); // 返回值 ret=-1,表示”Jack“《”Jacky“

1.5.4 strlen求字符串长度

字符串求长度时,结束符‘\0’不计算在内。例如,

[cpp] view plain copyint n = strlen(”LiMing“); // 返回长度n为6

1.5.5 strchr查找字符

strchr(s, c)用于在字符串s中查找字符c,并返回第一处匹配的位置。其返回值是char*类型,表示匹配的位置,

[cpp] view plain copychar* s = ”LiMing“;

char* p = strchr(s, ‘M’); // 返回值p指向字符‘M’的地址

if(p!= NULL)

{

printf(”find: %s \n“, p);

}

1.5.6 strstr查找子串

点击查看此节内容,第16章

1.5.7 memset内存填充

[cpp] view plain copymemset(s, c, n)用于向目标缓冲区s中添加n个相同的字符c,例如,

unsigned char buf[128];

memset(buf, 0, sizeof(buf)); // 全部填充为0

memset(buf, 0xFF, 128); // 全部填充为0xFF

memset(buf, 0x55, 100); // 前100个字节填充为0x55

memset(buf+100, 0x77, 10); // 100..109填充为0x77

1.5.8 memcpy内存拷贝

点击查看此节内容,第16章

1.5.9 memcmp内存比较

点击查看此节内容,第16章

其中,a和b的比较是容易得出结果的 ,但a与c的比较呢?由于要转成unsigned char再比较,所以c》a。

1.5.10 memmove移动数据

memmove(dst, src, n)用于在内存中移动数据,将开始于src的n个字节移动到dst位置,这个函数的强大之处在于它允许src和dst有交迭。

例如,利用这个函数我们可以实现在字符串中插入字符,当插入字符串如果将插入点之后的所有字符后移,

点击查看此节内容,第16章

点击查看此节内容,第16章

1.6 stdarg.h

stdarg.h中的接口用于实现省略号参数。在函数中曾经指出,函数参数有一个特殊形式,就是不指定个数和类型,直接用省略号表示。

void test (。。。)

当参数为省略号时,可以输入任意个数的参数,而且参数的类型不受限制。我们观察printf函数的原型,发现它就是使用了省略号参数,

int printf(const char *format, 。。。);

printf的第一个参数为字符串类型,用于传递格式参数, 而后面的省略号处可以传递0..N个参数。这种灵活的传参方式在某此特殊场合会用到。下面就指出一种应用需求并给出其实现方法。

需求:自定义一个函数用于日志输出,在输出的时候自动添加日期时期信息、日志等级,其原型要求是这种形式,

void log(int level, const char* fmt, 。。。);

其中, level为日志级别:level=0时,显示ERR, level=1时显示:WRN, level=2时显示INF, level=3时显示DBG。

在调用的时候要求跟printf类似地使用格式化控制符来控制输出,并前缀以日期显示:

log(0, “My name is %s, I‘m %d years old.\n”, “Jennifer”, 30);

此需求实际上就是要实现一个自定义的打印函数,

[cpp] view plain copy#include 《stdio.h》

#include 《string.h》

#include 《stdarg.h》

void log(int level, const char* fmt, 。。。)

{

// 打印日志等级

const char* token = ”DBG“;

switch(level)

{

case 0: token = ”ERR“; break;

case 1: token = ”WRN“; break;

case 2: token = ”INF“; break;

case 3: token = ”DBG“; break;

}

printf(”[ %s ] “, token);

// 将省略号参数格式化成字符串

char buf[512];

va_list args;

va_start(args, fmt);

vsprintf(buf, fmt, args);

va_end(args);

printf(buf);

}

int main()

{

log(2, ”Name: %s, Age: %d.\n“, ”LiMing“, 30);

return 0;

}

对省略号参数的核心处理是这几行代码:

[cpp] view plain copyva_list args;

va_start(args, fmt);

。。。 此处已经将参数取得在args里。。。

va_end(args);

能够以va_list作为参数的函数有vsprintf, vprintf, vfprintf,其原型是:

int vprintf(const char *format, va_list ap); // 输出到控制台

int vsprintf(char *s, const char *format, va_list ap); // 输出到字符串缓冲区

int vfprintf(FILE *stream, const char *format, va_list ap); // 输出到文件流

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 函数库
    +关注

    关注

    1

    文章

    82

    浏览量

    32329
  • 标准函数
    +关注

    关注

    0

    文章

    3

    浏览量

    6313
  • c函数
    +关注

    关注

    0

    文章

    11

    浏览量

    7477
收藏 人收藏

    评论

    相关推荐

    求labview的函数库

    我的定时选板里没有这个绿色背景的时间计数器别人告诉我是需要下载相关的才行 哪位前辈能给我一个函数库我不知道怎么弄
    发表于 02-21 19:06

    函数库.dll问题

    labview能调用两个函数库吗?在标题栏能切换两个吗?
    发表于 03-31 14:24

    PIC的官方函数库在哪里

    PIC的官方函数库在哪里,MPLAB IDEXC16,是在编译器的安装目录下吗?想要用里面的函数,可是没有找到传说中的函数库
    发表于 05-18 10:34

    请教关于C6416T-1G 函数库的问题

    1、需要使用C6416来解一个复杂方程,需要用到trust-region-dogleg算法,编写难度很大,有6416可以支持的函数库吗?主要是矩阵运算。2、GSL函数库(用C编写的)是
    发表于 08-01 06:57

    STM32F101xx和STM32F103xx的固件函数库详细资料

    MISRA-C2004 标准(根据需要,我们可以提供兼容矩阵)。由于整个固态函数库按照“Strict ANSI-C标准编写,它不受不同开
    发表于 09-03 15:08

    STM32固件函数库资料分享!

    应用每一个外设。因此,使用本固态函数库可以大大减少用户的程序编写时间,进而降低开发成本。 所有的驱动源代码都符合“Strict ANSI-C标准(项目于范例文件符合扩充 ANSI-C
    发表于 08-31 17:26

    研华板卡1245函数库

    第一次使用研华板卡,型号是1245L‘但是函数库加载不到Labview里面,研华的卡的函数库是从工具里面导入共享的吗’,但是解析出来的是空的,怎么办,各位大神求解
    发表于 09-04 08:22

    什么是STM32函数库

    文章目录- 自己写—构建库函数雏形1. 什么是 STM32 函数库2. 为什么采用来开发及学习?3. 实验:构建库函数雏形3.1 外设寄
    发表于 08-05 07:30

    使用函数库编程控制GPIO口输出

    使用函数库编程控制GPIO口输出看了网上许多人的代码以及各类开发板所带的例程,大多数使用的都是官方发布的函数库来编程,通过查询后发现,使用函数库来编程可以简化开发过程,并不需要追溯到各个寄存器,通过
    发表于 08-05 06:30

    为什么采用来开发及学习STM32函数库

    什么是STM32函数库?为什么采用来开发及学习STM32函数库呢?
    发表于 10-29 07:39

    Labview任意精度函数库(高精度函数库

    自己编写了一系列Labview任意精度函数库,现在提供给大家免费使用。任意精度的意思是能以任何精度计算。为了加快计算速度,部分函数目前限制1000位十进制计算,如果需要更高精度可另外联系作者。由于
    发表于 12-27 16:35

    ZNetAdv配置函数库数据手册

    ZNetAdv 配置函数库是支持致远电子有限公司以太网产品的通用配置函数库,此函数库 支 持 的 以 太 网 产 品 有 : ZNE-100, ZNE-100T, ZNE-100TI
    发表于 10-31 07:54

    32位基于ARM微控制器STM32F101xx与STM32F103xx的固件函数库

    2004 标准(根据需要,我们可以提供兼容矩阵)。由于整个固态函数库按照“Strict ANSI-C标准编写,它不受不同开发环境的影响。仅对话启动文件取决于开发环境。该固态
    发表于 09-28 08:05

    怎么封装函数库

    怎么封装函数库,只留一些回调函数和引脚定义,完整程序不让人看
    发表于 11-08 08:12

    Linux C 函数库中文教程

    Linux C 函数库中文手册 cosh       原型:extern float cosh(float x);    用法:#include     功能:求x的双曲余
    发表于 03-10 14:09 36次下载