这里用来记录在 pwn 题中一些 C 语言函数的作用和一些函数的漏洞
# 1. atol、 atoi、atoq
int atoi(const char *nptr); | |
long atol(const char *nptr); | |
long long atoll(const char *nptr); | |
long long atoq(const char *nptr); |
atoi: 把字符串nptr转换为int
atol: 把字符串nptr转换为 long int/long long int
atoq:
# 2.__readfsqword(0x28u)
这段代码是通常用于 alarm 函数,防止调试
unsigned char __readfsbyte( | |
unsigned long Offset | |
); | |
unsigned short __readfsword( | |
unsigned long Offset | |
); | |
unsigned long __readfsdword( | |
unsigned long Offset | |
); | |
unsigned __int64 __readfsqword( | |
unsigned long Offset | |
); |
Offset:
[in] 从 FS 的开头开始读取的偏移量。
返回值:位置 FS:[Offset] 处的字节、字、双字或四字(由调用的函数名称指示)的内存内容。
# 3.fopen("arg1","agr2")
该函数原型:
FILE *fopen(const char *filename, const char *mode); | |
//filename-- 这是 C 字符串,包含了要打开的文件名称。 | |
//mode-- 这是 C 字符串,包含了文件访问模式。 |
功能:使用给定的模式 mode 打开 filename 所指向的文件。
返回值:文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回 NULL,并把错误代码存在 error 中。
# 4.fread(buffer,size,count,stream)
功能:从给定输入流 stream 读取最多 count 个对象到数组 buffer 中(相当于以对每个对象调用 size 次 fgetc),把 buffer 当作 unsigned char 数组并顺序保存结果。流的文件位置指示器前进读取的字节数。
返回值:
返回成功读取的对象个数,若出现错误或到达文件末尾,则可能小于count。
若size或count为零,则fread返回零且不进行其他动作。
fread不区分文件尾和错误,因此调用者必须用feof和ferror才能判断发生了什么。
示例
- buffer : 指向要读取的数组中首个对象的指针
- size : 每个对象的大小(单位是字节)
- count : 要读取的对象个数
- stream :输入流
# 5. int sprintf(char *string, char *format [,argument,...]);
将结果打印到字符串中,而printf是直接在命令行上输出
- string-- 这是指向一个字符数组的指针,该数组存储了 C 字符串。
- format--format-- 这是字符串,包含了要
被写入到 string 的文本
。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。 - [argument]...:根据不同的 format 字符串,函数可能需要一系列的附加参数,每个参数包含了一个要被插入的值,替换了 format 参数中指定的每个 % 标签。参数的个数应与 % 标签的个数相同。
返回值:
如果成功,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符。如果失败,则返回一个负数。
sprintf 返回以 format 为格式 argument 为内容组成的结果被写入 string 的字节数,结束字符‘\0’不计入内。即,如果 “Hello” 被写入空间足够大的 string 后,函数 sprintf 返回 5。
转换字符
%% 印出百分比符号,不转换。
%c 字符输出到缓冲区,不转换。
%d 整数转成十进位。
%f 倍精确度数字转成浮点数。
%o 整数转成八进位。
%s 字符串输出到缓冲区,不转换。
%x 整数转成小写十六进位。
%X 整数转成大写十六进位。
# 6.memchr(const void *buf, int ch, size_t count)
extern void *memchr(const void *buf , int ch , size_t count) |
功能:从 buf 所指内存区域的前 count 个字节查找字符 ch。
说明:当第一次遇到字符 ch 时停止查找。如果成功,返回指向字符 ch 的指针;否则返回 NULL。
# 7.memcpy(void *destin, void *source, unsigned n)
void *memcpy(void *destin, void *source, unsigned n); |
功能:从源 source 所指的内存地址的起始位置开始拷贝 n 个字节到目标 destin 所指的内存地址的起始位置中
返回值:该函数返回一个指向目标存储区 destin 的指针。
- destin-- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
- source-- 指向要复制的数据源,类型强制转换为 void* 指针。
- n-- 要被复制的字节数。
# 8.char * strcat ( char * destination, const char * source );
功能:strcat 的实现模式是将 src 中的所有字符(连同字符串最后的’\0’一起)加到 dest 字符串中第一个‘\0’的位置,具体一点说就是将 dest 中第一个’\0’替换成 src 的第一个字符,然后该’\0’后的字符替换成 src 的第二个字符,后面以此类推。【把 src 所指向的字符串追加到 dest 所指向的字符串的结尾】
返回值:该函数返回一个指向最终的目标字符串 dest 的指针。
# 9.fork()
fork 被调用一次会返回两次(后面的语句也就判断 2 次),返回 0 代表其是子进程,而在父进程中接到的返回值是子进程的 pid,fork 为负值代表出现错误
调用 fork 会产生一个子进程,子进程会复制父进程的当前状态,这时两个进程便会开始同时运行,且执行顺序不一定
子进程的fork()返回值为0 #第一次返回(不分先后,第一次返回也可能是父进程的fork)
父进程的fork()返回值为子进程的pid #第二次返回
https://blog.csdn.net/cuit2016123070/article/details/83280125
# 10.int fclose (FILE *fp); 【可以利用 Linux 命令重定向标准输入输出控制】
功能:关闭文件输出流(使用 fclose () 函数就可以把 缓冲区内最后剩余的数据输出到内核缓冲区,并释放 文件指针和有关的缓冲区。 )
返回值:如果流成功关闭,fclose 返回 0,否则返回 EOF(-1)【关闭成功返回 1】。(如果流为 NULL,而且程序可以继续执行,fclose 设定 error number 给 EINVAL,并返回 EOF。)
绕过方式:Linux 的 exec(cat [文件名]) 1>&0
,在 Linux 系统中文件描述符 1为标准输出流,0为标准输入流,2为标准错误(输出到屏幕)
,而通过 &+文件描述符
的方式可以代替文件名,(这里就指向了该终端,所以结果显示在终端上 因为默认打开一个终端后,0,1,2都指向该终端
)
# 11.strtol()
long int strtol(const char *str, char **endptr, int base) |
作用:将参数 str 所指向的字符串根据给定的 base 转换成一个长整型数(long int 型),base 必须介于 2 和 36 之间(包含),或者是特殊值 0
- str 将要转换成长整型的字符串
- endptr 对类型 char * 的引用(引用 str),其值设置为在 str 中
数值型字符串
的下一个字符(如 123ab,则设置为 ab),用来判断是否转化成功 - base 基数,如果为 0 则会根据字符串的前缀来进行判断进制;如果字符串以
0x
或0X
开头,则视为十六进制;如果字符串以0
开头,则视为八进制;否则视为十进制
返回值:
返回被转换的长整型数值,如果字符串不符合数字格式,则返回 0;若转换结果超出了 long
整数的表示范围,会产生 溢出
,并设置为 errno 为 ERANGE (可以使用 < srrno.h> 头文件中的 errno 变量来检查是否有溢出发生)
如果输入字符串不能被完全转换为整数,strtol () 函数将返回转换成功的部分,而 endptr
将指向 未转换
部分的第一个字符。在这个例子中, endptr
是指向字符串 末尾
的 空字符 '\0'
,表示 整个
输入字符串都被 成功转换
为整数。
如果输入字符串包含 非数字字符
,例如 "12ab",那么 endptr
将指向 "ab"
的起始位置,指示 转换失败
。
# 12.scanf()
int scanf(const char * restrict format,...); |
作用:
函数 scanf () 是从标准输入流 stdin [1] (标准输入设备,一般指向键盘) 中读内容的通用子程序,可以说明的格式读入多个字符,并保存在对应地址的变量中。
函数的第一个参数是格式字符串,它指定了输入的格式,并按照格式说明符解析输入对应位置的信息并存储于可变参数列表中对应的指针所指位置。每一个指针要求非空,并且与字符串中的格式符一一顺次对应。
返回值:
scanf 函数返回成功读入的数据项数,读入数据时遇到了 “文件结束” 则返回 EOF。
format 指向的字符串包含的格式指令(只举例一个 s):
s 读入一个的字符序列,后面会加上 空字节
,遇到 空白字符
(\t \r \n 空格等) 完成读取。
% s 表示读字符串,而 % d 表示读整数。格式串的处理顺序为从左到右,格式说明符逐一与变元表中的变元匹配。
格式命令可以说明最大域宽。 在百分号 (%) 与格式码之间的整数用于限制从对应域读入的最大字符数。例如,希望向 address 读入不多于 20 个字符时,可以书写成如下形式:
scanf("%20s",address);
如果输入流的内容 多于
20 个字符,则下次 scanf () 从 此次停止处
开始读入。 若达到最大域宽前已 遇到空白符
,则对该域的读 立即停止
;此时,scanf () 跳到下一个域。 【这也就是意味着,当读入超过20个字符时,没有空白字符时会继续读入】
# 13.memcmp 函数
int memcmp(const void *str1, const void *str2, size_t n) |
作用:
把存储区 str1 和存储区 str2 的前 n 个字节进行比较
参数:
- str1 -- 指向内存块的指针
- str2 -- 指向内存块的指针
- n -- 要被比较的字节数
返回值:
- 如果返回值 < 0,则表示 str1 小于 str2
- 如果返回值 > 0,则表示 str1 大于 str2
- 如果返回值 = 0,则表示 str1 等于 str2
c 语言非 0 即为真,因此必须相等才为假
与strcmp不同,该函数在找到空字符后不会停止比较
比较s1和s2所指向对象的前n个字符,如果都相等,返回0,出现第一个不等的地方,如果s1指向的数大于s2指向的数,返回大于0的数,如果小于,返回小于0的数。
对于这个库函数你是否表示费解,首先,有strcmp和strncmp库函数了,看到memcmp的函数声明是void*,我以为是可以比较任意类型的,比如int,float和double,那这个函数和strcnmp是不是重复了呢?
二者都可以用于字符串的比较,但是二者是有比较大的差异的,因为strncmp是按照字节(byte-wise)比较的,并且比较的过程中会检查是否出现了"\0"结束符,
一旦任意一个字符串指针前进过程中遇到结束符,将终止比较。
而memcmp函数是用于比较两个内存块的内容是否相等。
如果要比较的对象中包含一些由于边界对齐需求而填入结构对象中的空格、联合 (union)结束的额外空格、字符串所分配的空间未使用完的部分引起的“holes”的话,最好使用memcmp来完成。
这些“holes”(漏洞)的内容是不确定的,在执行byte-wise比较时结果也是不明确的。
memcmp 是可以比较 int,float,double
这里的比较是从左往右开始的 假设正确的Canary是0x12345678。您发送作为第一个字节。0x01memcmp会看到这个字节和Canary的第一个字节是匹配的,因此它会认为是正确的,发送0x01 0x23memcmp时会看到前两个字节都与Canary匹配,所以它仍然会认为它们是匹配的
https://www.cnblogs.com/yangguang-it/p/7236896.html
# 14. strcat 函数
char *strcat(char *dest, const char *src) |
作用:
把 src 所指向的字符串 追加
到 dest 所指向的字符串的 结尾
返回值:
该函数返回一个指向最终的目标字符串 dest 的指针
# 15.puts 函数
puts 函数是 C 语言标准库中的一个函数,用于输出一个字符串并在结尾加上一个 换行符('\n')
。
当 puts 函数 遇到
字符串结尾的 空字符('\0')
时,它会 停止输出
,因为空字符是 C 语言中字符串的结束标志, 意味着可以覆盖原本的空字符来泄露后面的内容
# 16.strncmp 函数
int strncmp(const char *str1, const char *str2, size_t n) |
作用:
把 str1 和 str2 进行比较,最多比较前 n 个字符。
strncmp () 函数通常用于比较两个字符串,以确定它们是否相等或哪个字符串在字典顺序上更小。
参数:
- str1 -- 要进行比较的第一个字符串。
- str2 -- 要进行比较的第二个字符串。
- n -- 要比较的最大字符数。
返回值:
- 如果返回值 < 0,则表示 str1 小于 str2。
- 如果返回值 > 0,则表示 str1 大于 str2。
- 如果返回值 = 0,则表示 str1 等于 str2。
# 17.mmap 函数
void mmap(void start,size_t length,int prot,int flags,int fd,off_t offset); | |
int munmap(void* start,size_t length); |
作用:
mmap 函数将文件映射到进程地址空间,实现直接访问文件内容的功能 (mmap 函数通常用于在内存中分配一块连续的地址空间,并指定相应的权限和属性)
mmap 将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。mmap 在用户空间映射调用系统中作用很大。
mmap 必须以页大小为单位进行映射,而内存也只能以页为单位进行映射,若要映射非 PAGE_SIZE 整数倍的地址范围,要先进行内存对齐,强行以 PAGE_SIZE 的倍数大小进行映射
mmap
函数,它开辟了一个可执行地址,返回值就是该地址
# 2
mmap () 函数的主要用途有三个:
1、将一个普通文件映射到内存中,通常在需要对文件进行频繁读写时使用,这样用内存读写取代 I/O 读写,以获得较高的性能;
2、将特殊文件进行匿名内存映射,可以为关联进程提供共享内存空间;
3、为无关联的进程提供共享内存空间,一般也是将一个普通文件映射到内存中。
# 补充
【尽管通过 mmap 开辟的空间没有执行权限,但是有读写权限时,仍然可以在这执行 open,read,write 等对应权限的功能】
open 打开文件时设置的权限(如读写权限 O_RDWR/O_RDONLY/O_WRONLY)必须要和 mmap 系统调用所需权限相匹配。
具体来说,
打开时,必须允许读取,即 O_RDONLY 和 O_RDWR 至少指定一个;
mmap 调用时,如果 prot 参数中指定了 PROT_WRITE,并且 flags 中指定了 MAP_SHARED,那么打开时,必须指定 O_RDWR。
注意:
1)open 时,不是所有文件都支持 mmap,如管道文件。
2)mmap 完成后,关闭 fd 并不会释放内存映射对应空间,需要专门调用 munmap。
https://www.cnblogs.com/fortunely/p/16212027.html
# 参数
参数 start
指向欲映射的内存起始地址,通常设为 NULL(0),代表让系统自动选定地址,映射成功后返回该地址。
参数 length
代表将文件中多大的部分映射到内存。
参数 prot
映射区域的保护方式。可以为以下几种方式的组合:
PROT_EXEC 映射区域可被执行 //1
PROT_READ 映射区域可被读取 //4
PROT_WRITE 映射区域可被写入//2
PROT_NONE 映射区域不能存取
参数 flags
影响映射区域的各种特性。在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。
MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此标志。
MAP_SHARED对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。
MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写回原来的文件内容。
MAP_ANONYMOUS建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。
MAP_DENYWRITE只允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝。
MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)。
参数 fd
要映射到内存中的文件描述符。如果使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设为-1。有些系统不支持匿名内存映射,则可以使用fopen打开/dev/zero文件,然后对该文件进行映射,可以同样达到匿名内存映射的效果。
参数 offset
文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是分页大小的整数倍。
返回值
若映射成功则返回映射区的内存起始地址,否则返回MAP_FAILED(-1),错误原因存于errno 中。
错误代码
EBADF 参数fd不是有效的文件描述词
EACCES 存取权限有误。如果是MAP_PRIVATE 情况下文件必须可读,使用
MAP_SHARED则要有PROT_WRITE以及该文件要能写入。
EINVAL 参数start、length 或offset有一个不合法。
EAGAIN 文件被锁住,或是有太多内存被锁住。
ENOMEM 内存不足。
需要注意的是内核并不是实时同步映射区与文件的,相反内核很少主动去同步,除非我们调用了函数 msync 或者关闭映射区(关闭映射区的时候,也不是立即同步的)
# 18.putchar 函数
int putchar(int char); |
作用:
C 库函数 int putchar (int char) 把参数 char 指定的字符(一个无符号字符)写入到标准输出 stdout 中【在屏幕上输出 char 型字符】
返回值:
该函数以无符号 char 强制转换为 int 的形式返回写入的字符,如果发生错误则返回 EOF。
# 19.memset 函数
void *memset(void *str, int c, size_t n) |
作用:
C 库函数 void *memset(void *str, int c, size_t n)
复制字符 c(一个无符号字符)到参数 str 所指向的字符串的 前 n 个字符
参数
- str -- 指向要填充的内存块。
- c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。
- n -- 要被设置为该值的字符数。
返回值
该值返回一个指向存储区 str 的指针。
# 20.bzero () 函数
void bzero(void *s, int n); |
作用:
bzero 函数用来将内存块(s)的前 n 个字节清零【由于 bzero 不是标准库函数,所以可移植性不是很好,建议使用 memset 函数代替】
返回值:无返回值
# 21.malloc (不清空内存值)
void *malloc(unsigned int num_bytes); |
参数:
num_byte
为要申请的空间大小
作用:
在内存的动态存储区中分配一块长度为 size 字节的 连续
区域,参数 size 为需要内存空间的长度,返回该区域的首地址(首地址指的是 data 域)
函数malloc不能初始化所分配的内存空间
,这意味着我们申请的空间里面的数据仍然保留着(也就是可以通过这种方式泄露里面的数据)
# 22.calloc(清空内存值)
void *calloc(size_t n, size_t size); |
参数:
其比 malloc 函数多一个参数,比如如果他要申请 20 个 int 类型空间: int *p = (int *)calloc(20, sizeof(int))
作用:
与 malloc 相似,参数 sizeof(int)
为申请地址的单位元素长度, size_t n
为元素个数,即在内存中申请 sizeof(int)*size_t n
字节大小的连续地址空间
malloc 申请后空间的值是原来申请前的,并没有进行初始化,而 calloc 却在申请后,对空间逐一进行 初始化
,并设置值为 0
# 23.realloc (改变已申请的空间的大小)
void realloc(void *ptr, size_t new_Size) |
参数:
ptr 为指向原来空间基址的指针, new_size 为接下来需要重新分配空间的大小
作用:
给一个已经分配了地址的指针重新分配空间,参数 ptr 为原有的空间地址,new_size 是重新申请的地址长度.
用于对动态内存进行扩容 (及已申请的动态空间不够使用,需要进行空间扩容操作
进一步分析:
如果size较小,原来申请的动态内存后面还有空余内存,系统将直接在原内存空间后面扩容,并返回原动态空间基地址;如果size较大,原来申请的空间后面没有足够大的空间扩容,系统将重新申请一块"原来的空间大小+新申请的空间大小"的内存,并把原来空间的内容拷贝过去,原来空间free;如果size非常大,系统内存申请失败,返回NULL,原来的内存不会释放。注意:如果扩容后的内存空间较原空间小,将会出现数据丢失,如果直接realloc(p, 0);相当于free(p).
# 24 brk 与 sbrk
// 函数原型: | |
#include<unistd.h> | |
int brk(void * addr); | |
void * sbrk(intptr_t increment); |
brk () 和 sbrk () 改变 程序间断点
的位置 (申请内存空间的起始处)。程序间断点就是程序数据段的结尾。(程序间断点是为初始化数据段的起始位置). 通过增加程序间断点进程可以更有效的申请内存 。当 addr 参数合理、系统有足够的内存并且不超过最大值时 brk () 函数将数据段结尾设置为 addr, 即间断点设置为 addr。sbrk () 将程序数据空间增加 increment字节
。当 increment
为 0 时则返回程序间断点的当前位置。
也就说通过对间断点的设置来分配空间和回收内存,两个函数都能够改变间断点
返回值:
brk () 成功返回 0,失败返回 - 1 并且设置 errno 值为 ENOMEM(注:在 mmap 中会提到)。
sbrk () 成功返回之前的程序间断点地址。如果间断点值增加,那么这个指针(指的是返回的之前的间断点地址)是指向分配的新的内存的首地址。如果出错失败,就返回一个指针并设置 errno 全局变量的值为 ENOMEM。
这两个函数都用来改变 “program break” (程序间断点) 的位置,改变数据段长度(Change data segment size),实现虚拟内存到物理内存的映射。
brk () 函数直接修改有效访问范围的末尾地址实现分配与回收。sbrk () 参数函数中:当 increment 为正值时,间断点位置向后移动 increment 字节。同时返回移动之前的位置,相当于分配内存。当 increment 为负值时,位置向前移动 increment 字节,相当与于释放内存,其返回值没有实际意义。当 increment 为 0 时,不移动位置只返回当前位置。参数 increment 的符号决定了是分配还是回收内存
http://t.csdnimg.cn/cIGqZ
# 25.fseek 函数()
int fseek(FILE *stream, long int offset, int whence) |
作用:
C 库函数 int fseek (FILE *stream, long int offset, int whence) 设置流 stream 的文件位置为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数【就是将指向文件具体位置的指针给修改位置使指向设定的偏移处】
参数:
- stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
- offset -- 这是相对 whence 的偏移量,以字节为单位。
- whence -- 这是表示开始添加偏移 offset 的位置。它一般指定为下列常量之一
常量 | 描述
SEEK_SET | 文件的开头
SEEK_CUR | 文件指针的当前位置
SEEK_END | 文件的末尾
返回值:
如果成功,则该函数返回零,否则返回非零值。
# 26.ftell 函数
long int ftell(FILE *stream) |
作用:
C 库函数 long int ftell (FILE *stream) 返回给定流 stream 的当前文件位置。【上面的 fseek 修改了位置,这里可以用来返回】
参数:
stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流
返回值:
该函数返回位置标识符的当前值。如果发生错误,则返回 -1L,全局变量 errno 被设置为一个正值
# 27. 空类型 (void)
通常用于以下三种情况
- 函数返回空:函数不返回值,或者返回空,例如 void exit (int status);
- 函数参数为空:函数不接受任何参数,不带参数的函数可以接受一个 void。例如 int rand (void);
- 指针指向 void:类型为 void * 的指针代表
对象的地址
,而不是类型
。例如,内存分配函数 void *malloc (size_t size); 返回指向 void 的指针,可以转换为任何数据类型。
# 28.pthread_creat 函数(创建子线程)、
pthread_create () 函数用来创建线程
int pthread_create(pthread_t *thread, | |
const pthread_attr_t *attr, | |
void *(*start_routine) (void *), | |
void *arg); |
各个参数的含义是:
-
pthread_t *thread
:传递一个pthread_t
类型的指针变量,也可以直接传递某个pthread_t
类型变量的地址。pthread_t
是一种用于表示线程的数据类型,每一个pthread_t
类型的变量都可以表示一个线程。 -
const pthread_attr_t *attr:
用于手动设置新建线程的属性,例如线程的调用策略、线程所能使用的栈内存的大小等。大部分场景中,我们都不需要手动修改线程的属性,将 attr 参数赋值为NULL
,pthread_create()
函数会采用系统默认的属性值创建线程。
pthread_attr_t
类型以结构体的形式定义在 <pthread.h> 头文件中,此类型的变量专门表示线程的属性。
-
void *(*start_routine) (void *):
以函数指针的方式指明新建线程需要执行的函数,该函数的参数最多有 1 个(可以省略不写),形参和返回值的类型都必须为 void* 类型。void* 类型又称空指针类型,表明指针所指数据的类型是未知的。使用此类型指针时,我们通常需要先对其进行强制类型转换,然后才能正常访问指针指向的数据。如果该函数有返回值,则线程执行完函数后,函数的返回值可以由 pthread_join () 函数接收。
-
void *arg
:指定传递给start_routine
函数的实参,当不需要传递任何数据时,将arg
赋值为NULL
即可。
返回值:
如果成功创建线程, pthread_create()
函数返回数字 0,反之返回非零值。各个非零值都对应着不同的宏,指明创建失败的原因,常见的宏有以下几种:
- EAGAIN:系统资源不足,无法提供创建线程所需的资源。
- EINVAL:传递给
pthread_create(
) 函数的 attr 参数无效。 - EPERM:传递给
pthread_create()
函数的 attr 参数中,某些属性的设置为非法操作,程序没有相关的设置权限
以上这些宏都声明在 <errno.h> 头文件中,如果程序中想使用这些宏,需提前引入此头文件
注意:
pthread_create () 函数成功创建的线程会自动执行指定的函数,不需要手动开启。为了确保创建的线程能在主线程之前执行完,程序中可以调用 sleep () 函数延缓了主线程的执行速度。否则整个进程会随着主线程执行结束而立即终止:由于主线程执行太快,子线程可能尚未执行完就被强制终止
https://c.biancheng.net/view/8607.html
# 29.pthread_join () 函数 【等待线程执行结束】
pthread_join () 函数声明在 < pthread.h> 头文件中,语法格式如下
int pthread_join(pthread_t thread, void ** retval); |
thread 参数用于指定接收哪个线程的返回值;retval 参数表示接收到的返回值,如果 thread
线程没有返回值,又或者我们不需要接收 thread
线程的返回值,可以将 retval
参数置为 NULL。
pthread_join()
函数会一直阻塞调用它的线程,直至目标线程执行结束(接收到目标线程的返回值),阻塞状态才会解除。如果 pthread_join()
函数成功等到了目标线程执行结束(成功获取到目标线程的返回值),返回值为数字 0;反之如果执行失败,函数会根据失败原因返回相应的非零值,每个非零值都对应着不同的宏,例如:
- EDEADLK:检测到线程发生了死锁。
- EINVAL:分为两种情况,要么目标线程本身不允许其它线程获取它的返回值,要么事先就已经有线程调用
pthread_join()
函数获取到了目标线程的返回值。 - ESRCH:找不到指定的 thread 线程。
再次强调,一个线程执行结束的返回值只能由一个 pthread_join()
函数获取,当有多个线程调用 pthread_join()
函数获取同一个线程的执行结果时,哪个线程最先执行 pthread_join()
函数,执行结果就由那个线程获得,其它线程的 pthread_join()
函数都将执行失败。
对于一个默认属性的线程 A 来说,线程占用的资源并不会因为 执行结束
(线程自己执行结束) 而得到释放。而通过在其它线程中执行 pthread_join(A,NULL);
语句,可以轻松实现 “及时释放线程 A 所占资源” 的目的
# 30. 关于指针
取值运算( *p
)返回保存在 内存地址为
p 的内存空间中的值【 *p
时 p 本身会被看作为一个地址】。取地址(&p)运算则返回操作数 p 的内存地址
https://zh.wikipedia.org/zh-hans/ 指標_(電腦科學)