004_C标准库函数之<stdlib.h>

在头文件<stdlib.h>中说明了用于数值转换内存分配以及具有其他相似任务的函数。

数值转换:字符串和字符串之间的转换

内存分支:主要说的是动态内存的申请使用等

【函数1: atof】


【格式】

double atof(const char *str);

【功能】

把字符串str转换成double类型数据,只对数字字符有效。等价于:strtod(str, (char**)NULL)

【入参】

const char *str:常量字符串

【返回值】

double类型的数据,带有后六位小数

【test_code】

输出结果:

【总结】

通过验证可以看出atof函数运行时的一些特征:

1.只对数字参数有效

2.从字符串头开始,若遇到数字则转,一旦遇到其他字符:字母,符号,下划线,小数点等,会立马退出

3.字符串开头若不是数字,则立马退出并打印数值为0.000000

【函数2: atoi】


【格式】

int atoi(const char *str);

【功能】

把字符串str转换成int类型。等价于:(int)strtol(str, (char**)NULL, 10)

【入参】

const char *str:常量字符串

【返回值】

int类型的数据

【test_code】

输出结果:

【总结】

通过验证可以看出atoi函数运行时的一些特征:

1.只对数字参数有效

2.从字符串头开始,若遇到数字则转,一旦遇到其他字符:字母,符号,下划线,小数点等,会立马退出

3.字符串开头若不是数字,则立马退出并打印数值为0

【函数3: atol】


【格式】

long atol(const char *str);

【功能】

把字符串str转换成long类型。等价于:strtol(str, (char**)NULL, 10)。

【入参】

const char *str:常量字符串

【返回值】

long类型的数据

【test_code】

输出结果:

【总结】

通过验证可以看出atol函数运行时的一些特征:

1.只对数字参数有效

2.从字符串头开始,若遇到数字则转,一旦遇到其他字符:字母,符号,下划线,小数点等,会立马退出

3.字符串开头若不是数字,则立马退出并打印数值为0

【函数4: strtod】


【格式】

double strtod(const char *start, char **end);

【功能】

把字符串start的前缀转换成double类型。在转换中跳过start的前导空白符,然后逐个读入构成数的字符,任何非浮点数成分的字符都会终止上述过程。如果end不为NULL,则把未转换部分的指针保存在*end中。

如果结果上溢,返回带有适当符号的HUGE_VAL,如果结果下溢,那么函数返回0。在这两种情况下,errno均被置为ERANGE。

【入参】

const char *start:常量字符串(常量指针)

char **end:二级字符指针,用来存储未被转换的部分,以备使用,若不用,则可用NULL代替

【返回值】

double类型的数据

【test_code】

输出结果:

【总结】

通过验证可以看出strtod函数运行时的一些特征:

1.只对数字参数有效

2.从字符串头开始,若遇到数字则转,一旦遇到其他字符:字母,符号,下划线,小数点等,会立马退出,会自动跳过前导空白

3.字符串开头若不是数字,则立马退出并打印数值为0

4.二级指针参数可以入参为NULL,验证如下:

【函数5: strtol】


【格式】

long int strtol(const char *start, char **end, int radix);

【功能】

把字符串start的前缀转换成long类型,在转换中跳过start的前导空白符。如果end不为NULL,则把未转换部分的指针保存在*end中。

如果radix的值在2到36间之间,那么转换按该基数进行;如果radix为0,则基数为八进制、十进制、十六进制,以0为前导的是八进制,以0x或0X为前导的是十六进制。无论在哪种情况下,串中的字母是表示10到radix-1之间数字的字母。如果radix是16,可以加上前导0x或0X。

如果结果上溢,则依据结果的符号返回LONG_MAX或LONG_MIN,置errno为ERANGE。

【入参】

const char *str:常量字符串

char **end:二级字符指针,用来存储未被转换的部分,以备使用,若不用,则可用NULL代替

radix:简单理解就是转换规则,要以什么规则把*str转为你需要的数据

【返回值】

long int类型的数据

【test_code】

输出结果:

打印1:因为是十进制转换输出,所以输出结果为123,也就是123.8hwe = 123

打印2:str被转换后剩余的部分,也就是.8hwe,存储在work中

打印3:因为是十进制转换输出,所以输出结果为576,也就是576wowo123 = 576

打印4:因为是按十六进制转换输出,所以输出结果为8295,也就是0x2067 = 8295

打印5:因为是十进制转换输出,所以输出结果为0,也就是..2074.967 = 0

打印6:因为是八进制,所以输出结果为1253,也就是2345= 1253

注意:比如打印4:

temp = strtol(str2, work, 16);

LOGLD(temp);

此时,str2 = 2067,上面函数的意思是:把2067按照十六进制计算,然后打印出来,LOGLD其实是#define LOGLD(h)  printf("%ld\n", h) 也就是说我打印的时候是按照十进制去打印的哦,所以打印出来便是8295,其他进制数打印原理类推

【总结】

通过验证可以看出strtol函数运行时的一些特征:

1.只对数字参数有效

2.从字符串头开始,若遇到数字则转,一旦遇到其他字符:字母,符号,下划线,小数点等,会立马退出,会自动跳过前导空白

3.字符串开头若不是数字,则立马退出并打印数值为0

4.关于radix的理解至关重要,范围是2-36,二进制 2 八进制 8 十进制 10 十六进制 16当然还有其他取值和用法,这里我就不再深究,后面 深入剖析C函数 的时候,可能会再深究,关于其他用法,还需理解深刻,便可体会之奥妙

5.二级指针参数可以入参为NULL,验证如下:

【函数6: strtoul】


【格式】

unsigned long int strtoul(const char *start, char **end, int radix);

【功能】

与strtol()类似,只是结果为unsigned long类型,溢出时值为ULONG_MAX

【入参】

const char *str:常量字符串

char **end:二级字符指针,用来存储未被转换的部分,以备使用,若不用,则可用NULL代替

radix:简单理解就是转换规则,要以什么规则把*str转为你需要的数据

【返回值】

unsigned long int类型的数据

【test_code】

输出结果:

【总结】

通过验证可以看出strtol函数运行时的一些特征:

1.只对数字参数有效

2.从字符串头开始,若遇到数字则转,一旦遇到其他字符:字母,符号,下划线,小数点等,会立马退出,会自动跳过前导空白

3.字符串开头若不是数字,则立马退出并打印数值为0

4.关于radix的理解至关重要,范围是2-36,二进制 2 八进制 8 十进制 10 十六进制 16当然还有其他取值和用法,这里我就不再深究,后面 深入剖析C函数 的时候,可能会再深究,关于其他用法,还需理解深刻,便可体会之奥妙

5.二级指针参数可以入参为NULL,验证如下:

【函数7: rand】


【格式】

int rand(void);

【功能】

产生一个0到RAND_MAX之间的伪随机整数。RAND_MAX值至少为32767

【入参】

void: 入参为空

【返回值】

int类型的数据

【test_code】

输出结果:

【总结】

1、rand()不需要入参,它返回一个从0到最大随机数的任意整数,最大随机数的大小通常是固定的一个大整数。

2、如果你要产生0~99中的一个随机数,可以写为:int num = rand() % 100; 那么,num的值就是0~99中的一个随机数了。

3、如果要产生1~100,则:int num = rand() % 100 + 1;

4、总结下,可以写成:int num = rand() % n +a;
a是起始值,n-1+a是终止值,n是整数的范围。

5、一般:rand() % (b-a+1)+ a ; 就表示 a~b 之间的一个随机整数。

6、若要产生0-1之间的小数,则可以先取得0-10的整数,然后均除以10即可得到“随机到十分位”的10个随机小数。

若要得到“随机到百分位”的随机小数,则需要先得到0~100的10个整数,然后均除以100,其它情况依 此类推

7.注意rand产生的是伪随机数,并不是真正的随机数,这个有兴趣的朋友可以了解下,在加密中会有解释

【函数8: srand】


【格式】

void srand(unsigned int seed);

【功能】

设置新的伪随机数序列的种子为seed。种子的初值为1,主要作用就是使rand出来的随机值每次都不一样

【入参】

unsigned int seed :种子值

【返回值】

无返回数据

【test_code】

输出结果:

注意:每一次执行后,随机值都不一样哦,

若不加srand随机值种子的话,每次执行都是一样的,如下:

输出结果:

【总结】

若要生成每次变化的随机值,就要使用srand函数进行“播随机种子”操作哦,这个例子中我们用到了time(NULL)函数,这个在后面的time.h中会讲到,就先简单理解就是给出随机时间作为种子

【函数9: calloc】


【格式】

void *calloc(size_t num, size_t size);

【功能】

为num个大小为size的对象组成的数组分配足够的内存,并返回指向所分配区域的第一个字节的指针;如果内存不足以满足要求,则返回NULL。

分配的内存区域中的所有位被初始化为0

【入参】

size_t num:需要申请动态内存的个数

size_t size:单位内存的大小,一般是sizeof(int)或者sizeof(char)等,用sizeof算出类型数据的单位内存是多少

【返回值】

void*类型的数据,指向所分配区域的第一个字节的指针

【test_code】

输出结果:

【总结】

1.calloc在申请num个单位内存后,会自动的初始化为0,所以这个避免了乱码

2.注意返回值是void*类型,理论上用什么类型接都行,但是建议接的时候,尽量用的指针类型和申请内存的类型是一致的哦,不然后面子在操作使用时,会出现移位不对称,导致数据乱码哦(这个应该不牵扯的,还待验证探究)

3.也可以像下面这样用:

建议最好是像下面这样用法,比较标准清晰(代码是写出来让别人读的,所以最好清晰明了,也算是一个良好习惯的养成吧):

4.最后一点,也是最重要的一点,因为这个申请下来的是动态能存(堆内存),不会随着函数的结束而释放掉(随着函数结束而释放掉的是栈内存),所以申请使用完之后,一定要记住释放内存,free下

【函数10: malloc】


【格式】

void *malloc(size_t size);

【功能】

为大小为size的对象分配足够的内存,并返回指向所分配区域的第一个字节的指针;如果内存不足以满足要求,则返回NULL。

不对分配的内存区域进行初始化

【入参】

size_t size:需要申请动态内存的个数(大小)

【返回值】

void*类型的数据,指向所分配区域的第一个字节的指针

【test_code】

输出结果:

【总结】

1.calloc在申请num个单位内存后,会自动的初始化为0,所以这个避免了乱码

2.注意返回值是void*类型,理论上用什么类型接都行,但是建议接的时候,尽量用的指针类型和申请内存的类型是一致的哦,不然后面子在操作使用时,会出现移位不对称,导致数据乱码哦(这个应该不牵扯的,如上验证,也可以用char*的接,这个malloc应该不影响,他的入参只要申请的大小,并没有检查单位数据类型)

3.最后一点,也是最重要的一点,因为这个申请下来的是动态能存(堆内存),不会随着函数的结束而释放掉(随着函数结束而释放掉的是栈内存),所以申请使用完之后,一定要记住释放内存,free下

【函数11: realloc】


【格式】

void *realloc(void *ptr, size_t size);

【功能】

将ptr指向的内存区域的大小改为size个字节。如果新分配的内存比原内存大,那么原内存的内容保持不变,增加的空间不进行初始化。如果新分配的内存比原内存小,那么新内存保持原内存区中前size字节的内容。函数返回指向新分配空间的指针。如果不能满足要求,则返回NULL,原ptr指向的内存区域保持不变。

如果ptr为NULL,则行为等价于malloc(size)。

如果size为0,则行为等价于free(ptr)

【入参】

void *ptr:指向原来的动态内存的指针(现在要改变大小的那个动态内存的指针)

size_t size:准备再次增加(减少)申请的动态内存的大小

【返回值】

void*类型的数据,指向所分配区域的第一个字节的指针

【test_code】

验证1:增加内存长度

输出结果:

可以看出,新申请的5个单位的内存,内容并未初始化,是有乱码的哦,而且ptr的动态内存比之前增加了5个单位的长度

验证2:减少内存长度

输出结果:

不知道为何tp[4]和tp[5]的输出结果却为3和4,这里不清理解,还需要再深究下,哪个大神可以帮忙解释下?3Q

【总结】

1.realloc在申请num个单位内存后,会保留原来的内存内容并把新增加的内存长度添加上去,但是并不会自动的对增加的内存初始化为0

2.关于缩减内存长度这块儿,感觉好像没生效,没有搞的很清楚

3.最后一点,也是最重要的一点,因为这个申请下来的是动态能存(堆内存),不会随着函数的结束而释放掉(随着函数结束而释放掉的是栈内存),所以申请使用完之后,一定要记住释放内存,free下

【函数12: free】


【格式】

void free(void *ptr);

【功能】

释放ptr指向的内存空间,若ptr为NULL,则什么也不做。ptr必须指向先前用动态分配函数malloc、realloc或calloc分配的空间

【入参】

void *ptr:指针名

【返回值】

void类型的数据(也就是空)

【test_code】

输出结果:

可以看到,释放sptr之后,再打印,就是空了,所以这个sptr的内容及内存就被释放掉了,一般情况下,释放后,建议赋值为NULL,这样就不容易成为野指针

【总结】

1.free释放内存,只是针对动态内存(calloc,malloc,realloc申请的),也就是只是针对堆内存而言,栈内存一般都是函数运行结束后,系统会自动释放,所以一定要注意场景

2.free完后,建议给赋值为NULL,这样就尽可能的防止了出现使用野指针的情况,因为我们代码一般使用指针的时候,都会进行判空操作,我们释放掉,再赋值NULL,这样就不容易被作为野指针使用,当然了,这只是防止野指针的一种情况,关于野指针,后面我们有专题探讨。

3.形成一条固定记忆:但凡使用calloc,malloc,realloc申请的内存,在使用完了之后,一定要用free释放掉,不然就会形成内存泄漏问题!

【函数13: abort】


【格式】

void abort(void);

【功能】

使程序非正常终止。其功能类似于raise(SIGABRT)。

【入参】

void:空参,也就是不需要入参

【返回值】

void类型的空数据,也可以简单的理解为没有数据返回

【test_code】

输出结果:

【总结】

这个类似于断言的功能一样,可能主要用于debug吧,具体用法场景还不是很清楚,有知道的大神希望指点一二,3Q

【函数14: exit】


【格式】

void exit(int status);

【功能】

使程序正常终止。status的值如何被返回依具体的实现而定,但用0表示正常终止,也可用值EXIT_SUCCESS和EXIT_FAILURE

【入参】

status: 状态值

【返回值】

void类型的数据

【test_code】

输出结果:

【总结】

在使用exit的时候,入惨status最好用成对应的枚举值,enum,这样,在找问题的时候就很方便和快捷高效,而不是出现魔鬼数字,让人猜

【函数15: atexit】


【格式】

int atexit(void (*func)(void));

【功能】

注册在程序正常终止时所要调用的函数func。如果成功注册,则函数返回0值,否则返回非0值。atexit函数以与注册相反的顺序被调用,所有打开的文件被刷新,所有打开的流被关闭。

【入参】

void (*func)(void):函数指针,func指向对应的函数便可,为了后面调用

【返回值】

int类型的数据

【test_code】

输出结果:

【总结】

1.注意函数指针的定义及定义实体对象,如下:

typedef void(*func) ();//定义一个函数指针

func funcptr;//定义实体

funcptr = fun;//赋值函数地址,等待被调用

atexit(funcptr);//调用函数指针

2.也可以这样搞,如下:

void (*funcptr) ();

funcptr = fun;//赋值函数地址,等待被调用

atexit(funcptr);//调用函数指针

3.atexit调用函数,执行的顺序是从后往前,就是先注册的后调用运行,后注册的先调用运行

【函数16: system】


【格式】

int system(const char *str);

【功能】

把字符串str传送给执行环境。如果str为NULL,那么在存在命令处理程序时,返回0值。如果str的值非NULL,则返回值与具体的实现有关。

【入参】

const char *str:常量字符串

【返回值】

int类型的数据

【test_code】

输出结果:

【总结】

1.system(str),这个入参其实就是一条指令,根据系统的不同而不同,比如图中在linux中,入参1是ipconfig,就not found;入参为ls,便直接执行这个指令了

2.这个应该是系统调用,具体和Linux内部系统调用的区别,还待考究

【函数17: getenv】


【格式】

char *getenv(const char *name);

【功能】

返回与name相关的环境字符串。如果该字符串不存在,则返回NULL。其细节与具体的实现有关。

【入参】

const char *name:常量字符串

【返回值】

char* 类型的数据

【test_code】

输出结果:

【总结】

1.这个函数使用的话,首先要知道env变量都有哪些,linux系统的话,直接env指令,就会出现env变量,window的话,需要去在配置里查看

2.将得到的env变量的路径值返回出去,可以接,也可以直接打印出来,都行,看具体需要而定

【函数18: bsearch】


【格式】

void *bsearch(const void *key, const void *base, size_t n, size_t size, int (*compare)(const void *, const void *));

(建议先看下一个qsort函数,再回头来看这个bsearch函数)

【功能】

在base[0]...base[n-1]之间查找与*key匹配的项。size指出每个元素占有的字节数。函数返回一个指向匹配项的指针,若不存在匹配则返回NULL。

函数指针compare指向的函数把关键字key和数组元素比较,比较函数的形式为:

int func_name(const void *arg1, const void *arg2);

arg1是key指针,arg2是数组元素指针。

返回值必须如下:

  • arg1 < arg2时,返回值<0;
  • arg1 == arg2时,返回值==0;
  • arg1 > arg2时,返回值>0。

数组base必须按升序排列(与compare函数定义的大小次序一致)。

【入参】

const void *key:要查找的项的内容

void *base:一般是个数组,int base[];或者char *base[]; float base[]均可,根据需要而定,结构数组也行

size_t n:这个表示base数组的长度,计算方法:n = sizeof(base)/sizeof(base[0]);

size_t size:这个表示单个元素的大小,计算方法:sizeof(base[0]);

int (*compare)(const void *, const void *):这个是函数指针,也是我们实现的重点,我们根据需要实现此函数为cmp_zll,在调用的时候直接将函数名调用便可,关于函数指针和指针函数,之前有写过文章已经说过,这里不在赘述。

【返回值】

void* 类型的数据

【test_code】

输出结果:

输出结果:

【总结】

1.次函数入参比较多,建议先把qsort搞熟悉,再来看这个,就比较容易了

2.记得在使用bsearch之前,一定要用qsort给base把序排了,而且只能是升序,不能搞成降序哦

3.这个就是二分查找,具体深入探索在后面会再说

【函数19: qsort】


【格式】

void qsort(void *base, size_t n, size_t size, int (*compare)(const void *, const void *));

【功能】

对由n个大小为size的对象构成的数组base进行升序排序。

比较函数compare的形式如下:

int func_name(const void *arg1, const voie *arg2);

其返回值必须如下所示:

  • • arg1 < arg2,返回值<0;
  • • arg1 == arg2,返回值==0;
  • • arg1 > arg2,返回值>0。

【入参】

void *base:一般是个数组,int base[];或者char *base[]; float base[]均可,根据需要而定,结构数组也行

size_t n:这个表示base数组的长度,计算方法:n = sizeof(base)/sizeof(base[0]);

size_t size:这个表示单个元素的大小,计算方法:sizeof(base[0]);

int (*compare)(const void *, const void *):这个是函数指针,也是我们实现的重点,我们根据需要实现此函数为cmp_zll,在调用的时候直接将函数名调用便可

【返回值】

void类型

【test_code】

输出结果:

(注意为升序排序结果)

输出结果:

【总结】

1.qsort函数默认是按照升序排列的

2.重点是实现函数指针compare

比较函数compare的形式如下:

int func_name(const void *arg1, const voie *arg2);

实现完毕后,直接从qsort调用便可

【函数20: abs】


【格式】

int abs(int num);

【功能】

返回int变量num的绝对值。

【入参】

int num:整形变量,可正可负

【返回值】

int类型的数据

【test_code】

输出结果:

【总结】

1.abs函数就是一个取绝对值的函数

2.入参可以为单个数字,也可以为表达式,或者三目计算符,只要最后给出的是一个整形数据就行,不牵扯过程

【函数21: labs】


【格式】

long labs(long int num);

【功能】

返回long类型变量num的绝对值。

【入参】

long int num:长整形参数

【返回值】

long类型的数据

【test_code】

输出结果:

【总结】

1.labs函数就是一个取绝对值的函数

2.入参可以为单个数字,也可以为表达式,或者三目计算符,只要最后给出的是一个长整形数据就行,不牵扯过程

3.针对long int的数据打印,我查了下,网上众说纷纭,有说用%li格式打印的,有说%ld格式打印的,我试了下,这两个的话,都报warning,我用%d调试,没有报warning,所以这个还是根据自己的编译器看吧,只要结果不出差错,应该是都可以接收的

【函数22: div】


【格式】

div_t div(int numerator, int denominator);

【功能】

返回numerator/denominator的商和余数,结果分别保存在结构类型div_t的两个int成员quot和rem中。

【入参】

int numerator:入参为被除数(也就是分子)

int denominator:入参为除数(也就是分母)

【返回值】

div_t类型的数据

【test_code】

输出结果:

【总结】

1.先看解析

可以看出,div_t就是一个结构体,就是两个成员:quot和rem,前者存商值,后者存余值

2.这个div函数具体的使用情况,大家可以思考下

【函数23: ldiv】


【格式】

ldiv_t div(long int numerator, long int denominator);

【功能】

返回numerator/denominator的商和余数,结果分别保存在结构类型ldiv_t的两个long成员quot和rem中。

【入参】

long int numerator:入参为被除数(也就是分子)

long int denominator:入参为除数(也就是分母)

【返回值】

ldiv_t类型的数据

【test_code】

输出结果:

【总结】

参考div函数的总结,这个ldiv就是数据类型变为long int

  • 26
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值