第十四章 标准函数库
算术<stdlib.h>
int abs(int value); //求绝对值
long int labs(long int value); //对长整型求绝对值
div_t div(int denominator, int numerator);
第1个参数(分母)除以第2个参数(分子),产生商与余数。返回的结果是一个定义的结构体
typedef struct {
int quot/*商*/, rem/*余数*/;
} div_t;
quot = numerator/denominator
rem = numerator%denominator
ldiv_t ldiv(long int denominator, long int numerator);
typedef struct {
long quot, rem;
} ldiv_t;
与div功能一样,适用于长整型。
这两个除法函数中,当denominator为0或者当商或余数无法用返回类型表示时,这些函数的行为是未定义的(不同的编译器不同,不一定导致定义域错误)
随机数<stdlib.h>
int rand(void);
返回一个范围在0到RAND_MAX(至少为32768)之间的随机数,为了得到一个更小范围的随机数,产先把这个函数的返回值根据所需范围的大小进行取模,然后通过加上或减去一个偏移量对它进行调整。
void srand(unsigned int seed);
为了避免程序每次运行时获得相同的随机数,可以调用srand初始化随机数生成器。种子一般使用时间:srand((unsigned int)tim(0))
如果种子相同,则每次(不是指同一运行过程中多次调用,而是指定程序运行结束后再次运行)程序运行产生的随机数序列是一样的。如果未使用srand就调用了rand,则默认会使用srand(1)先作初始化操作。
字符串转换整数<stdlib.h>
字符串转换函数将字符串转换成数值。这些函数的第一个参数如果包含了前导空白字符,它们都将被忽略,然后函数把合法字符转换为指定类型的值。如果存在任何非法缀尾字符,它们也将被忽略。
int atoi(char const *string);
long int atol(char const *string);
上面两个函数将以10为基数进行转换,下面两个可以指定基数。
long int strtol(char const *string, char **unused, int base);
unsigned long int strtoul(char const *string, char **unused, int base);
unused参数是一个二级指针,它指向了第一个未被转换的字符指针,也以为NULL,如果为NULL则不会存储未被成功转换的缀尾非法串。
如果基数base为0,则数值的格式可以是十进制常量、八进制常量或十六进制常量,具体是什么是根据它的格式推断出来的。除基数可以为0外,还可以是2到36(包含两者),字母a~z(A~Z)分别表示10~35的数字。一种特殊情况,如果基数为16,则数值可以以0x或0X开始(在符号位之后),这个前缀将被忽略。
如果这些函数的string参数并不是一个合法的数值串,函数就返回0。如果被转换的值无法表示,则会设置errno为ERANGE值,并返回以下值:
strtol:如果值太大且为负数,返回LONG_MIN,如果太大且为正数,返回LONG_MAX
strtoul:如果值太大,返回ULONG_MAX
浮点型函数
头文件math.h包含了函数库stdlib.h中剩余的数学函数的声明,这些函数返回值以及绝大多数都是double类型。
如果一个函数的参数不在函数定义域之内,称为定义域错误(domain error),例如:
sqrt(-5.0);
perror("sqrt");//sqrt: Domain error
当出现一个定义域错误时,函数返回一个由编译器定义的错误值,并且在errno中存储EDOM这个值。
如果一个函数的结果值过大或过小,无法用double类型表示,这称为范围错误(range error),例如:
printf("%f\n",exp(LONG_LONG_MAX));//1.#INF00
perror("exp");//exp: Result too large
由于结果值太大,函数返回 HUGE_VAL,它是一个在math.h中定义的double类型值。如果一个函数的结果值太小,无法用一个double表示,函数将返回0,这种情况也属于范围错误,但errno会不会设置为ERANGE取决于编译器;
对数和指数函数<math.h>
double exp(double x);
返回e值的x次幂,也就是ex
double log(double x);
返回x以e为底的对数,即自然对数。logex
double log10(double x);
返回x以10为底的对数。log10x
如果它们的参数为负,两个对数函数都将出现定义域错误。
x以任意一个b为底的对数可以通过下面的公式进行计算:
logbx = logex/logeb
log39 = loge9/loge3 = log(9)/log(3) = 2
printf("%f\n", log(9)/log(3));//2.000000
幂<math.h>
double pow(double x, double y);
返回xy
double sqrt(double x);
底数、顶数、绝对值、余数<math.h>
double floor(double x);
返回不大于其参数的最大整数值,这个值以double的形式返回,是因为double能够表示的范围远大于int。
double ceil(double x);
返回不小于其参数的最小整数值。
double fabs(double x);
返回绝对值
double fmod(double x, double y);
返回x除以y所产生的余数,由于操作符 % 只能用在两个整数之间,所以提供了这个函数
字符串转换浮点数<math.h>
这些函数和整数字符串转换函数类似,只不守它们返回浮点值
double atof(char const *string);
double strtod(char const *string, char **unused);
日期和时间函数
处理器时间<time.h>
clock_t clock(void);
返回从程序开始执行起处理器所消耗的时间,这个值可能是个近似值。如果需要更精确的值,你可以在main函数刚开始执行时调用clock,然后把以后调用clock时所返回的值减去前面的那个值。如果无法提供处理器时间,或太大无法用clock_t表示,则返回-1。
clock函数返回一个数字,它是由编译器定义的,通常它是处理器时钟滴答的次数,为了把这个值转换为秒,你应该把它除以常量 CLOCKS_PER_SEC
typedef long clock_t;
#define CLOCKS_PER_SEC ((clock_t)1000)
当天时间<time.h>
time_t time(time_t *returned_vale);
如果参数是一个非NULL的指针,时间值也将通过这个指针进行存储。如果机器无法提供当前日期与时间,或时间值太大,无法用time_t表示,则返回-1。正常一般返回的是从1970.1.1 00:00:00开始到今天的秒数(注,不像Java中返回的是毫秒数,如果time_t是一个long型,且结果是以秒为单位的,这个值会在2038年溢出)。注:标准并未规定函数的返回结果用秒来表示,不过从大多数的实现来看是秒。
typedef __time32_t time_t;
typedef __int32 __time32_t;
#define __int32 long
日期和时间转换<time.h>
char *ctime(time_t const *time_value);
返回的格式固定为:Fri Apr 08 17:52:04 2011\n\0
用来存储日期字符串的内存类型,许多编译器使用一个静态数组,因此,下一次调用ctime时,这个 字符串将被覆盖,从下面的测试 s1 与 s2 就可以很明显的看出来。
time_t *p;
time_t t = time(p);
printf("%d\n", *p);//1302256323
printf("%d\n", t);//1302256323
char *s1 = ctime(&t);
printf("%s", s1);//Fri Apr 08 17:34:01 2011
*p = *p + 1;//注,t的值并没有被修改
printf("%d\n", *p);//1302256324
printf("%d\n", t);//1302256323
char *s2 = ctime(p);
printf("%s", s2);//Fri Apr 08 17:52:04 2011
//s1的值也被修改了,则说明s1与s2指向同一静态存储空间
printf("%s", s1);//Fri Apr 08 17:52:04 2011
ctime很可能是以以下这种方式实现的:
asctime(localtime(time_value));
double difftime(time_t time1, time_t time2);
计算 time1 – time2 的差,并把结果值转换为秒,注意返回的是一个double类型。
下面两个函数把一个time_t值转换为一个tm结构,这样我们就很方便地访问日期和时间的各个部分了。gmtime将时间值转换为UTC(格林尼汉标准时间),localtime函数把一个时间值转换为当地时间。标准包含了这两个函数,但它并没有描述UTC和当地时间的实现之间的关系。
struct tm *gmtime(time_t const * time_value);
struct tm *localtime(time_t const *time_value);
struct tm {
int tm_sec; /* Seconds: 0-59 (K&R says 0-61?用于闰秒) */
int tm_min; /* Minutes: 0-59 */
int tm_hour; /* Hours since midnight: 0-23 */
int tm_mday; /* Day of the month: 1-31 */
*/
*/
int tm_wday; /* Days since Sunday (0-6) */
int tm_yday; /* Days since Jan. 1: 0-365 */
int tm_isdst; /* +1 Daylight Savings Time, 0 No DST,
* -1 don't know 夏令时标志*/
};
示例:当前当地日间为2011-13-12 10:29:52
time_t ti = time(NULL);
struct tm *t = gmtime(&ti);
printf("gmtime : %d-%d-%d %d:%d:%d\n", t->tm_year + 1900, t->tm_mday + 1,
t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
t = localtime(&ti);
printf("localtime : %d-%d-%d %d:%d:%d", t->tm_year + 1900, t->tm_mday + 1,
t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
输出:
gmtime : 2011-13-12 2:29:52
localtime : 2011-13-12 10:29:52
你可以将上面的tm结构作为参数传递给下面的函数之一:
char *asctime(struct tm const *tm_ptr);
size_t strftime(char *string, size_t maxsize, char const *format,
struct tm const *tm_ptr);
asctime返回的格式与ctime是一样的,ctime很可能是调用了asctime来实现自己的功能:
time_t *p;
time_t t = time(p);
//Tue Apr 12 17:47:48 2011
printf("%s", ctime(&t));
//Tue Apr 12 17:47:48 2011
printf("%s", asctime(localtime(&t)));
strftime函数把一个tm结构转换为一个根据某个格式字符串而定的字符串。如果转换结果字符串的长度小于maxsize参数,该字符串就被复制到第1个参数所指向的数组中,并返回字符串的长度,否则,函数返回-1且数组的内容是未定义的。
格式代码 |
输出 |
%% |
%字符 |
%a |
星期中的某天,星期几的简写表示 |
%A |
星期中的某天,星期几的全写表示 |
%b |
月份,月份名的简写 |
%B |
月份,月份名的全写 |
%c |
日期和时间,等效于“%x %X” |
%x |
日期,使用本地的日期格式 |
%X |
时间,使用本地的时间格式 |
%d |
一个月的第几天(01-31) |
%H |
小时,以24小时格式(00-23) |
%I |
小时,以12小时格式(00-12) |
%J |
一年的第几天(001-366) |
%m |
月数(01-12) |
%M |
分钟(00-59) |
%P |
AM或PM |
%S |
秒(00-61) |
%U |
一年的第几星期(00-53),以星期日为第1天 |
%W |
一年的第几星期(00-53),以星期一为第1天 |
%w |
一星期的第几天,星期日为第0天 |
%y |
当前世纪的年份(00-99) |
%Y |
年份的全写形式(例如,1984) |
%Z |
时区简写 |
time_t t = time(NULL);
struct tm *tm = localtime(&t);
char s[20];
//第二个参数只是指明数组的长度,防止数组越界
strftime(s, 20, "%Y-%m-%d %H:%M:%S", tm);
printf("%s\n", s);//2011-04-12 18:20:21
strftime(s, 20, "%c", tm);
printf("%s\n", s);//04/12/11 18:20:21
strftime(s, 20, "%x", tm);
printf("%s\n", s);//04/12/11
strftime(s, 20, "%X", tm);
printf("%s\n", s);//18:20:21
time_t mktime(struct tm *tm_ptr)
将tm结构转换为一个time_t值。
非本地跳转<setjmp.h>
setjmp和longjmp函数提供了一种类似goto的机制,它能从函数调用链的最底端直接跳转到最上层函数(调用setjmp的函数),不必向调用链中的每个中间层函数返回一个错误标志。
int setjmp(jmp_buf state);
void longjmp(jump_buf state, int value);
setjmp函数的jmp_buf参数用来存储初始化程序的状态信息(堆栈指针的当前位置和程序的计数器),第一次调用setjmp函数时返回0,调用setjmp所处理的函数便成为了“顶层”函数。以后在顶层函数或其他任何它所调用的函数(不论是直接还是间接调用)内的任何调用longjmp函数,将导致这个被保存的状态重新恢复(调用setjmp的语句会重新执行一次)。setjmp第一次调用时,即初始化时固定返回为0,第二次调用返回时,返回值为longjmp函数的第二个参数,它必须是一个非0。通过检查setjmp函数的返回值,程序可以判断是否调用了longjmp,还可以判断是由哪个longjmp返回的(根据第二个参数值)。
下面程序用来检测get_trans、process_trans或其他任何被这个函数直接调用的或间接调用的函数,如果调用了
longjmp(restart, 1)
类似的语句,程序则会直接从调用longjmp的函数直接返回到最上层函数(这里是调用setjmp函数的main函数),而不会像Java里的异常那样一层层向上抛出异常,最后从第一次setjmp保存的程序状态点开始重新执行(第一次调用setjmp函数的下一条语句)。
/*
** 事务结构的简单声明,以及处理事务的函数原型声明
*/
typedef struct {
int a;
} Trans;
Trans *get_trans(void);
void process_trans(Trans *);
void write_data_to_file(void);
----trans.h
#include "trans.h"
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
/*
** 用于存储setjmp的状态信息变量.
*/
jmp_buf restart;
int main() {
int value;
Trans *transaction;
/*
** 创建一个在longjmp函数调用之后程序恢复执行的地点,
** 每次从这条语句开始恢复执行
*/
value = setjmp( restart );
/*
** 判断是否是从longjmp返回的,如果是,还可以判断是从
** 哪个longjmp返回的
*/
switch (value) {
default:
/*
** longjmp 被调用 -- 致命错误
*/
fputs("Fatal error.\n", stderr);
break;
case 1:
/*
** longjmp 被调用 -- 小错误
*/
fputs("Invalid transaction.\n", stderr);
/* 错误处理后继续执行 */
case 0:
/*
** setjmp初始返回点: 执行正常处理,如果get_trans
** 、process_trans中调用了longjmp,则重新返回到
** 前面setjmp调用处并重新执行
*/
while ((transaction = get_trans()) != NULL)
process_trans(transaction);
}
/*
** Save data and exit the program
*/
write_data_to_file();
return value == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
信号<signal.h>
信号表示一种事件,这种事件即可来自于程序外部,也可来自程序本身。
标准所定义的信号:
来源 |
信号 |
含义 |
程序内部(使用相同的数组每次运行必然发生) |
SIGABRT |
程序请求异常终止,由abort函数所引发的信号 |
SIGFPE |
发生一个算术错误 |
|
SIGILL |
检测到非法指令 |
|
SIGSEGV |
检测到对内存的非法访问 |
|
程序外部 |
SIGINT |
收到一个交互性注意信号,用户试图中断程序时发生,可以与程序交互 |
SIGERM |
收到一个终止程序的请求,不配备信号处理函数 |
int raise(int sig);
显示地引发一个信号,你可以调用这个函数对信号处理函数进行测试。
当一个信号发生时,程序可以使用三种方式对它作用反应。缺省反应是由编译器定义的,通常是终止程;第二种是可以忽略信号;第三种程序可以设置一个信号处理函数,当信号发生时调用这个函数,signal函数就是用于指定程序希望采取的反应。
void (* signal(int sig, void(*handler)(int)))(int);
先看 signal(int sig, void(*handler)(int)):
第一个参数为上表中的信号之一,第二个参数信号处理函数,这个处理函数是一个函数指针,它所指向的函数接受一个整形参数且没有返回值,当信号发生时,信号sig参数值会通过handler函数指针的参数传递给信号处理函数,这个处理函数则可以根据它的参数来处理不同的信号。
signal首先是一个函数(注意,不是函数指针,请参数函数指针章节),但它返回一个函数指针,这个返回的函数指针指向了一个没有返回值但带一个整型参数的函数,这个值就是在我们设置信号处理函数前原先所设置原信号处理函数,这样我们就可以拿到原先的函数处理器,并可以恢复成原先以前的处理函数。如果这个函数调用失败,则返回SIG_ERR值。
signal.h头文件还定义了两个宏,SIG_DFL和SIG_IGN,它们可以作为signal函数的第2个参数。SIG_DFL为编译器对该信号的缺省反应,SIG_IGN表示信号将被忽略。
#define SIG_DFL ((__p_sig_fn_t) 0)
#define SIG_IGN ((__p_sig_fn_t) 1)
#define SIG_ERR ((__p_sig_fn_t) -1)
typedef void (*__p_sig_fn_t)(int);
打印可变参数<signal.h>
int vprintf(char const * format, va_list arg);
int vfprintf(FILE *stream, char const * format, va_list arg);
int vsprintf(char *buffer, char const * format, va_list arg);
arg参数必须使用va_start进行初始化,这些函数都不需要调用va_end:
void func(int first, ...) {
va_list arg;
va_start(arg,first);
vprintf("%c %d\n", arg);//a 10
vfprintf(stdout, "%c %d\n", arg);//a 10
char buffer[10];
vsprintf(buffer, "%c %d\n", arg);
printf(buffer);//a 10
}
int main() {
func(0, 'a', 10);
}
执行环境
终止执行<stdlib.h>
void abort(void);
用于不正常终止一个正在运行的程序,它将引发SIGABRT信号
void atexit(void( func)(void));
可以把一些函数注册为退出函数。当程序将要正常终止时(或者调用了exit,或者由于main函数返回),退出函数将被调用。
void exit(int status);
用于正常终止程序。如果程序以main函数返回一个值结束,那么作用相当于这个值用作exit函数调用。
当exit函数被调用时,所有被atexit函数注册为退出函数的函数将按照它们所注册的顺序被反序依次调用。然后,所有用于流的缓冲区被刷新,所有打开的文件被关闭,用tmpfile函数创建的文件被删除,然后退出状态返回给宿主环境,程序正常结束。
不要在atexit注册的退出函数中再次调用exit,结果不可预料。
断言<assert.h>
断言就是声明某种东西应该为真。
#define assert(e) ((e) ? (void)0 : _assert(#e, __FILE__, __LINE__))
void _assert (const char*, const char*, int);
如果这个宏的值为假(零),它就向标准错误打印一条诊断信息并终止程序,这条信息的格式是由编译器定义的,但它将包含这个表达式和源文件的名字及断言所在的行号;如果表达式为真(非零),它不打印任何东西,程序继续执行。
例如,如果一个函数的某个参数不能为NULL:
void func(char * p) {
assert(p!=NULL);//Assertion failed: p!=NULL, file ..\src\setjmp.c, line 6
//指针不为空时才能继续
//...
}
int main() {
func(NULL);
}
注意,一旦断言失败,程序就会终止。
当程序被完整地测试完成后,你可以消除所有的断言,这样提高效率。消除有两种方式:使用 –DNDEBUG编译器命令行选项或者在源文件中当头文件assert.h被包含之前增加下面这个定义:
#define NDEBUG
这样在编译时会丢弃所有断言,而又不需要将源文件中所有断言实际删除。
获取环境变量<stdlib.h>
char * getenv(char const *name);
返回操作系统中定义的环境变量,如果没有返回NULL。
//C:\Program Files\Java\jdk1.6.0_22
printf(getenv("java_home"));
注意标准并未定义一个对应的putenv函数,有些编译器提供了这个函数,但为了可移植行最好不要使用。
调用系统命令<stdlib.h>
int system(char const *command);
调用操作系统命令。system的行为以及返回值因编译器而译,但返回值通常是该命令的完成状态。system可以用一个NULL参数,用于询问命令处理器(或shell)是否实际存在,在这种情况,如果存在一个可用的命令处理器,system返回一个非零值,否则返回零。
printf("%d", system(NULL));//1
printf("%d", system("notepad"));//0
快速排序和二分查找<stdlib.h>
void qsort(void *base, size_t n_elements, size_t el_size, int(*compare)(
void const *, void const *));
第1个参数指向需要排序的数组,第2个参数指定数组中的元素个数,第3个参数指定每个元素的长度(以字节为单位),第4个参数是一个函数指针,相当于Java中的一个比较器。该函数可以对任意类型进行排序,但在比较器中需要强制转换再比较。
void *bsearch(void const *key, void const *base, size_t n_elements,
size_t el_size, int(*compare)(void const *, void const *));
第1个参数指向需要查找的值,第2个参数指向需要排序的数组,第3个参数指定数组中的元素个数,第4个参数指定每个元素的长度(以字节为单位),第4个参数是一个函数指针,相当于Java中的一个比较器。返回一个指向查找到的数组元素指针,不存在时返回NULL。注意,查找前请一定要进行排序,否则结束不可预料。
#include <stdlib.h>
#include <string.h>
typedef struct {
char key[10]; //比较关键字
int other_data;
} Record;
//比较器
int r_compare(void const *a, void const *b) {
return strcmp(((Record *) a)->key, ((Record *) b)->key);
}
char * r(Record *, int);
void p(Record *, int);
int main() {
Record array[10] = { { { 0 } } };
Record key;
Record *ans;
char *s = r(array, 10);
p(array, 10);
strcpy(key.key, s);
qsort(array, 10, sizeof(Record), r_compare);
p(array, 10);
ans = bsearch(&key, array, 10, sizeof(Record), r_compare);
if (ans != NULL) {
printf("%s", (*ans).key);
} else {
printf("Not find");
}
return EXIT_SUCCESS;
}
void p(Record * re, int size) {
int i = 0;
for (; i < size; i++, re++) {
printf("%s-%d ", re->key, re->other_data);
}
printf("\n");
}
char * r(Record * re, int size) {
int i = 0;
srand(time(NULL));
int ri;
for (; ri = rand() % 26, i < size; i++, re++) {
*(re->key) = ri + 65;
re->other_data = ri;
}
return (--re)->key;
}
local
修改local可能影响库函数的运行方式。
char * setlocal(int category, char const * locale);
用于修改整个或部分的local,category参数指定locale的哪个部分需要进行修改,它的值如下表:
值 |
修改 |
LC_ALL |
整个locale |
LC_COLLATE |
对照序列,它将影响strcoll和strxfrm函数的行为 |
LC_CTYPE |
定义于ctype.h中的字符处理函数所使用的字符类型 |
LC_MONETARY |
在格式化货币值时使用的字符 |
LC_NUMERIC |
在格式化非货币值时使用的字符。同时修改由格式化输入/输出函数和字符串转换函数所使用的小数点符号 |
LC_TIME |
strftime函数的行为 |
locale参数表示区域名称,格式通常为:
language[_territory][.codeset]
language为ISO639中规定的语言代码,territory为ISO3166中规定的国家/地区代码,codeset为字符集名称。
在Linux下,可以使用locale –a命令查看系统中所有已配置的locale。用不带选项locale命令查看当前Shell中活动的locale。用locale –m命令查看locale系统支持的所有可用的字符集编码。
Windows的locale不符合POSIX规范,比如采用GBK字符集的大陆中文,POSIX名字为 zh_CN.GBK,而在Windows终端中要使用 "Chinese_People's *.936"
当参数locale为NULL时,函数只取回当前正在使用的locale,返回这个当前locale,此时并不修改locale;当locale为 "" 时,根据环境的设置来设定locale,检测顺顺序是:环境变量LC_ALL,每个单独的locale分类LC_*,最后是LANG变量,修改后并反回修改后的locale。为了使用程序可以根据环境来灵活改变locale,一般都在程序的初始化阶段加入下面代码:
setlocale(LC_ALL,"")
当C语言程序初始化时(刚进入到main()时),locale初始化为默认的 "C" locale,其采用的字符编码是所有本地ANSI字符集编码的公共部分,即用来书写C语言源程序的最小字符集(所以才取locale名叫:"C") 。当setlocale调用成功时,会返回当前正在使用locale的全名称,如果失败,会返回NULL。
在Liunx环境中(Windows上返回NULL):
char * loc = setlocale(LC_ALL, "zh_CN.UTF-8");
printf("%s\n", loc);//zh_CN.UTF-8
数值和货币格式<locale.h>
localeconv函数用于获得根据当前的locale对非货值和货币值进行合适的格式化所需要的信息。
struct lconv * localeconv(void);
lconv结构包含两种类型的成员:字符和字符指针。如果一个字符成员为 CHAR_MAX,那这个值就在当前的locale中不可用(或不使用),对于字符指针成员指向一个空字符串(不是NULL),意义与前面相同。
struct lconv
{
char* decimal_point;//用作小数点的字符,该值一定不能是空字符串
char* thousands_sep;//用作分隔小数点左边各组数的符号
位一组,则grouping=”\3”,使用的是八进制表示,如果是4位一组grouping=”\4”,如果仅挨着小数点的3位一组,次4位一组,则grouping=”\3\4”
char* int_curr_symbol;
char* currency_symbol;//本地货币符号
char* mon_decimal_point;//货币小数点
char* mon_thousands_sep;//用于分隔货币小数点左边各组数字的字符
char* mon_grouping; //指定出现在货币小数点左边每组数字的数字个数,与grouping类似
char* positive_sign;//用于提示非负货币值的字符串
char* negative_sign;//用于提示负货币值的字符串
char int_frac_digits;
char frac_digits;//出现在小数点右边的数字个数
(使用 '\1' 赋值方式);如果出现在后面,其值为0(使用 '\0' 赋值方式)
(使用 '\1' 赋值方式),否则为0(使用 '\0' 赋值方式)
(使用 '\1' 赋值方式);如果出现在后面,其值为0(使用 '\0' 赋值方式)
(使用 '\1' 赋值方式),否则为0(使用 '\0' 赋值方式)
char p_sign_posn;//提示positive_sign出现在一个非负值的位置。允许下列值:
0 货币符号和值两边的括号 (使用 '\0' 赋值方式)
1 正号出现在货币符号和值之前(使用 '\1' 赋值方式)
2 正号出现在货币符号和值之后(使用 '\2' 赋值方式)
3 正号紧邻货币符号之前(使用 '\3' 赋值方式)
4 正号紧邻货币符号之后(使用 '\4' 赋值方式)
char n_sign_posn;//提示negative_sign出现在一个负值中的位置,用于p_sign_posn的值也可用于此处
};
注,该函数只是用于获取当前区域中格式化数字及货币格式化的有关信息,这就允许程序员根据当前的lconv中的信息实现一些应用程序特定的转换和格式化函数,并且它们在不同的区域间具有移植性,从而避免了向标准C中添加区域特定的转换工具函数的必要性。
数值和货币格式<locale.h>
int strcoll(const char *s1, const char *s2);
size_t strxfrm(char *dest, const char *src, size_t len);
strcoll函数根据setlocale所设置的LC_COLLATE的区域比较约定进行字符比较,返回的结果与strcmp一样。
strxfrm函数把字符串src转换为另一个字符串,存储在字符数组dest中,经过转换后的字符可以直接使用strcmp来比较。该函数返回存储这转换后的字符串所需要的字符个数(不包括结尾的null字符)。
第十五章 经典抽象数据类型(ADT)
抽象数据类型就是指数据结构,因为数据结构是与语言无关的,所以叫抽象的。
前面章节以讨论过链表结构,现讨论堆栈、队列、树等。
内存分配
实现这些抽象数据类型时,需要分配内存。有三种分配方式:静态数组、动态分配的数组、动态分配的链式结构。
动态分配数组可以使用malloc函数:
char * s = malloc(10);
s[0]='a';
s[1]='b';
s[2]='\0';
printf(s);//ab
链式的动态分配可以在每个元素需要时才单独进行分配,分配最灵活,但在链式结构中访问一个特定元素的效率不如数组。
堆栈
接口
/*
** 堆栈接口
*/
#define STACK_TYPE int /* 栈中的元素类型,定义成宏便于类型方便的更换 */
/*
** push:入栈
*/
void push(STACK_TYPE value);
/*
** pop:出栈,并移除
*/
void pop(void);
/*
** top:返回栈顶元素,但不移除
*/
STACK_TYPE top(void);
/*
** 栈是否为空
*/
int is_empty(void);
/*
** 栈是否已满
*/
int is_full(void);
— — stack.h
数组堆栈
所有不属于外部接口的内容都被声明为static,这可以防止用户使用预定义接口之外的任何方式访问堆栈中的值。
[C和指针]第五部分的更多相关文章
-
C和C指针小记(五)-指针类型
1.指针常量(pointer constant) 一般是没有这个概念的,指针类型的常量理解起来可以看着指针类型的常量,常用 0xff123456 表示,我们一般不会这么做.因为程序员一般无法事先知道计 ...
-
【C++】智能指针简述(五):解决循环引用的weak_ptr
总结一下前文内容: 1.智能指针通过RAII方法来管理指针:构造对象时,完成资源初始化;析构对象时,对资源进行清理及汕尾. 2.auto_ptr,通过“转移所有权”来防止析构一块内存多次.(如何转移? ...
-
【原创】浅谈指针(五)const和指针
前言 过了几个月再次更新.最近时间也不多了,快要期中考试了,暂且先少写一点吧. 本文仅在博客园发布,如在其他平台发现均为盗取,请自觉支持正版. 练习题 我们先来看几道题目.如果这几道题都不会的话,就先 ...
-
C和指针 第五章 位数组
5.4的习题:编写一组函数,实现维数组,函数原型如下: //指定位设置为1void set_bit(char bit_array[], unsigned bit_number); //指定位清零 vo ...
-
C和指针 第五章 习题
下列输出的值: #include <stdio.h> int func(){ static int count = 1; return ++count; } int main() { in ...
-
C和指针 第五章 警告总结
1.有符号的值得右移位操作是不可移植的 2.移位操作的位数是个负数,是未定义的 3.连续赋值的各个变量的长度 不一,导致变量值截断. #include <stdio.h> int main ...
-
C和指针 第五章 逻辑位移与算术位移
对于操作数的左位移都是相同的,右边空出来的位置用0补齐. 但是对于右位移,对于有符号和无符号数是不一样的,最高位的1有两种处理方式.逻辑位移和算术位移. 逻辑位移:右移入位用0补齐 算术位移:右移入位 ...
-
js中的this指针(五)
js中的函数有一个很有意思的地方,即函数自身也方法. apply方法让我们可以建立一个参数数组并用其来调用函数. apply方法接受两个参数,第一个是将被绑定给 this 的值.第二个就是一个参数数组 ...
-
C语言:指针的几种形式二
一.const指针 1.const int* p和int const* p:两者意义是相同的.指向的内容是只读数据,不可以q改变:但是指向的地址可以改变. 2.int* const p:必须先对指针初 ...
随机推荐
-
Java 清除数组相同元素
定义一个函数清除该数组的重复元素,返回一个不能浪费长度的数组.代码如下: import java.util.*; public class demo1 { public static void mai ...
-
noi 2989 糖果
题目链接:http://noi.openjudge.cn/ch0206/2989/ 首先,数据很大,直接用背包会re. 这里增加的是对%k 的余数维度.f[i][j] 表示前 i 种糖果取到总颗数模 ...
-
android去掉顶部标题栏
在清单文件(manifest.xml)里面实现 <application> <activity android:name="cn.ui.activity.UserRegAc ...
-
JavaScript遍历方式详解
为了方便例子讲解,现有数组和json对象如下: var demoArr = ['Javascript', 'Gulp', 'CSS3', 'Grunt', 'jQuery', 'angular']; ...
-
Mysql创建函数时报错
先去查询 show variables like '%func%' ; 这个语句,如果该语句最后输出的值是OFF 那么就用下面的语句去修改就可以:set global log_bin_trust_f ...
-
《代码的第一行——Android》封面诞生
<代码的第一行--Android>已经上市近一个月,现在的情况是相当不错的销售,也特别感谢众多朋友的支持. 其实一本好书,假设你想卖.除了给予外力所要求的内容.封面设计是至关重要的,这本书 ...
-
LuaFramework内存资源管理器ResourceManger详解及切换场景资源清理
1.成员变量 m_BaseDownloadingURL : 获取资源的地方,加载AssetBundle包的时候会用到 m_AssetBundleManifest : 包间依赖关系文件,从这个类中的信息 ...
-
C# for循环或者foreach往List中添加对象的时候前面的数据总被最后加入的覆盖
昨天我旁边小姐姐遇到一个问题,就是在执行for循环往list添加数据的时候,前面的数据信息总是被后面的数据信息所覆盖. 这样编写就会造成这样的数据效果:(所有的数据都会被覆盖) 问题原因:对 ...
-
Unity如何退出游戏
使用 Application.Quit(),但在 editor 模式下使用 Application.Quit()是没用的,要用 EditorApplication.isPlaying = false. ...
-
yum upgrade和yum update的区别
Linux升级命令有两个分别是yum upgrade和yum update, 这个两个命令是有区别的: yum -y update 升级所有包同时也升级软件.系统版本和系统内核 yum -y upgr ...