C 编程/stdlib.h/函数参考
void abort(void);
此函数用于异常终止程序或进程。当程序执行中遇到错误条件时,程序需要退出,这时可以使用此函数。
它会在结束程序之前删除缓冲区并关闭所有打开的文件。abort() 函数会终止当前程序。根据实现的不同,函数的返回值可能指示已取消(例如,使用 signal() 函数捕获 SIGABRT)或终止失败。进程在调用 cstdlib 中定义的 abort libc 函数时会向自身发送 SIGABRT 信号。可以捕获 SIGABRT 信号,但不能阻塞它;如果信号处理程序返回,则所有打开的流都会被关闭并刷新,程序会终止(如果合适的话会转储核心),然后它会将控制权返回给主机环境。这意味着 abort 调用永远不会返回。由于这种特性,它通常用于在支持库中发出致命条件信号,在这种情况下,当前操作无法完成,但主程序可以在退出之前执行清理工作。如果断言失败,它也会被使用。
它是标准 c 库中的一个线程安全函数。也就是说,该函数可以由不同的线程调用,不会出现任何问题。
int abs (int i);
long labs (long i);
double fabs (double i);
float fabsf (float i);
long double fabsl (long double i);
许多编程语言都有函数可以计算数字的绝对值,要么叫 abs 要么叫 Abs。在 C 语言这样的语言中,它有针对长整型和浮点型的变体,分别称为 labs 和 fabs。所有函数都以一个有符号数作为参数,并以相同的数据类型返回该数字的绝对值。
int atexit(void (*function)(void));
此函数注册给定的 函数,以便在正常进程终止时执行,无论是通过 exit 还是通过从程序的主函数返回。
atexit
函数以要注册的回调函数的引用作为参数。如此注册的函数将按照其注册顺序的反序调用;不会传递任何参数。
atexit
函数由 POSIX 规范标准化。
如果该函数成功完成执行,它将返回零 (0)。非零返回值表示错误。
POSIX 要求 atexit
的实现允许注册至少 ATEXIT_MAX (32) 个这样的函数。
ISO/IEC 9899:1999 规范 (PDF). p. 315, § 7.20.4.2.
double atof (const char *str);
此函数将字符串转换为浮点数值表示形式。atof
代表 ASCII 到浮点。它包含在 C 标准库头文件 stdlib.h
中。它的原型如下
str
参数指向一个字符串,该字符串由一个字符数组表示,包含浮点值的 字符 表示形式。如果字符串不是 double
的有效文本表示形式,atof
会静默失败,在这种情况下会返回零 (0.0)。[1]
请注意,虽然 atoi
和 atol
返回与其名称相对应的变量类型(“atoi
” 返回整数,“atol
” 返回长整数),但 atof 不会返回 float
,它返回 double
。
一个相关的函数是 sscanf。此函数从字符串中提取值,其返回值是它成功提取的有效值的个数(因此,与 atof
不同,sscanf
可以用来测试字符串是否以有效数字开头)。
int atoi(const char *str);
此函数将字符串转换为整数数值表示形式。atoi
代表 ASCII 到整数。它包含在 C 标准库头文件 stdlib.h
中。它的原型如下
str
参数是一个字符串,由一个字符数组表示,包含一个有符号整数的字符。该字符串必须以 null 结尾。当 atoi 遇到没有数值序列的字符串时,它会返回零 (0)。
atoi 函数有几个变体,atol、atof 和 atoll,它们分别用于将字符串转换为 long
、double
或 long
long
类型。atoll 以前称为 atoq,并已包含在 C99 中。
无法判断字符串是否包含表示数字 0 的有效数字序列或无效数字,因为该函数在这两种情况下都会返回 0。较新的函数 strtol 没有此缺陷。
atoi 在某些操作系统上既不是线程安全的,也不是异步取消安全的。[2]
此外,atoi 只转换十进制 ascii 值(从不同的角度来看,这可能也是一个优点)。strtol 和其他函数支持十六进制和八进制等其他进制。
然而,由于返回 0 的模糊性和在某些操作系统上缺乏线程安全和异步取消安全,atoi 被认为已被 strtol 弃用。[2]
The Version 7 Unix Manual Pages © 1979 by Bell Telephone Laboratories, Incorporated.
The Version 1 Unix Manual page for atoi written by Ken Thompson (November 1971).
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
int (*compare)(const void *, const void *));
bsearch 是一个通用函数,它可以在包含任何类型对象或指向对象的指针的任何大小的排序数组中进行搜索,并使用任何类型的比较谓词。然而,通用性是以类型安全为代价的,因为 bsearch 在 void 指针上操作;并且在函数调用次数上也很昂贵(因为每次比较都需要调用比较谓词),这会产生很大的开销。
bsearch() 函数返回指向数组中匹配成员的指针,如果未找到匹配项,则返回 NULL。如果数组具有多个匹配元素,则返回值将是指向其中一个元素的指针。哪个特定元素是未指定的。[4]
div 是 C 编程语言中的一个函数,它接受两个整数作为参数并返回它们之间除法的结果。它在 ANSI-C 中指定,在使用时从 stdlib.h 头文件中包含。
div 总是向 0 舍入,这与 C 中的普通整数除法不同,在 C 中,负数的舍入是实现相关的。
div
的原型如下
div_t div (int numerator, int denominator)
返回值 div_t
是一种特殊的数据类型,专门用于存储该函数的结果。它的定义如下
typedef struct {
int quot;
int rem;
} div_t;
其中 quot
存储商,rem
存储余数。
ldiv 和 lldiv 是类似的函数,它们分别对 long
和 long long
类型的整数进行除法;并分别返回类型为 ldiv_t
和 lldiv_t
的结构。
ldiv_t ldiv (long numerator, long denominator );
lldiv_t lldiv (long long numerator, long long denominator);
- stdlib.h
- stdio.h
在许多计算机操作系统上,计算机进程通过执行 exit 系统调用来终止其执行。更一般地说,在多线程环境中退出意味着执行线程已停止运行。操作系统回收进程使用的资源(内存、文件等)。进程在终止后被称为死进程。
在 Unix 和类 Unix 操作系统下,当父进程执行fork 系统调用时,进程启动。父进程随后可能等待子进程终止,或者可能继续执行(可能分叉出其他子进程)。当子进程终止(“死亡”)时,无论是通过调用exit正常终止,还是由于致命错误或信号(例如,SIGTERM、SIGINT、SIGKILL)异常终止,退出状态都会返回给操作系统,并且会向父进程发送 SIGCHLD 信号。然后,父进程可以通过wait 系统调用检索退出状态。
大多数操作系统允许终止进程向系统提供特定的退出状态,该状态会提供给父进程。通常,这是一个小的整数值,尽管某些操作系统(例如,Plan 9)允许指定一个字符字符串。
退出操作通常在将控制权返回给操作系统之前在进程空间内执行清理操作。一些系统和编程语言允许注册用户子例程,以便在程序真正终止之前调用它们。作为终止的最后一步,会调用一个原始系统退出调用,通知操作系统进程已终止,并允许其回收进程使用的资源。
有时可以绕过通常的清理;C99 提供了_exit()
函数,它终止当前进程,没有任何额外的程序清理。例如,这可以在 fork-exec 例程中使用,当exec
调用无法替换子进程时;调用atexit
例程会错误地释放属于父进程的资源。
一些操作系统以特殊方式处理父进程已终止的子进程。这种孤儿进程成为特殊根进程的子进程,然后等待子进程终止。同样,类似的策略用于处理僵尸进程,即已终止但其退出状态被其父进程忽略的子进程。这种进程成为特殊父进程的子进程,该进程检索子进程的退出状态,并允许操作系统完成死进程的终止。处理这些特殊情况会使系统进程表保持一致状态。
在计算中,malloc
是执行动态内存分配的子例程。malloc
是标准库的一部分,在stdlib.h
头文件中声明。
malloc
的许多实现可用,每个实现根据计算硬件和程序的编写方式而表现不同。性能在执行时间和所需内存方面各不相同。使用 malloc 分配的内存的指针最终必须传递给free
子例程以释放内存,以避免内存泄漏。
malloc
函数是标准 C 中用于分配内存的函数之一。它的函数原型是
void *malloc(size_t size);
它分配size
字节的内存。如果分配成功,则返回指向内存块的指针。该指针保证适当地对齐到任何类型(包括结构体等),否则返回 NULL 指针。
通过malloc
分配的内存是持久存在的:它将继续存在,即使程序离开调用分配的范围,直到程序终止或程序员显式地通过free
子例程释放内存。这是通过使用free
子例程实现的。它的原型是
void free(void *pointer);
它释放由pointer
指向的内存块。pointer
必须是之前由malloc
,calloc
或realloc
返回的,并且在将其传递给free之后,pointer
不能再使用。特别是,通过new或new[]分配的内存不应该传递给free,并且没有来自malloc(例如堆栈变量)或已经被释放的指针不能发送到free。调用NULL指针上的free是安全的,它没有任何效果。
使用示例
[edit | edit source]创建10个int对象的数组的标准方法
int array[10];
但是,如果希望动态地分配类似的数组,可以使用以下代码
/* Allocate space for an array with ten elements of type
int. */
int *ptr = (int *) malloc(10 * sizeof (int));
if (ptr == NULL) {
/* Memory could not be allocated, the program should
handle the error here as appropriate. */
} else {
/* Allocation succeeded. Do something. */
free(ptr); /* We are done with the int objects, and
free the associated pointer. */
ptr = NULL; /* The pointed-to-data must not be used again,
unless re-assigned by using malloc
again. */
}
malloc
返回一个空指针以指示没有可用内存,或发生了其他错误,从而阻止了内存分配。
由于malloc
调用可能由于内存不足而失败,因此定义一个在malloc
失败时调用malloc
并退出的宏通常很方便。一个可能的宏定义是
if(!(ptr=malloc(sizeof(int))))
{
fprintf(stderr,"Insufficient memory"); /* Prints the necessary error statement. */
exit(EXIT_FAILURE); /* Exits <code>malloc</code> as there is insufficient memory. */
}
此宏将是
#define MALLOC(p,s) /*Here p & s are 2 integer pointer & variable respectively. */ \
if(!((p)=malloc(s))) \
{ \
fprintf(stderr,"Insufficient memory"); /* Prints the necessary error statement. */ \
exit(EXIT_FAILURE); /* Exits <code>malloc</code> as there is insufficient memory. */ \
}
因此,上述基本代码可以用一行代码代替
MALLOC(ptr,sizeof(int));
此示例中显示了使用malloc
的有用习惯用法
int *ptr = malloc(10 * sizeof(*ptr));
也就是说,不要将硬编码类型写入malloc
的参数,而是使用要分配的指针的内容对sizeof
运算符。这确保了分配左侧和右侧的类型在代码修改时永远不会不同步。
用于创建和返回大小为m*n的二维数组的C函数
int **create(int m, n)
{
int **p, i;
p = (int **)malloc(m*sizeof(int*));/* this will store base order of all the row in p */
for(i = 0; i < m; i++)
p[i] = (int *)malloc(n*sizeof(int));/* this will create m row of n elements */
return p;
}
强制转换和类型安全
[edit | edit source]malloc
返回一个void指针(void *
),它表示它是一个指向未知数据类型区域的指针。malloc
返回的特定指针类型缺乏是一种类型不安全行为:malloc
根据字节数进行分配,而不是根据类型进行分配。
可以将此指针“强制转换”(参见类型转换)为特定类型
int *ptr;
ptr = malloc(10 * sizeof (*ptr)); // Without a cast
ptr = (int*)malloc(10 * sizeof (int)); // With a cast
执行此类强制转换有优点和缺点。
强制转换的优点
[edit | edit source]- 如果存在强制转换,并且随后更改了左侧指针的类型,则将生成一个警告以帮助程序员纠正否则可能成为错误的行为。
- 强制转换允许使用最初返回
char *
的旧版malloc
。[6]
强制转换的缺点
[edit | edit source]- 根据ANSI C标准,强制转换是多余的。
- 添加强制转换可能会掩盖未包含头文件
stdlib.h
的错误,其中找到了malloc
的原型。[6][7]在没有malloc
的原型的情况下,标准要求C编译器假设malloc
返回一个int
。如果没有强制转换,在将此整数分配给指针时会发出警告;但是,使用强制转换,则不会生成此警告,从而隐藏了一个错误。在某些体系结构和数据模型(例如64位系统上的LP64,其中long
和指针为64位,而int
为32位)中,此错误实际上会导致未定义的行为,因为隐式声明的malloc
返回一个32位值,而实际定义的函数返回一个64位值。根据调用约定和内存布局,这可能会导致堆栈溢出。此问题在现代编译器中不存在,因为它们统一生成使用未声明函数的警告,因此仍然会出现警告。例如,gcc的默认行为是显示一条警告,提示“内建函数的不兼容隐式声明”,无论是否存在强制转换。
相关函数
[edit | edit source]calloc
[edit | edit source]malloc
返回一个为程序员分配的内存块,用于程序员使用,但未初始化。如果需要,内存通常会手动初始化,方法是通过memset
函数,或者通过一个或多个对指针进行解引用赋值语句。另一种方法是使用calloc
函数,它分配连续内存,然后将其初始化为零。它的原型是
void *calloc(size_t nelements, size_t elementSize);
realloc
[edit | edit source]能够缩小或扩大内存块通常很有用。这可以使用realloc
来完成,它返回一个指向指定大小的内存区域的指针,该区域包含与pointer
指向的旧区域相同的数据(截断为旧大小和新大小的最小值)。如果realloc
能够就地调整内存区域的大小,它将分配新的存储空间,复制所需的数据,并释放旧指针。如果此分配失败,realloc
将保持原始指针不变,并返回空指针值。在扩展的情况下,复制到旧数据的外部新内存区域未初始化(内容不可预测)。函数原型是
void *realloc(void *pointer, size_t size);
常见错误
[edit | edit source]不正确使用malloc
和相关函数通常会导致错误。
分配失败
[edit | edit source]不能保证malloc
会成功,如果内存不可用,或者程序已超过其允许引用的内存量,malloc
将返回一个空指针,这应该在分配后始终检查。许多编写得很差的程序没有检查malloc
是否失败。这样的程序将尝试像使用指向已分配内存一样使用malloc
返回的空指针。该程序很可能崩溃;在某些环境中,特别是执行虚拟内存管理的较旧或较小的平台,零是一个有效地址,因此问题将不会被发现。
内存泄漏
[edit | edit source]当malloc
,calloc
或realloc
调用成功时,返回的指向已分配内存的指针最终应该传递给free
函数。这将释放已分配的内存,使其能够重复使用以满足其他内存分配请求。如果不这样做,已分配的内存将不会在进程退出之前(在某些环境中,甚至在进程退出之后)释放,换句话说,内存泄漏将发生。通常,内存泄漏是由于丢失指针造成的,例如没有使用临时指针来表示realloc
的返回值,这可能会导致原始指针被空指针覆盖。
释放后使用
[edit | edit source]在将指针传递给free
之后,它将变为悬空指针:它引用一个具有未定义内容的内存区域,该区域可能不可用。指针的值不可访问。例如
int *ptr = malloc(sizeof (int));
free(ptr);
*ptr = 7; /* Undefined behavior */
类似的代码具有未定义的行为:其效果可能会有所不同。实际上,即使尝试读取已释放指针的值也会导致未定义的行为(此处)。
释放未分配的内存
[edit | edit source]另一个问题是,当free
传递一个未由malloc
,realloc
或calloc
分配的地址时。当将指向字面字符串的指针或已声明数组的名称传递给free
时,可能会发生这种情况,例如
char *msg = "Default message";
int tbl[100];
将上述任一指针传递给free
会导致未定义的行为。
分配大小限制
[edit | edit source]malloc
能分配的最大内存块取决于主机系统,尤其是物理内存的大小和操作系统的实现。理论上,最大值应该是 size_t
类型可以容纳的最大值,它是一个与实现相关的无符号整数,表示内存区域的大小。最大值为 2CHAR_BIT*sizeof(size_t) − 1
,或 C99 标准中的常量 SIZE_MAX
。
外部链接
[edit | edit source]- IEEE Std 1003.1 标准中 malloc 的定义
- glibc 分配器基础的设计,作者:Doug Lea
- ptmalloc 主页,作者:Wolfram Gloger
- Hoard 主页,作者:Emery Berger
- nedmalloc 主页,作者:Niall Douglas
- jemalloc 主页,作者:Jason Evans
- TCMalloc 主页,由 Google 开发的高性能 malloc
- 简单的内存分配算法,来自 OSDEV 社区
- Hoard:用于多线程应用程序的可扩展内存分配器,作者:Emery Berger
- 可扩展的无锁动态内存分配,作者:Maged M. Michael
- 内存管理内部 - 动态分配的选择、权衡和实现,作者:Jonathan Bartlett
- 内存减少(GNOME) wiki 页面,包含大量关于修复 malloc 的信息
- C99 标准草案,包括 TC1/TC2/TC3
- 一些关于 C 的有用参考
- ISO/IEC 9899 - 编程语言 - C
qsort
[edit | edit source]qsort 是一个用于对数组中的元素进行排序的函数。它以快速排序算法命名,尽管 C 标准不要求它使用任何特定的算法来实现。[8]
qsort
是一个通用函数,它可以对任何大小的数组进行排序,这些数组包含任何类型的对象(尽管,如果对象大小不同,则必须使用指针),并使用任何类型的比较谓词。但是,通用性是以牺牲类型安全性为代价的,因为 qsort
对 void 指针进行操作。
原型
[edit | edit source]void qsort(void *base, size_t nmemb, size_t size, int (*compare)(const void *, const void *));
行为
[edit | edit source]数组的内容根据比较函数(由 compare 指向)进行排序。当项目比较相等时,它们在结果数组中的顺序是不确定的,这意味着 qsort 不必是稳定的排序。
strtod
[edit | edit source]strtod(string to double)将字符字符串转换为双精度浮点数。[9] 它被定义为
double strtod ( const char * str, char ** endptr );
概述
[edit | edit source]strtod 函数解析 C 字符串 str,将其内容解释为浮点数,并将其值作为双精度数返回。如果 endptr 不是空指针,函数还会将 endptr 指向的值设置为指向数字后面的第一个字符。
该函数首先丢弃尽可能多的空格字符,直到找到第一个非空格字符。然后,从这个字符开始,获取尽可能多的字符,这些字符有效地遵循类似于浮点文字的语法,并将它们解释为数值。指向数字后面最后一个有效字符之后的字符串的指针存储在 endptr 指向的对象中。
strtod 的有效浮点数由以下内容组成:
- 可选的加号或减号
- 数字序列,可选地包含小数点字符
- 可选的指数部分,它本身由一个 'e' 或 'E' 字符后跟一个可选符号和一个数字序列组成。
如果 str 中第一个非空格字符序列不构成上面定义的有效浮点数,或者如果由于 str 为空或仅包含空格字符而不存在这样的序列,则不会执行转换。
参数
[edit | edit source]str
- 以浮点数表示形式开头的 C 字符串。
endptr
- 对已分配的 char* 类型对象的引用,该函数将该值设置为指向 str 中数值后面的下一个字符。此参数也可以是空指针,在这种情况下它不会被使用。
返回值
[edit | edit source]如果成功,该函数将返回转换后的浮点数作为双精度值。如果无法执行有效的转换,则返回零值 (0.0)。如果正确的值超出了可表示值的范围,则返回正值或负值 HUGE_VAL,并将全局变量 errno 设置为 ERANGE。如果正确的值会导致下溢,则返回零,并将 errno 设置为 ERANGE。
strtod 函数包含在 stdlib.h
中。
strtol
[edit | edit source]strtol 是 C 编程语言中一个将字符串转换为长整数的函数。strtol
代表string to long。它包含在 C 标准库头文件 stdlib.h
中。它的原型如下所示
long strtol(const char *restrict str, char **restrict end, int base);
str
参数指向一个字符串,该字符串由一个字符数组表示,其中包含有符号整数值的字符表示形式。该字符串必须以 null 结尾。base
参数指定要使用的数字基数,从 2 到 36。如果数字基数大于 10,则使用字母字符('A'
到 'Z'
)作为表示形式中的数字。转换完成后,end
指向的值将设置为指向字符串中最后一个有效数字字符后面的字符,并将返回转换后的整数值。如果字符串不包含有效的数字序列,则返回零 (0),并将全局变量 errno
设置为 EINVAL
。
这些函数还有类似的变体,分别名为 strtoul
、strtoll
和 strtoull
,它们分别解析并返回类型为 unsigned long
、long long
和 unsigned long long
的整数。
标准一致性
[edit | edit source]strtol 函数是 ISO 标准 C 库(C89,1989)的一部分,strtoll 函数作为 C99 库(1999)的一部分添加。它作为对现有 atoi 函数的更良好行为的替换而添加到标准 C 库中。
参考资料
[edit | edit source]- strtol at OpenGroup.org
- 正确且可移植地使用 strtol
在 C 标准库中,system 是一个用于执行子进程和命令的函数。 它在 stdlib.h
头文件中定义。 它不同于 exec
/spawn
函数族,因为 system
不向执行对象传递参数,而是向系统 shell(通常是 POSIX shell,/bin/sh -c
)传递一个字符串。
int system (const char *command);
system
函数是阻塞的;也就是说,调用将等待直到子进程终止并返回它的退出值。 在这段时间内,SIGCHLD
将被阻塞,因为 system
等待子进程死亡;此外,SIGINT
和 SIGQUIT
被忽略,所以为了确保响应性,程序员应该检查返回值以查看用户是否试图终止进程。 发生错误时,system
在 fork
之前或在 fork
时失败将返回 -1(例如,进程数量限制已达到),但在 fork
之后失败(例如,无法执行 sh)将返回 127;这与命令以状态 127 退出无法区分。
在 POSIX 下,system
会派生并执行 /bin/sh
,带有两个参数:"-c
" 和 command
。 虽然 sh 的行为在其他地方有说明,但值得注意的是,command
不必是一个单一的命令;它实际上可以是一个管道,甚至是一系列管道。 例如,考虑一个想要显示屏幕截图的程序
system ("pngtopnm \"My Screenshot.png\" | pnmtoxwd > out.xwd && xwud out.xwd");
这一行展示了一个重要的考虑因素:由于 command
将被解析为一个 shell 命令行,因此必须对例如文件名周围的引号进行转义。 但是,这会引发安全问题,因为如果 command
是从用户提供的数据构建的,攻击者可能会绕过任何引号并以父进程的上下文中执行任意命令;事实上,这几乎是规范的代码注入漏洞。 因此,只有在预定的命令字符串上使用 system
被认为是明智的,使用其他函数(spawn
等)通过 argv 传递用户提供的数据,或者通过管道或临时文件传递此类数据。
由 system
派生的子进程会继承父进程的标准流;因此子进程可以接收键盘输入并写入终端。 请注意,这意味着父进程不会接收子进程的输出,除非使用重定向或 tee。
- ↑ ISO/IEC 9899:1999 规范, 第 307 页,§ 7.20.1.1
- ↑ a b http://www.codecogs.com/reference/c/stdlib.h/atoi.php
- ↑ ISO/IEC 9899:1999 规范 (PDF). 第 318 页,§ 7.20.5.1.
- ↑ UNIX 手册页:
man 3 bsearch
- ↑ http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf 第 7.20.6.2 章
- ↑ a b FAQ > Explanations of... > Casting malloc 在 Cprogramming.com 上于 2007 年 3 月 9 日访问
- ↑ comp.lang.c FAQ 列表 · 问题 7.7b 在 C-FAQ 上于 2007 年 3 月 9 日访问
- ↑ ISO/IEC 9899:1999 规范 (PDF). 第 319 页,§ 7.20.5.2.
- ↑ ISO/IEC 9899:1999 规范 (PDF). 第 308 页,§ 7.20.1.3.