Java并发编程学习18-线程池的使用(递归算法的并行化改进)

引言

上篇介绍了 ThreadPoolExecutor 配置和扩展相关的信息,本篇开始将介绍递归算法的并行化。

还记得我们在《Java并发编程学习11-任务执行演示》中,对页面绘制程序进行一系列改进,这些改进大大地提供了页面绘制的并行性。

Java并发编程学习17-线程池的使用(配置和扩展ThreadPoolExecutor)

引言

上篇分析了在使用任务执行框架时需要注意的各种情况,并简单介绍了如何正确调整线程池大小。

Java并发编程学习16-探究任务和执行策略间的隐性耦合,解锁线程池大小设置的正确姿势

引言

前面的章节介绍了任务执行框架及其实际应用的一些内容。

本篇开始将分析在使用任务执行框架时需要注意的各种情况,并介绍对线程池进行配置与调优的一些方法。

C语言函数大全--p 开头的函数

总览

函数声明 函数功能
void perror(const char *s); 用于将当前错误码对应的错误信息打印到标准输出设备(通常是终端)。
void pieslice(int x, int y, int stanle, int endangle, int radius); 在图形模式下绘制并填充一个扇形(饼图切片)
double pow(double x, double y); 用于计算 x 的 y 次幂,并返回结果(double)
float powf(float x, float y); 用于计算 x 的 y 次幂,并返回结果(float)
long double powl(long double x, long double y); 用于计算 x 的 y 次幂,并返回结果(long double)
double pow10(double x); 用于计算 $10^x$ (double)
float pow10f(float x); 用于计算 $10^x$(float)
long double pow10l(long double x); 用于计算 $10^x$ (long double)
int printf(const char *format, ...); 它是标准 I/O 库中定义的函数,用于将格式化的数据输出到标准输出流(通常是控制台窗口)
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); 创建一个新的线程
int pthread_join(pthread_t thread, void **retval); 阻塞当前线程,等待指定的线程结束。
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); 初始化互斥锁。返回值为 0 表示成功,否则表示失败。
int pthread_mutex_destroy(pthread_mutex_t *mutex); 销毁互斥锁。返回值为 0 表示成功,否则表示失败。
int pthread_mutex_lock(pthread_mutex_t *mutex); 尝试获取互斥锁,如果已经被占用,则会阻塞当前线程。返回值为 0 表示成功获取互斥锁,否则表示失败。
int pthread_mutex_unlock(pthread_mutex_t *mutex); 释放互斥锁。
int putc(int c, FILE *stream); 它是标准 I/O 库中定义的函数,用于将一个字符输出到指定的文件流。
int putchar(int c); 它是标准 I/O 库中定义的函数,用于将一个字符输出到标准输出流(通常是控制台窗口)。
int putenv(char *string); 用于设置环境变量的值。
void putimage(int left, int top, void *bitmap, int op); 用于将位图图像输出到指定的窗口或屏幕上
void putpixel(int x, int y, int color); 用于将一个像素点画在指定的位置上
int puts(const char *s); 用于将一个字符串输出到标准输出流
int putw(int w, FILE *stream); 用于将一个整数值以二进制形式写入到指定的文件中。

1. perror

1.1 函数说明

函数声明 函数功能
void perror(const char *s); 用于将当前错误码对应的错误信息打印到标准输出设备(通常是终端)。

参数:

  • s : 用于描述错误类型或者出错的上下文信息。它会在该信息后面追加当前错误码对应的错误信息,并且自动换行。

1.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <errno.h>

int main() {
FILE *fp = fopen("tempfile.txt", "r");
if (fp == NULL) {
perror("Failed to open file");
printf("Error code: %d\n", errno);
}
return 0;
}

在上述的示例中,

  • 我们首先尝试打开一个不存在的文件,并通过 fopen() 函数返回的值来检查是否成功。由于该文件不存在,fopen() 函数将返回 NULL
  • 然后我们可以使用 perror() 函数来输出一个错误消息和具体的错误信息,以帮助我们找到问题所在。如果发生了错误,errno 全局变量会被设置为一个非零值,我们还可以使用它来获取具体的错误代码。

1.3 运行结果

2. pieslice

2.1 函数说明

函数声明 函数功能
void pieslice(int x, int y, int stanle, int endangle, int radius); 在图形模式下绘制并填充一个扇形(饼图切片)

参数:

  • x : 圆心的x 坐标
  • y : 圆心的y 坐标
  • startangle : 圆弧的起始角度(以度为单位)
  • endangle : 圆弧的结束角度(以度为单位)
    • radius : 圆的半径

2.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <graphics.h>

int main() {
int gdriver = DETECT, gmode;
int midx, midy;
int stangle = 45, endangle = 135, radius = 100;

initgraph(&gdriver, &gmode, ""); // 初始化图形窗口

midx = getmaxx() / 2;
midy = getmaxy() / 2;

setcolor(YELLOW); // 设置画笔颜色为黄色
setfillstyle(EMPTY_FILL, getmaxcolor()); // 设置填充样式。
// 绘制一个半径为100像素、起始角度为45度、结束角度为135度的圆弧
pieslice(midx, midy, stangle, endangle, radius);

getch(); // 等待用户按键
closegraph(); // 关闭图形窗口
return 0;
}

2.3 运行结果

3. pow,powf,powl

3.1 函数说明

函数声明 函数功能
double pow(double x, double y); 用于计算 x 的 y 次幂,并返回结果(double)
float powf(float x, float y); 用于计算 x 的 y 次幂,并返回结果(float)
long double powl(long double x, long double y); 用于计算 x 的 y 次幂,并返回结果(long double)

3.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <math.h>

int main() {
double result = pow(2.0, 3.5);
printf("The result of pow(2.0, 3.5) is: %.20lf\n", result);

float resultf = powf(2.0f, 3.5f);
printf("The result of powf(2.0f, 3.5f) is: %.20f\n", resultf);

long double resultL = powl(2.0L, 3.5L);
printf("The result of powl(2.0L, 3.5L) is: %.20Lf\n", resultL);
return 0;
}

3.3 运行结果

4. pow10,pow10f,pow10l

4.1 函数说明

函数声明 函数功能
double pow10(double x); 用于计算 $10^x$ (double)
float pow10f(float x); 用于计算 $10^x$(float)
long double pow10l(long double x); 用于计算 $10^x$ (long double)

4.2 演示示例

1
2
3
4
5
6
7
8
#include <stdio.h>
#include <math.h>

int main() {
double result = pow10(-2.0);
printf("The result is: %lf\n", result);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <cmath>

int main() {
float result = std::pow10f(-2.0f);
std::cout << "10^-2 = " << result << std::endl;

long double result = std::pow10l(-2.0L);
std::cout << "10^-2 = " << result << std::endl;

return 0;
}

5. printf

5.1 函数说明

函数声明 函数功能
int printf(const char *format, ...); 它是标准 I/O 库中定义的函数,用于将格式化的数据输出到标准输出流(通常是控制台窗口)
参数:
  • format : 一个格式化字符串
  • 可变参数 : 任意数量,这些可变参数会根据格式化字符串进行格式化,并被输出到标准输出流。

返回值 :

  • 成功打印的字符数。

5.2 演示示例

1
2
3
4
5
6
7
8
#include <stdio.h>

int main() {
printf("Hello, world!\n");
char name[20] = "Huazie";
printf("My name is %s", name);
return 0;
}

5.3 运行结果

6. pthread_create,pthread_join

6.1 函数说明

函数声明 函数功能
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); 创建一个新的线程
int pthread_join(pthread_t thread, void **retval); 阻塞当前线程,等待指定的线程结束。

pthread_create 参数:

  • thread : 指向线程标识符的指针,用于返回新创建线程的标识符
  • attr : 指向线程属性结构体的指针,用于指定线程的属性,通常为 NULL 表示使用默认属性
  • start_routine : 指向线程函数的指针,用于指定线程的入口点
  • arg : 传递给线程函数的参数,可以为任意类型的指针。

pthread_join 参数:

  • pthread_t thread :目标线程的标识符(由 pthread_create() 返回)。必须是已创建且未被分离(non-detached)的线程。
  • void **retval :用于接收目标线程的退出状态值的指针的地址。
    • 若目标线程通过 return 返回值或调用 pthread_exit(void *retval) 终止,retval 将指向该值。
    • 若不需要获取退出状态,可设为 NULL
    • 若目标线程被取消,retval 会被设为 PTHREAD_CANCELED(通常为 -1)。

6.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <pthread.h>

void *thread_func(void *arg) {
printf("Hello from the new thread!\n");
return NULL;
}

int main() {
pthread_t thread;
int ret = pthread_create(&thread, NULL, thread_func, NULL);
if (ret != 0) {
printf("Failed to create thread!\n");
return -1;
}

pthread_join(thread, NULL);

printf("Hello from the main thread!\n");
return 0;
}

在上述的示例中,主线程调用了 pthread_create() 函数来创建一个新的线程,并传递一个函数指针 thread_func 作为新线程的入口点。新线程运行这个函数,并输出一条消息。主线程等待新线程结束,然后继续运行自己的代码。

6.3 运行结果

7. pthread_mutex_init,pthread_mutex_destroy,pthread_mutex_lock,pthread_mutex_unlock

7.1 函数说明

函数声明 函数功能
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); 初始化互斥锁。返回值为 0 表示成功,否则表示失败。
int pthread_mutex_destroy(pthread_mutex_t *mutex); 销毁互斥锁。返回值为 0 表示成功,否则表示失败。
int pthread_mutex_lock(pthread_mutex_t *mutex); 尝试获取互斥锁,如果已经被占用,则会阻塞当前线程。返回值为 0 表示成功获取互斥锁,否则表示失败。
int pthread_mutex_unlock(pthread_mutex_t *mutex); 释放互斥锁。

参数:

  • mutex : 指向互斥锁对象的指针。
  • attr : 指向线程属性结构体的指针,用于指定互斥锁对象的属性,通常为 NULL 表示使用默认属性。

7.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h>
#include <pthread.h>

pthread_mutex_t mutex;

void *thread_func(void *arg) {
printf("Thread %d: Attempting to lock the mutex.\n", (int)arg);

pthread_mutex_lock(&mutex);

printf("Thread %d: Locked the mutex!\n", (int)arg);
// ... Critical section code ...
printf("Thread %d: Unlocking the mutex.\n", (int)arg);

pthread_mutex_unlock(&mutex);

return NULL;
}

int main() {
pthread_t thread1, thread2;
int ret;

ret = pthread_mutex_init(&mutex, NULL);
if (ret != 0) {
printf("Failed to initialize mutex!\n");
return -1;
}

ret = pthread_create(&thread1, NULL, thread_func, (void *)1);
if (ret != 0) {
printf("Failed to create thread 1!\n");
return -1;
}

ret = pthread_create(&thread2, NULL, thread_func, (void *)2);
if (ret != 0) {
printf("Failed to create thread 2!\n");
return -1;
}

pthread_join(thread1, NULL);
pthread_join(thread2, NULL);

pthread_mutex_destroy(&mutex);

return 0;
}

在上述的示例中,两个线程分别调用了 pthread_mutex_lock()pthread_mutex_unlock() 函数来保护临界区代码,以确保同时只有一个。

7.3 运行结果

8. putc

8.1 函数说明

函数声明 函数功能
int putc(int c, FILE *stream); 它是标准 I/O 库中定义的函数,用于将一个字符输出到指定的文件流。

参数:

  • c : 要输出的字符
  • stream : 指向目标文件流的指针

返回值:

  • 成功输出的字符的 ASCII 码

8.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

int main() {
FILE *fp;
char *str = "Hello, world!\n";

fp = fopen("output.txt", "w");
if (fp == NULL) {
printf("Failed to open file for writing!\n");
return -1;
}

int i = 0;
while (str[i] != '\0') {
putc(str[i], fp);
i++;
}

fclose(fp);

return 0;
}

在上述的示例中,我们打开名为 output.txt 的文件以供写入,并使用 putc() 函数将字符串中的每个字符逐一输出到文件中。最后,我们关闭文件。

注意:putchar() 函数类似,putc() 函数可以用来逐一输出字符串中的每个字符,但通常会更多地用于将数据写入文件或其他输出流。

8.3 运行结果

9. putchar

9.1 函数说明

函数声明 函数功能
int putchar(int c); 它是标准 I/O 库中定义的函数,用于将一个字符输出到标准输出流(通常是控制台窗口)。

参数:

  • c : 要输出的字符

返回值 :

  • 成功输出,则返回该字符的 ASCII 码值

9.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>

int main() {
putchar('H');
putchar('e');
putchar('l');
putchar('l');
putchar('o');
putchar(',');
putchar(' ');
putchar('w');
putchar('o');
putchar('r');
putchar('l');
putchar('d');
putchar('!');
putchar('\n');
return 0;
}

注意: 单引号用于表示字符常量,例如 ‘H’ 表示字符 H 的 ASCII 码。

9.3 运行结果

10. putenv

10.1 函数说明

函数声明 函数功能
int putenv(char *string); 用于设置环境变量的值。

参数:

  • string : 字符串参数,格式为 name=value,其中 name 是要设置的环境变量的名称,value 是要为其设置的值。

返回值:

  • 0 表示成功;
  • 0 表示失败。

10.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stdlib.h>

int main() {
char *var = "MY_VAR=hello";
int ret = putenv(var);
if (ret != 0) {
printf("Failed to set environment variable!\n");
return -1;
}

char *val = getenv("MY_VAR");
printf("The value of MY_VAR is: %s\n", val);

return 0;
}

在上述的示例中,

  • 我们首先使用 putenv() 函数将一个名为 MY_VAR 的环境变量设置为字符串 "hello"
  • 然后,我们使用 getenv() 函数获取 MY_VAR 环境变量的值;
  • 最后使用 printf() 函数输出它。

注意: 由于 putenv() 函数接受一个指向可变字符串的指针,因此应该避免将局部变量的地址传递给它,以免在函数返回后出现未定义行为。

10.3 运行结果

11. putimage

11.1 函数说明

函数声明 函数功能
void putimage(int left, int top, void *bitmap, int op); 用于将位图图像输出到指定的窗口或屏幕上

参数:

  • left: 表示位图左上角的 x 坐标
  • top: 表示位图左上角的 y 坐标
  • bitmap: 一个指向位图数据的指针,这个数据必须按照指定格式存储
  • op: 表示绘制位图的方式,可以为以下值之一:
    • COPY_PUT:覆盖模式,即将位图完全覆盖到目标位置。
    • AND_PUT:按位与模式,即将位图与目标位置进行按位与运算后输出。
    • OR_PUT:按位或模式,即将位图与目标位置进行按位或运算后输出。
    • XOR_PUT:按位异或模式,即将位图与目标位置进行按位异或运算后输出。

11.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <graphics.h>

#define ARROW_SIZE 10

void draw_arrow(int x, int y);

int main(void)
{
int gdriver = DETECT, gmode;
void *arrow;
int x, y, maxx;
unsigned int size;

initgraph(&gdriver, &gmode, "");

maxx = getmaxx();
x = 0;
y = getmaxy() / 2;

draw_arrow(x, y);

size = imagesize(x, y-ARROW_SIZE, x+(4*ARROW_SIZE), y+ARROW_SIZE);

arrow = malloc(size);

getimage(x, y-ARROW_SIZE, x+(4*ARROW_SIZE), y+ARROW_SIZE, arrow);

while (!kbhit())
{
putimage(x, y-ARROW_SIZE, arrow, XOR_PUT);

x += ARROW_SIZE;
if (x >= maxx)
x = 0;

putimage(x, y-ARROW_SIZE, arrow, XOR_PUT);
}

free(arrow);
closegraph();
return 0;
}

void draw_arrow(int x, int y)
{
moveto(x, y);
linerel(4*ARROW_SIZE, 0);
linerel(-2*ARROW_SIZE, -1*ARROW_SIZE);
linerel(0, 2*ARROW_SIZE);
linerel(2*ARROW_SIZE, -1*ARROW_SIZE);
}

上述程序使用了 graphics.h 图形库来画一个箭头,并在屏幕上循环移动它。

  • 首先,程序初始化图形库,然后调用 getmaxx()getmaxy() 函数获取屏幕的宽度和高度。
  • 接着,程序调用 draw_arrow() 函数来绘制箭头图形,并使用 imagesize() 函数计算出需要分配给位图数据的内存大小。
  • 接下来,程序使用 malloc() 函数动态分配了一块内存空间,并调用 getimage() 函数获取箭头图像的位图数据。该函数会将指定位置的屏幕区域保存到指定的内存地址中。
  • 最后,程序进入一个循环,在每次迭代中先使用 putimage() 函数将之前存储的箭头图像覆盖掉屏幕上的箭头,然后将箭头向右移动一定的距离。当用户按下任意键时,程序结束循环并退出。

11.3 运行结果

12. putpixel

12.1 函数说明

函数声明 函数功能
void putpixel(int x, int y, int color); 用于将一个像素点画在指定的位置上

参数:

  • x: 表示要绘制像素的 x 坐标
  • y: 表示要绘制像素的 y 坐标
  • color: 表示要绘制的像素颜色,通常为一个整数值。

12.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <graphics.h>
#include <time.h>

#define PIXEL_COUNT 1000
#define DELAY_TIME 100

int main(void)
{
int gdriver = DETECT, gmode;
int i, x, y, color, maxx, maxy, maxcolor, seed;

initgraph(&gdriver, &gmode, "");

maxx = getmaxx() + 1;
maxy = getmaxy() + 1;
maxcolor = getmaxcolor() + 1;

while (!kbhit())
{
srand((unsigned)time(NULL));
for (i=0; i<PIXEL_COUNT; i++)
{
x = rand() % maxx + 1;
y = rand() % maxy + 1;
color = rand() % maxcolor + 1;
putpixel(x, y, color);
}

delay(DELAY_TIME);
}

getch();
closegraph();
return 0;
}

上述这个程序使用 graphics.h 图形库在屏幕上绘制随机像素点,并不断更新它们的颜色。

  • 首先,程序初始化图形库,并调用 getmaxx()getmaxy()getmaxcolor() 函数获取屏幕的尺寸和颜色范围。
  • 接着,程序进入一个循环,在每次迭代中使用 rand() 函数生成随机的坐标和颜色值,并使用 putpixel() 函数绘制对应位置的像素点。另外还使用了 delay() 函数来控制每次更新之间的时间间隔,该函数会使程序暂停指定的毫秒数,以便让用户观察到像素点的变化效果。
  • 最后当用户按下任意键时,程序退出循环并结束执行。

12.3 运行结果

13. puts

13.1 函数说明

函数声明 函数功能
int puts(const char *s); 用于将一个字符串输出到标准输出流

参数:

  • s: 要输出的以 '\0' 结尾的字符串

返回值:

  • 成功输出的字符数(包括换行符)

13.2 演示示例

1
2
3
4
5
6
7
#include <stdio.h>

int main() {
char *str = "Hello, world!";
puts(str);
return 0;
}

注意: 在输出字符串时,puts() 函数会自动将字符串的内容和换行符写入到标准输出流中,因此不需要再进行额外的操作。如果要将字符串输出到文件或其他输出流中,则可以使用 fputs() 函数。

13.3 运行结果

14. putw

14.1 函数说明

函数声明 函数功能
int putw(int w, FILE *stream); 用于将一个整数值以二进制形式写入到指定的文件中。

参数:

  • w : 要写入的整数值
  • stream : 指向要写入的文件的指针。

返回值:

  • 如果成功,则返回写入的字节数(通常为 4 个字节);
  • 如果发生错误,则返回 EOF

14.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

int main() {
int num = 42;
FILE *fp = fopen("output.bin", "wb");

putw(num, fp);

fclose(fp);
return 0;
}

在上述的示例中,

  • 我们首先定义了一个整数变量,并使用 fopen() 函数打开一个名为 "output.bin" 的二进制文件;
  • 接着,我们调用 putw() 函数将整数值写入到该文件中;
  • 最后关闭文件并结束程序。

注意: 由于 putw() 函数是以二进制形式写入数据的,因此相对于文本文件而言,它更适合用于存储数字、结构体等复杂类型的数据。如果要将字符串或其他文本数据写入到文件中,则可以使用 fprintf() 函数。

14.3 运行结果

参考

  1. [API Reference Document]

C语言函数大全--o 开头的函数

总览

函数声明 函数功能
void obstack_init(struct obstack *obstack_ptr); 它是 POSIX 标准库中的一个非标准函数,用于初始化对象堆栈。对象堆栈是一种可以动态增长以存储任意类型的对象的数据结构。其中,obstack_ptr 参数是一个指向 struct obstack 类型的指针,表示要初始化的对象堆栈。
void obstack_free(struct obstack *obstack_ptr, void *object_ptr); 用于释放通过对象堆栈分配的所有内存。其中,obstack_ptr 参数是一个指向 struct obstack 类型的指针,表示要释放的对象堆栈;object_ptr 参数是要释放的内存块。
void *obstack_alloc(struct obstack *obstack_ptr, int size); 用于从对象堆栈中分配指定大小的内存,并返回其地址。其中,obstack_ptr 参数是一个指向 struct obstack 类型的指针,表示要从中分配内存的对象堆栈;size 参数表示要分配的内存块的大小。
void *obstack_blank(struct obstack *obstack_ptr, int size); 用于向对象堆栈添加指定数量的空间,并返回指向添加的第一个字节的指针。其中,obstack_ptr 参数是一个指向 struct obstack 类型的指针,表示要添加空间的对象堆栈;size 参数表示要添加的空间大小。
void *obstack_grow(struct obstack *obstack_ptr, const void *data, int size); 用于将数据复制到对象堆栈,并返回指向添加的第一个字节的指针。其中,obstack_ptr 参数是一个指向 struct obstack 类型的指针,表示要添加数据的对象堆栈;data 参数是要复制的数据的指针;size 参数表示要复制的数据的大小。
#define offsetof(type, member) ((size_t)(&((type *)0)->member)) 它是一个宏,用于获取结构体中某个成员的偏移量。
int open(const char *path, int oflag, ...); 用于打开文件
int openat(int dirfd, const char *pathname, int flags, mode_t mode); 它是 Linux 系统定义的一个函数,它可以打开一个相对于指定目录的文件。与 open() 函数相比,openat() 函数更加灵活,并支持更多的选项。
DIR *opendir(const char *name); 它是 POSIX 标准定义的一个函数,用于打开目录并返回一个指向 DIR 结构体类型的指针。
int openpty(int *amaster, int *aslave, char *name, const struct termios *termp, const struct winsize *winp); 它是 POSIX 标准定义的一个函数,用于打开一个伪终端(PTY)并返回与之关联的主从设备文件描述符。伪终端可以用于在进程之间建立通信,或者在程序中模拟终端行为。
int on_exit(void (*function)(int, void *), void *arg); 它是 POSIX 标准定义的一个函数,用于在进程退出时调用注册的回调函数。这个函数可以用于在程序异常退出或者正常退出时执行一些清理工作、记录日志等操作
void outtext(char *textstring); 在图形视区显示一个字符串
void outtextxy(int x, int y, char *textstring); 在指定位置显示一字符串

1. obstack_init,obstack_free,obstack_alloc,obstack_blank,obstack_grow

1.1 函数说明

函数声明 函数功能
void obstack_init(struct obstack *obstack_ptr); 它是 POSIX 标准库中的一个非标准函数,用于初始化对象堆栈。对象堆栈是一种可以动态增长以存储任意类型的对象的数据结构。其中,obstack_ptr 参数是一个指向 struct obstack 类型的指针,表示要初始化的对象堆栈。
void obstack_free(struct obstack *obstack_ptr, void *object_ptr); 用于释放通过对象堆栈分配的所有内存。其中,obstack_ptr 参数是一个指向 struct obstack 类型的指针,表示要释放的对象堆栈;object_ptr 参数是要释放的内存块。
void *obstack_alloc(struct obstack *obstack_ptr, int size); 用于从对象堆栈中分配指定大小的内存,并返回其地址。其中,obstack_ptr 参数是一个指向 struct obstack 类型的指针,表示要从中分配内存的对象堆栈;size 参数表示要分配的内存块的大小。
void *obstack_blank(struct obstack *obstack_ptr, int size); 用于向对象堆栈添加指定数量的空间,并返回指向添加的第一个字节的指针。其中,obstack_ptr 参数是一个指向 struct obstack 类型的指针,表示要添加空间的对象堆栈;size 参数表示要添加的空间大小。
void *obstack_grow(struct obstack *obstack_ptr, const void *data, int size); 用于将数据复制到对象堆栈,并返回指向添加的第一个字节的指针。其中,obstack_ptr 参数是一个指向 struct obstack 类型的指针,表示要添加数据的对象堆栈;data 参数是要复制的数据的指针;size 参数表示要复制的数据的大小。

1.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <obstack.h>

int main(void)
{
struct obstack my_obstack;
const char *str1 = "Hello, ";
const char *str2 = "World!";
char *dst;

obstack_init(&my_obstack);

dst = (char *)obstack_alloc(&my_obstack, strlen(str1) + strlen(str2) + 1);
strcpy(dst, str1);
strcat(dst, str2);

printf("%s\n", (char *)my_obstack.chunk);

dst = (char *)obstack_blank(&my_obstack, sizeof(int)*2);
int a = 100;
int b = 200;
memcpy(dst, &a, sizeof(int));
memcpy(dst+sizeof(int), &b, sizeof(int));

printf("%d %d\n", *(int *)(my_obstack.next_free-sizeof(int)*2), *(int *)(my_obstack.next_free-sizeof(int)));

double d = 3.1415926;
dst = (char *)obstack_grow(&my_obstack, &d, sizeof(double));

printf("%f\n", *(double *)(my_obstack.next_free-sizeof(double)));

obstack_free(&my_obstack, NULL);

return 0;
}

在上述的程序中,

  • 我们首先定义一个名为 my_obstackstruct obstack 类型变量,并将其传递给 obstack_init() 函数以初始化对象堆栈。
  • 接着,我们使用 obstack_alloc() 函数从对象堆栈中分配一块内存,并将两个字符串连接起来。
  • 然后,我们使用 obstack_blank() 函数向对象堆栈添加一块指定大小的空间,并使用 memcpy() 函数将两个整数复制到该空间中。
  • 接下来,我们使用 obstack_grow() 函数向对象堆栈添加一个双精度浮点数,并返回指向该浮点数的指针。
  • 最后,我们使用 printf() 函数将连接后的字符串、添加的整数和添加的双精度浮点数输出到终端,并使用 obstack_free() 函数释放通过对象堆栈分配的所有内存。

注意:在使用 obstack_blank() 函数向对象堆栈添加空间时,建议使用 sizeof 运算符来计算要添加的空间大小,并在使用 memcpy() 复制数据时也应该小心不要越界。同时,在使用 obstack_grow() 函数向对象堆栈添加数据时,需要小心指针是否正确,并且操作前需要先使用 memcpy() 将要添加的数据复制到一个临时变量中。

2. offsetof

2.1 宏说明

宏定义 宏描述
#define offsetof(type, member) ((size_t)(&((type *)0)->member)) 它是一个宏,用于获取结构体中某个成员的偏移量。

参数:

  • type : 表示结构体类型
  • member : 表示结构体中的一个成员变量名

返回值: 一个 size_t 类型的值,表示该成员变量在结构体中的偏移量(单位是字节)。

2.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stddef.h>

struct example {
int a;
char b;
double c;
};

int main(void)
{
size_t offset_b = offsetof(struct example, b);
printf("Offset of 'b' in struct example: %zu\n", offset_b);

return 0;
}

在这个程序中,

  • 我们定义了一个名为 example 的结构体类型,并使用 offsetof 宏获取结构体中成员变量 b 的偏移量。
  • 最后,我们使用 printf() 函数将结果输出到终端。

注意: 在使用 offsetof 宏时,结构体类型名称必须使用括号括起来,否则代码会产生语法错误。此外,offsetof 宏的参数必须是已定义的结构体类型名称和该结构体类型中的成员变量名称,否则也会导致编译错误。

3. open

3.1 函数说明

函数声明 函数功能
int open(const char *path, int oflag, ...); 用于打开文件

参数:

  • path : 表示要打开的文件路径
  • oflag : 表示打开文件时的选项标志,可以为以下常量之一或多个按位或组合而成:
    • O_RDONLY:只读模式打开文件。
    • O_WRONLY:只写模式打开文件。
    • O_RDWR:读写模式打开文件。
    • O_CREAT:如果文件不存在,则创建它。
    • O_TRUNC:如果文件已存在,则将其长度截断为 0。
    • O_APPEND:在文件末尾追加数据。
  • 可选参数 : 表示文件所有者、组和其他用户的访问权限。如果使用了 O_CREAT 选项,则必须提供这个参数

3.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(void)
{
int fd = open("temp.txt", O_RDONLY);
if (fd == -1) {
perror("open");
exit(1);
}

char buf[1024];
ssize_t nread;
while ((nread = read(fd, buf, sizeof(buf))) > 0) {
if (write(STDOUT_FILENO, buf, nread) != nread) {
perror("write");
exit(1);
}
}

if (nread == -1) {
perror("read");
exit(1);
}

if (close(fd) == -1) {
perror("close");
exit(1);
}

return 0;
}

在上述的程序中,

  • 我们使用 open() 函数打开文件 temp.txt,并通过 read() 函数读取其中的数据。
  • 然后,我们使用 write() 函数将数据写入标准输出,直到读取完整个文件。
  • 最后,我们使用 close() 函数关闭文件。

注意: 在使用 open() 函数打开文件时,返回值为负数则表示出现了错误。此时可以使用 perror() 函数输出错误信息,并使用 exit() 函数退出程序。同时,在使用 read() 函数和 write() 函数读写文件时也需要小心处理返回值,以避免出现不可预期的错误。

3.3 运行结果

4. openat

4.1 函数说明

函数声明 函数功能
int openat(int dirfd, const char *pathname, int flags, mode_t mode); 它是 Linux 系统定义的一个函数,它可以打开一个相对于指定目录的文件。与 open() 函数相比,openat() 函数更加灵活,并支持更多的选项。

参数:

  • dirfd : 表示要打开文件所在目录的文件描述符。如果传递的是 AT_FDCWD,则表示使用当前工作目录。
  • pathname : 表示要打开的文件路径
  • flags : 表示打开文件时的选项标志,可以为以下常量之一或多个按位或组合而成:
    • O_RDONLY:只读模式打开文件。
    • O_WRONLY:只写模式打开文件。
    • O_RDWR:读写模式打开文件。
    • O_CREAT:如果文件不存在,则创建它。
    • O_TRUNC:如果文件已存在,则将其长度截断为 0。
    • O_APPEND:在文件末尾追加数据。
    • O_DIRECTORY:要求打开的文件必须是一个目录。
    • O_NOFOLLOW:不跟随符号链接打开文件。
  • mode : 表示文件所有者、组和其他用户的访问权限。如果使用了 O_CREAT 选项,则必须提供这个参数

4.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>

int main(void)
{
int dirfd = open(".", O_RDONLY | O_DIRECTORY);
if (dirfd == -1) {
perror("open");
exit(1);
}

DIR *dirp = fdopendir(dirfd);
if (dirp == NULL) {
perror("fdopendir");
exit(1);
}

struct dirent *entry;
while ((entry = readdir(dirp)) != NULL) {
printf("%s\n", entry->d_name);
}

if (closedir(dirp) == -1) {
perror("closedir");
exit(1);
}

return 0;
}

在这个程序中,

  • 我们使用 openat() 函数打开当前目录,并通过 fdopendir() 函数将文件描述符转换为目录流。
  • 然后,我们使用 readdir() 函数读取目录中的文件,并将文件名输出到终端。
  • 最后,我们使用 closedir() 函数关闭目录。

注意: 在使用 openat() 函数打开文件时,可以通过传递不同的文件描述符指定要打开的目录,从而实现更加灵活的操作。此外,在使用 readdir() 函数读取目录时也需要注意判断返回值是否为 NULL,以避免出现不可预期的错误。

5. opendir

5.1 函数说明

函数声明 函数功能
DIR *opendir(const char *name); 它是 POSIX 标准定义的一个函数,用于打开目录并返回一个指向 DIR 结构体类型的指针。

参数:

  • name : 表示要打开的目录路径

返回值:

  • 如果该函数执行成功,则返回一个指向 DIR 类型的指针;
  • 否则返回 NULL

5.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>

int main(void)
{
DIR *dirp = opendir(".");
if (dirp == NULL) {
perror("opendir");
exit(1);
}

struct dirent *entry;
while ((entry = readdir(dirp)) != NULL) {
printf("%s\n", entry->d_name);
}

if (closedir(dirp) == -1) {
perror("closedir");
exit(1);
}

return 0;
}

在上述的程序中,我们使用 opendir() 函数打开当前目录,并通过 readdir() 函数读取目录中的文件名,最后使用 closedir() 函数关闭目录。

注意: 在使用 opendir() 函数打开目录时,返回值为 NULL 则表示出现了错误。此时可以使用 perror() 函数输出错误信息,并使用 exit() 函数退出程序。同时,在使用 readdir() 函数读取目录时也需要小心处理返回值,以避免出现不可预期的错误。

6. openpty

6.1 函数说明

函数声明 函数功能
int openpty(int *amaster, int *aslave, char *name, const struct termios *termp, const struct winsize *winp); 它是 POSIX 标准定义的一个函数,用于打开一个伪终端(PTY)并返回与之关联的主从设备文件描述符。伪终端可以用于在进程之间建立通信,或者在程序中模拟终端行为。

参数:

  • amaster : 表示要返回的主设备文件描述符
  • aslave : 表示要返回的从设备文件描述符
  • name : 表示从设备名称的缓冲区,如果不需要则可以传递 NULL
  • termp : 表示要使用的终端属性,如果不需要则可以传递 NULL
  • winp : 表示要使用的窗口大小,如果不需要则可以传递 NULL

返回值:

  • 如果该函数执行成功,则返回值为 0
  • 否则返回 -1

6.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>

int main(void)
{
int master, slave;
char buf[1024];
ssize_t nread;

if (openpty(&master, &slave, NULL, NULL, NULL) == -1) {
perror("openpty");
exit(1);
}

printf("Slave device: /dev/pts/%d\n", slave);

while ((nread = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
if (write(master, buf, nread) != nread) {
perror("write");
exit(1);
}
}

if (nread == -1) {
perror("read");
exit(1);
}

if (close(master) == -1) {
perror("close");
exit(1);
}

return 0;
}

在上述的程序中,

  • 我们使用 openpty() 函数打开一个伪终端,并通过 read() 函数读取标准输入中的数据。
  • 然后,我们将数据写入主设备文件描述符;
  • 最后关闭该设备。

注意: 在使用 openpty() 函数打开伪终端时,返回值为 -1 则表示出现了错误。此时可以使用 perror() 函数输出错误信息,并使用 exit() 函数退出程序。同时,在使用 read() 函数和 write() 函数读写文件时也需要小心处理返回值,以避免出现不可预期的错误。

7. on_exit

7.1 函数说明

函数声明 函数功能
int on_exit(void (*function)(int, void *), void *arg); 它是 POSIX 标准定义的一个函数,用于在进程退出时调用注册的回调函数。这个函数可以用于在程序异常退出或者正常退出时执行一些清理工作、记录日志等操作

参数:

  • function : 表示要注册的回调函数
  • arg : 表示传递给回调函数的参数

返回值:

  • 如果该函数执行成功,则返回值为 0
  • 否则返回 -1

7.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void cleanup(int status, void *arg)
{
printf("Cleanup function called with status %d\n", status);
}

int main(void)
{
if (on_exit(cleanup, NULL) != 0) {
perror("on_exit");
exit(EXIT_FAILURE);
}

printf("This is the main program\n");

return 0;
}

在如上的程序中,

  • 我们使用 on_exit() 函数注册了一个回调函数 cleanup(),并将其参数设置为 NULL。
  • 然后,在主函数中输出了一条消息。当程序退出时,会自动调用回调函数来进行清理操作。

8. outtext

8.1 函数说明

函数声明 函数功能
void outtext(char *textstring); 在图形视区显示一个字符串

参数:

  • char *textstring :指向以空字符(’\0’)结尾的字符串的指针。

8.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <graphics.h>

int main(void)
{
int gdriver = DETECT, gmode, errorcode;
int midx, midy;

initgraph(&gdriver, &gmode, "");

midx = getmaxx() / 2;
midy = getmaxy() / 2;

moveto(midx, midy);

outtext("This ");
outtext("is ");
outtext("a ");
outtext("test.");

getch();
closegraph();
return 0;
}

在上述的程序中,

  • 我们首先调用 initgraph() 函数初始化图形系统;
  • 然后获取窗口的中心坐标;
  • 接着使用 moveto() 函数将当前绘图位置移动到屏幕中心。
  • 最后,使用 outtext() 函数输出一段文字,然后等待用户按下任意键,并关闭图形窗口。

8.3 运行结果

9. outtextxy

9.1 函数说明

函数声明 函数功能
void outtextxy(int x, int y, char *textstring); 在指定位置显示一字符串

参数:

  • int x : 字符串输出的水平起始坐标(单位为像素)。取值范围:与当前图形模式的屏幕分辨率相关(例如,640x480 模式下,x 范围为 0 到 639)。

  • int y : 字符串输出的垂直起始坐标(单位为像素)。取值范围:与当前图形模式的屏幕分辨率相关(例如,480p 模式下,y 范围为 0 到 479)。

  • char *textstring : 指向以空字符(’\0’)结尾的字符串的指针。

9.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <graphics.h>

int main(void)
{
int gdriver = DETECT, gmode, errorcode;
int x, y;

initgraph(&gdriver, &gmode, "");

x = getmaxx() / 2;
y = getmaxy() / 2;

outtextxy(x, y, "Hello, world!");

getch();
closegraph();
return 0;
}

在上述这个程序中,

  • 我们首先通过 initgraph() 函数初始化图形系统并创建一个窗口;
  • 然后,定义了一个坐标位置 (x, y) 并使用 outtextxy() 函数在该位置输出一段文本。
  • 最后,使用 getch 函数等待用户按下任意键,然后关闭图形窗口。

9.3 运行结果

C语言函数大全--n 开头的函数

总览

函数声明 函数功能
double nan(const char *tagp); 用于返回一个表示 NaN(非数值)的 double 类型数字
int nanosleep(const struct timespec *req, struct timespec *rem); 用于暂停当前进程的执行一段指定的时间。相比于 sleep() 函数,nanosleep() 函数可以精确地指定等待时间,以纳秒为单位。
double nearbyint(double x); 用于将一个浮点数四舍五入到最接近的整数值(double)
float nearbyintf(float x); 用于将一个浮点数四舍五入到最接近的整数值(float)
long double nearbyintl(long double x); 用于将一个浮点数四舍五入到最接近的整数值(long double)
double nextafter(double x, double y); 用于找出与给定的浮点数最接近的下一个浮点数(double)
float nextafterf(float x, float y); 用于找出与给定的浮点数最接近的下一个浮点数(float)
long double nextafterl(long double x, long double y); 用于找出与给定的浮点数最接近的下一个浮点数(long double)
double nexttoward(double x, long double y); 用于找出与给定的浮点数最接近、并朝着指定方向的下一个浮点数(double)
float nexttowardf(float x, long double y); 用于找出与给定的浮点数最接近、并朝着指定方向的下一个浮点数(float)
long double nexttowardl(long double x, long double y); 用于找出与给定的浮点数最接近、并朝着指定方向的下一个浮点数(long double)
locale_t newlocale(int category_mask, const char *locale, locale_t base); 用于创建并返回一个新的本地化环境变量,以便在不同的本地化设置之间进行切换。
int nftw(const char *dirpath, int (*fn)(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf), int nopenfd, int flags); 用于递归遍历指定目录下的所有文件和子目录,并对每个文件或目录执行指定操作。
int nice(int inc); 它是一个系统调用,可将进程截止到当前用户的最大优先级减少指定的优先级,以更改进程的调度优先级。较高的 niceness 值意味着较低的优先级。
char *nl_langinfo(nl_item item); 它是一个 POSIX 标准函数,用于获取当前本地化环境下的语言环境信息。它可以返回一些与语言、货币、日期和时间格式等相关的信息。
long nrand48(unsigned short xsubi[3]); 用于生成带有指定状态的随机数。它使用 48 位整数来表示随机数的状态,可以方便地切换不同的随机数生成器状态。
uint32_t ntohl(uint32_t netlong); 用于将网络字节序(大端序)转换为主机字节序(小端序)。
uint16_t ntohs(uint16_t netshort); 用于将网络字节序(大端序)转换为主机字节序(小端序)。

1. nan

1.1 函数说明

函数声明 函数功能
double nan(const char *tagp); 用于返回一个表示 NaN(非数值)的 double 类型数字

参数:

  • tagp : 指向字符串的指针;用于指定 NaN 数字的类型。如果不需要指定类型,则可以将该参数设置为 NULL。

1.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <math.h>

int main(void)
{
double x = 0.0 / 0.0; // 使用除 0 运算符来生成 NaN 数字
printf("x: %f\n", x);

double y = nan(NULL); // 使用 nan() 函数来生成 NaN 数字
printf("y: %f\n", y);

return 0;
}

注意: NaN 数字具有一些特殊的属性,例如与任何数字进行比较都会返回 false,因此在实际编程中需要特别小心处理 NaN 的情况,避免出现异常结果

1.3 运行结果

2. nanosleep

2.1 函数说明

函数声明 函数功能
int nanosleep(const struct timespec *req, struct timespec *rem); 用于暂停当前进程的执行一段指定的时间。相比于 sleep() 函数,nanosleep() 函数可以精确地指定等待时间,以纳秒为单位。

参数:

  • req : 指向 timespec 结构体的指针,用于指定要等待的时间。timespec 结构体包含两个成员变量:tv_sec 表示等待时间的整数部分(秒),tv_nsec 表示等待时间的小数部分(纳秒)。如果 rem 参数不为 NULL,则在函数返回时,未完成的等待时间将被存储在 rem 指向的 timespec 结构体中。
  • rem : 未完成的等待时间

2.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <time.h>

int main(void)
{
struct timespec req = { 0 };
req.tv_sec = 2; // 等待时间为 2 秒
req.tv_nsec = 5000000; // 加上 5 毫秒

int ret = nanosleep(&req, NULL);
if (ret == 0) {
printf("nanosleep completed\n");
} else {
printf("nanosleep interrupted by signal\n");
}

return 0;
}

在上述的程序中,

  • 我们首先创建一个 timespec 结构体变量 req,用于指定等待时间。在本例中,我们将等待时间设置为 2 秒加上 5 毫秒。
  • 接着,我们调用 nanosleep() 函数,并传递 req 变量的地址作为第一个参数。如果函数执行成功(即完成了预定的等待时间),则返回值为 0,否则返回 -1
  • 最后,我们检查函数的返回值,以确定 nanosleep() 是否成功完成。如果返回值为 0,则表示函数已经完成了预定的等待时间;如果返回值为 -1,则说明函数被信号中断。在实际编程中,我们还可以通过检查 errno 变量来获取更具体的错误信息。

3. nearbyint,nearbyintf,nearbyintl

3.1 函数说明

函数声明 函数功能
double nearbyint(double x); 用于将一个浮点数四舍五入到最接近的整数值(double)
float nearbyintf(float x); 用于将一个浮点数四舍五入到最接近的整数值(float)
long double nearbyintl(long double x); 用于将一个浮点数四舍五入到最接近的整数值(long double)

3.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <math.h>

int main(void)
{
double x = 2.3;
double y = -1.8;

double z = nearbyint(x); // 将 2.3 四舍五入到 2
double w = nearbyint(y); // 将 -1.8 四舍五入到 -2

printf("x: %lf, nearbyint(x): %lf\n", x, z);
printf("y: %lf, nearbyint(y): %lf\n", y, w);

float xf = 2.5;
printf("xf: %f, nearbyintf(xf): %f\n", xf, nearbyintf(xf));

long double xL = -1.3;
printf("xL: %Lf, nearbyintl(xL): %Lf\n", xL, nearbyintl(xL));

return 0;
}

注意: nearbyint() 函数对于 0.5 的情况具有特殊处理:如果要转换的数恰好与两个整数的距离相等,则按照偶数方向进行舍入(即选择更接近偶数的整数)。例如,如果要将 2.5 转换为整数,那么将近似到最接近的偶数 2,而不是 3。这种舍入方式称为 “银行家舍入法”“四舍六入五成双”

3.3 运行结果

4. nextafter,nextafterf,nextafterl

4.1 函数说明

函数声明 函数功能
double nextafter(double x, double y); 用于找出与给定的浮点数最接近的下一个浮点数(double)
float nextafterf(float x, float y); 用于找出与给定的浮点数最接近的下一个浮点数(float)
long double nextafterl(long double x, long double y); 用于找出与给定的浮点数最接近的下一个浮点数(long double)

参数:

  • x : 要查找其下一个浮点数的浮点数
    • y : 给定浮点数的目标值,表示前进方向。

返回值:

  • 如果 y 大于 x,则向正无穷方向查找;
  • 如果 y 小于 x,则向负无穷方向查找;如果 y 等于 x,则返回 y

4.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <math.h>

int main(void)
{
double x = 1.0;
double y = 2.0;

printf("nextafter(%lf, %lf): %.20lf\n", x, y, nextafter(x, y));
printf("nextafter(%lf, %lf): %.20lf\n", y, x, nextafter(y, x));

float xf = 2.0;
float yf = 1.0;
printf("nextafterf(%f, %f): %.20f\n", xf, yf, nextafterf(xf, yf));

long double xL = -1.2;
long double yL = - 1.5;
printf("nextafterl(%Lf, %Lf): %.20Lf\n", xL, yL, nextafterl(xL, yL));

return 0;
}

注意: 由于计算机内部存储浮点数的方式是有限制的,因此在进行浮点数计算时可能会存在误差。在实际编程中,我们应该特别小心处理这些情况,避免出现异常结果。

4.3 运行结果

5. nexttoward,nexttowardf,nexttowardl

5.1 函数说明

函数声明 函数功能
double nexttoward(double x, long double y); 用于找出与给定的浮点数最接近、并朝着指定方向的下一个浮点数(double)
float nexttowardf(float x, long double y); 用于找出与给定的浮点数最接近、并朝着指定方向的下一个浮点数(float)
long double nexttowardl(long double x, long double y); 用于找出与给定的浮点数最接近、并朝着指定方向的下一个浮点数(long double)

参数:

  • x : 要查找其下一个浮点数的浮点数
  • y : 给定浮点数的目标值,表示前进方向。

返回值:

  • 如果 y 大于 x,则向正无穷方向查找;
  • 如果 y 小于 x,则向负无穷方向查找;如果 y 等于 x,则返回 y

5.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <math.h>

int main(void)
{
double x = 1.0;
long double y = 2.0;

printf("nexttoward(%lf, %Lf): %.20lf\n", x, y, nexttoward(x, y));

float xf = 3.2;
printf("nexttowardf(%f, %Lf): %.20f\n", xf, y, nexttowardf(xf, y));

long double xL = 1.9;
printf("nexttowardl(%Lf, %Lf): %.20Lf\n", xL, y, nexttowardl(xL, y));

return 0;
}

5.3 运行结果

6. newlocale

6.1 函数说明

函数声明 函数功能
locale_t newlocale(int category_mask, const char *locale, locale_t base); 用于创建并返回一个新的本地化环境变量,以便在不同的本地化设置之间进行切换。

参数:

  • category_mask : 指定了要创建的本地化环境变量包含哪些类别。可以使用下列常量按位或来指定:
    • LC_ALL_MASK:表示所有类别。
    • LC_COLLATE_MASK:表示字符串比较和排序规则。
    • LC_CTYPE_MASK:表示字符分类和转换规则。
    • LC_MESSAGES_MASK:表示本地化消息文本。
    • LC_MONETARY_MASK:表示货币格式。
    • LC_NUMERIC_MASK:表示数字格式。
    • LC_TIME_MASK:表示时间和日期格式。
  • locale : 指定了要使用的区域设置名称。如果为 NULL 或空字符串,则使用当前系统默认的本地化设置。
  • base : 指定了要基于的基础本地化环境变量。如果为 NULL,则使用 LC_GLOBAL_LOCALE

6.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <locale.h>

int main()
{
// 创建一个新的本地化环境变量,用于解析德语字符串比较和排序规则。
locale_t loc = newlocale(LC_COLLATE_MASK, "de_DE.UTF-8", LC_GLOBAL_LOCALE);

// 在新的本地化环境变量下比较两个字符串,并输出比较结果。
const char *str1 = "äbc";
const char *str2 = "abc";
int result = strcoll_l(str1, str2, loc);
printf("%s %s %s\n", str1, (result < 0 ? "<" : (result > 0 ? ">" : "==")), str2);

// 释放本地化环境变量
freelocale(loc);

return 0;
}

在上述程序中,

  • 我们首先使用 newlocale() 函数创建一个新的本地化环境变量 loc,以便比较和排序德语字符串。
  • 接着,我们使用 strcoll_l() 函数来在新的本地化环境变量下比较两个字符串 str1str2
  • 最后,我们输出比较结果,并使用 freelocale() 函数释放 loc 变量。

注意: 在实际编程中应该特别注意本地化设置对字符处理、货币格式、时间格式等方面的影响,避免出现不必要的错误。

7. nftw

7.1 函数说明

函数声明 函数功能
int nftw(const char *dirpath, int (*fn)(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf), int nopenfd, int flags); 用于递归遍历指定目录下的所有文件和子目录,并对每个文件或目录执行指定操作。

参数:

  • dirpath : 要遍历的目录路径
  • fn : 一个回调函数,用于在遍历过程中对每个文件或目录执行指定操作。该函数的参数如下:
    • fpath : 当前文件的完整路径。
    • sb : 当前文件的 struct stat 结构体指针,包含了当前文件的各种属性信息。
    • typeflag : 表示当前文件的类型,可能为以下值之一:
      • FTW_F:普通文件。
      • FTW_D:目录。
      • FTW_DNR:无法读取的目录。
      • FTW_NS:无法访问的文件(可能是因为权限问题)。
      • FTW_SL:符号链接。
      • FTW_DP:与 FTW_D 相同,但目录本身还未被访问。
      • FTW_SLN:符号链接,指向不存在的文件。
    • ftwbuf : 一个 struct FTW 结构体指针,包含了一些关于遍历状态的信息。
  • nopenfd : 最大打开文件描述符数
  • flags : 控制遍历行为的标志位,可以使用下列常量按位或来指定:
    • FTW_CHDIR:进入目录后更改工作目录。
    • FTW_DEPTH:深度优先遍历。
    • FTW_MOUNT:不跨越文件系统边界。
    • FTW_PHYS:不跟随符号链接。

7.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
#include <stdlib.h>
#include <ftw.h>

static int count = 0;

int print_file_info(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
{
// 打印文件路径和类型
printf("%s ", fpath);
if (typeflag == FTW_F) {
printf("(file)\n");
} else if (typeflag == FTW_D) {
printf("(dir)\n");
} else {
printf("(other)\n");
}

// 计数器加一
count++;

return 0;
}

int main(void)
{
int result = nftw(".", print_file_info, 10, FTW_PHYS);

if (result == -1) {
perror("nftw");
exit(EXIT_FAILURE);
}

printf("Total files and directories: %d\n", count);

return 0;
}

在上述的程序中,

  • 我们首先定义了一个回调函数 print_file_info(),用于打印每个文件或目录的路径和类型,并将计数器加一。
  • 接着,我们调用 nftw() 函数来递归遍历当前目录下的所有文件和子目录,并对每个文件或目录执行 print_file_info() 函数。
  • 最后,我们输出遍历总数。

注意: 在实际编程中应该特别注意文件访问权限等问题,以避免出现不必要的错误。

7.3 运行结果

8. nice

8.1 函数说明

函数声明 函数功能
int nice(int inc); 它是一个系统调用,可将进程截止到当前用户的最大优先级减少指定的优先级,以更改进程的调度优先级。较高的 niceness 值意味着较低的优先级。

参数:

  • inc : 要增加或减少的优先级值。如果 inc 的值为正数,则表示将进程的优先级降低;如果 inc 的值为负数,则表示将进程的优先级提高。通常情况下,只有具有 root 权限的进程才能将自己的优先级升高。

8.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <unistd.h>

int main(void)
{
// 输出当前进程的初始优先级
printf("Initial nice value: %d\n", nice(0));

// 将进程的优先级减少 10
int result = nice(10);
if (result == -1) {
perror("nice");
} else {
printf("New nice value: %d\n", result);
}

return 0;
}

在上述程序中,

  • 我们首先使用 nice(0) 函数输出当前进程的初始优先级;
  • 接着,我们使用 nice(10) 函数将进程的调度优先级降低 10
  • 最后将新的优先级值输出到终端。

注意: 由于 nice() 函数并不是标准 C 库中的函数,因此在编译时需要传递 -posix 参数或其他类似参数以启用 POSIX 标准。在实际编程中应该特别注意优先级修改对进程正常运行的影响,以避免出现不必要的错误。

9. nl_langinfo

9.1 函数说明

函数声明 函数功能
char *nl_langinfo(nl_item item); 它是一个 POSIX 标准函数,用于获取当前本地化环境下的语言环境信息。它可以返回一些与语言、货币、日期和时间格式等相关的信息。

参数:

  • item : 指定要获取的本地化信息。可以使用下列常量之一来指定:
    • ABDAY_*:星期缩写名称(0 ~ 6 表示周日到周六)。
    • DAY_*:星期全称(0 ~ 6 表示周日到周六)。
    • ABMON_*:月份缩写名称(0 ~ 11 表示一月到十二月)。
    • MON_*:月份全称(0 ~ 11 表示一月到十二月)。
    • AM_STR:上午字符串。
    • PM_STR:下午字符串。
    • D_FMT:日期格式字符串。
    • T_FMT:时间格式字符串。
    • ERA:纪元字符串。
    • ERA_D_T_FMT:带日期和时间的纪元字符串。
    • ERA_D_FMT:仅带日期的纪元字符串。
    • ERA_T_FMT:仅带时间的纪元字符串。
    • ALT_DIGITS:非十进制数字字符。

9.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <langinfo.h>

int main(void)
{
char *time_fmt = nl_langinfo(T_FMT);
char *date_fmt = nl_langinfo(D_FMT);

printf("Time format: %s\n", time_fmt);
printf("Date format: %s\n", date_fmt);

return 0;
}

在上述的程序中,

  • 我们使用 nl_langinfo(T_FMT) 函数获取当前本地化环境下的时间格式字符串,并将其输出到终端。
  • 接着,我们使用 nl_langinfo(D_FMT) 函数获取当前本地化环境下的日期格式字符串,并将其输出到终端。

注意: 在实际编程中应该特别注意处理不同本地化环境下信息的差异,以避免出现不必要的错误。

10. nrand48

10.1 函数说明

函数声明 函数功能
long nrand48(unsigned short xsubi[3]); 用于生成带有指定状态的随机数。它使用 48 位整数来表示随机数的状态,可以方便地切换不同的随机数生成器状态。

参数:

  • xsubi : 一个包含 316 位无符号整数的数组,表示了当前随机数生成器的状态。如果想更改随机数生成器的状态,只需修改 xsubi 数组即可。

10.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
unsigned short seed[3];
seed[0] = (unsigned short) time(NULL);
seed[1] = (unsigned short) getpid();
seed[2] = 12345;

srand48(seed);

printf("Random number: %ld\n", nrand48(seed));

return 0;
}

在上述的程序中,

  • 我们首先创建了一个包含 316 位无符号整数的数组 seed,并将其用作随机数生成器的种子。
  • 接着,我们使用 srand48() 函数初始化随机数生成器,并使用 nrand48() 函数生成一个随机数,并将其输出到终端。

注意: 由于 nrand48() 函数生成的是伪随机数,因此在实际使用时需要注意选择足够复杂的种子,并采取适当的加密措施以避免出现不必要的安全问题。

11. ntohl,ntohs

11.1 函数说明

函数声明 函数功能
uint32_t ntohl(uint32_t netlong); 用于将网络字节序(大端序)转换为主机字节序(小端序)。
uint16_t ntohs(uint16_t netshort); 用于将网络字节序(大端序)转换为主机字节序(小端序)。

参数:

  • netlong : 表示要转换的 32 位整数。
  • netshort : 表示要转换的 16 位整数。

11.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdint.h>
#include <netinet/in.h>

int main(void)
{
uint32_t num1 = 0x12345678;
uint16_t num2 = 0x5678;

printf("Original value (hex):\n");
printf("num1: %08X\n", num1);
printf("num2: %04X\n", num2);

num1 = ntohl(num1);
num2 = ntohs(num2);

printf("Converted value (hex):\n");
printf("num1: %08X\n", num1);
printf("num2: %04X\n", num2);

return 0;
}

注意: 在实际编程中应该特别注意不同平台之间的字节序差异,以避免出现不必要的错误。

C语言函数大全--m 开头的函数(下)

总览

函数声明 函数功能
int mkdirat(int dirfd, const char *pathname, mode_t mode); 它是一个 Linux 系统下的系统调用函数,用于在指定目录下创建新的子目录
int mkfifo(const char *pathname, mode_t mode); 它是一个 Linux 系统下的系统调用函数,用于创建命名管道
int mkstemp(char *template); 用于在磁盘上创建一个唯一的临时文件并打开它以进行读写
int mkdir(const char *pathname, mode_t mode); 它是一个 Linux 系统下的系统调用函数,用于创建新目录
int mkdir(const char *pathname); 它是在 Windows 系统下的系统调用函数,用于创建新目录
time_t mktime(struct tm *timeptr); 用于将表示时间的结构体(struct tm)转换为对应的 Unix 时间戳
int mlock(const void *addr, size_t len); 它是一个 Linux 系统下的系统调用函数,用于将指定内存区域锁定在物理内存中,防止其被交换到磁盘上
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 它是一个 Linux 系统下的系统调用函数,可以将一个文件或者设备映射到内存中,并返回指向该内存区域的指针
double modf(double x, double *iptr); 用于将浮点数 value 拆分为其整数部分和小数部分(double)
float modff(float value, float *iptr); 用于将浮点数 value 拆分为其整数部分和小数部分(float)
long double modfl(long double value, long double *iptr); 用于将浮点数 value 拆分为其整数部分和小数部分(long double)
int mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data); 用于将文件系统挂载到指定的挂载点,并返回挂载点的文件描述符
int msync(void *addr, size_t length, int flags); 用于将指定内存区域的数据同步到文件中
int munmap(void *addr, size_t length); 用于取消内存映射区域,并释放与之相关的资源
int munlock(const void *addr, size_t len); 用于将之前使用mlock()函数锁定的内存区域解锁,使其可被操作系统交换出去或被回收

1. mkdirat

1.1 函数说明

函数声明 函数功能
int mkdirat(int dirfd, const char *pathname, mode_t mode); 它是一个 Linux 系统下的系统调用函数,用于在指定目录下创建新的子目录

参数:

  • dirfd : 要在其下创建新目录的父目录的文件描述符。如果值为 AT_FDCWD,则表示使用当前工作目录
  • pathname : 要创建的新目录的名称和路径
  • mode : 要创建的新目录的权限模式

返回值:

  • 如果成功创建新目录时,则返回 0
  • 如果失败时,则返回 -1,并设置错误码(errno)。

1.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

int main()
{
int dirfd;
if ((dirfd = open("/tmp", O_RDONLY)) == -1)
{
printf("Error opening directory.\n");
return 1;
}

if (mkdirat(dirfd, "testdir", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1)
{
printf("Error creating directory.\n");
return 1;
}

close(dirfd);
return 0;
}

在上述的示例代码中,

  • 首先,我们打开 /tmp 目录并获取其文件描述符 dirfd
  • 然后,我们调用 mkdirat() 函数,并将目录的文件描述符、要创建的新目录的名称和路径以及目录的权限模式作为参数传递给函数。如果函数调用成功,则新目录将在 /tmp 目录下创建。
  • 最后,调用 close() 函数关闭文件。

注意:

  • 使用 mkdirat() 函数时,我们需要确保指定的父目录存在并具有适当的权限。
  • 如果要使用相对路径创建新目录,需要确保当前工作目录正确设置。

2. mkfifo

2.1 函数说明

函数声明 函数功能
int mkfifo(const char *pathname, mode_t mode); 它是一个 Linux 系统下的系统调用函数,用于创建命名管道

参数:

  • pathname : 要创建的命名管道的名称和路径
  • mode : 命名管道的权限模式

返回值:

  • 如果成功创建命名管道时,则返回 0
  • 如果失败时,则返回 -1,并设置错误码(errno)。

2.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
// 如果函数调用成功,则在 /tmp 目录下创建一个名为 myfifo 的命名管道
if (mkfifo("/tmp/myfifo", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == -1)
{
printf("Error creating named pipe.\n");
return 1;
}

return 0;
}

注意:

  • 使用 mkfifo() 函数时,我们需要确保指定的路径可被访问并且不存在同名的文件或目录。
  • 如果要使用相对路径创建命名管道,需要确保当前工作目录正确设置。

3. mkstemp

3.1 函数说明

函数声明 函数功能
int mkstemp(char *template); 用于在磁盘上创建一个唯一的临时文件并打开它以进行读写

参数:

  • template : 指向包含 6 个连续 'X' 的字符串的指针,这些 'X' 将被替换为随机字符以创建唯一的文件名。例如,"/tmp/tempfile-XXXXXX" 将会被替换为类似 "/tmp/tempfile-5ZqYU2" 的唯一文件名。

3.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
char temp_file_template[] = "tmp/tempfile-XXXXXX";
int fd;

if ((fd = mkstemp(temp_file_template)) == -1)
{
printf("Error creating temporary file.\n");
return 1;
}

printf("Temporary file created: %s\n", temp_file_template);

// 读写临时文件

// 关闭临时文件
close(fd);
return 0;
}

如上演示了,在 windows 下 创建临时文件:

  • 首先,我们定义了一个字符串 temp_file_template,它包含连续的 6'X'
  • 接着,我们调用 mkstemp() 函数,并将指向 temp_file_template 的指针作为参数传递给函数。如果函数调用成功,则返回新创建文件的文件描述符,并将 temp_file_template 中的 'X' 替换为随机字符以形成唯一的文件名;
  • 然后,调用 printf() 函数,输出该临时文件的名称;
  • 再接着,可以使用 fd 来操作临时文件【这里示例省略了】;
  • 再然后,临时文件使用完毕,调用 close() 函数关闭临时文件。
  • 最后,返回 0 表示程序执行成功。

注意: 在使用 mkstemp() 函数时,我们需要确保提供的模板字符串至少包含 6'X',并且文件命名方式不能与现有文件冲突。

3.3 运行结果

4. mkdir

4.1 函数说明

函数声明 函数功能
int mkdir(const char *pathname, mode_t mode); 它是一个 Linux 系统下的系统调用函数,用于创建新目录
int mkdir(const char *pathname); 它是在 Windows 系统下的系统调用函数,用于创建新目录

参数:

  • pathname: 要创建的新目录的名称和路径
  • mode : 要创建的新目录的权限模式

返回值:

  • 如果成功创建新目录时,则返回 0
  • 如果失败时,则返回 -1,并设置错误码(errno)。

4.2 演示示例

4.2.1 Windows 下示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

int main()
{
if (mkdir("tmp/newdir") == -1)
{
printf("Error creating new directory.\n");
return 1;
}

return 0;
}

4.2.1 Linux 下示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>

int main()
{
if (mkdir("/tmp/newdir", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1)
{
printf("Error creating new directory.\n");
return 1;
}

return 0;
}

4.3 运行结果

Windows 下示例运行结果

5. mktime

5.1 函数说明

函数声明 函数功能
time_t mktime(struct tm *timeptr); 用于将表示时间的结构体(struct tm)转换为对应的 Unix 时间戳

参数:

  • timeptr : 指向 struct tm 结构体的指针,其中包含要转换为 Unix 时间戳的日期和时间信息

返回值:

  • 如果转换成功,则返回对应于输入时间的 Unix 时间戳;
  • 如果转换失败,则返回 -1

5.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
struct tm my_time = {0}; // 初始化为 0,避免随机值影响结果
my_time.tm_year = 2023 - 1900; // 年份应该减去 1900
my_time.tm_mon = 4 - 1; // 月份从 0 开始计数,应该减去 1
my_time.tm_mday = 15;
my_time.tm_hour = 10;
my_time.tm_min = 30;
my_time.tm_sec = 0;

time_t timestamp = mktime(&my_time);
if (timestamp == -1)
{
printf("Error converting time to timestamp.\n");
return 1;
}

printf("Unix timestamp: %lld\n", timestamp);

return 0;
}

在上面的示例代码中,

  • 首先,我们创建一个 struct tm 结构体 my_time,并将其初始化为 0
  • 然后,设置结构体的年、月、日、时、分、秒等信息。这里的年份应该减去 1900,月份应该从 0 开始计数减去 1
  • 接着,调用 mktime() 函数,并将指向 my_time 结构体的指针作为参数传递给函数。如果函数调用成功,则返回对应于输入时间的 Unix 时间戳。
  • 最后,输出该 Unix 时间戳。

注意:

  • 在使用 mktime() 函数时,我们需要确保提供的 struct tm 结构体中的所有字段都已正确设置。
  • 由于 mktime() 函数所使用的时区可能与系统默认的时区不同,所以在某些情况下,转换结果可能会有一定偏差。

5.3 运行结果

6. mlock

6.1 函数说明

函数声明 函数功能
int mlock(const void *addr, size_t len); 它是一个 Linux 系统下的系统调用函数,用于将指定内存区域锁定在物理内存中,防止其被交换到磁盘上

参数:

  • addr : 要锁定的内存区域的起始地址
  • len : 要锁定的内存区域的长度(字节数)

返回值:

  • 如果成功锁定内存区域时,则返回 0
  • 如果失败时,则返回 -1,并设置错误码(errno)。

6.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // memset 函数所需头文件
#include <sys/mman.h> // mlock 函数所需头文件

#define PAGE_SIZE 4096 // 页大小

int main()
{
char *buf;
size_t size = PAGE_SIZE;

// 分配一段内存并清零
buf = (char *)malloc(size);
memset(buf, 0, size);

// 锁定分配的内存区域
if (mlock(buf, size) == -1)
{
printf("Error locking memory.\n");
return 1;
}

// 使用分配的内存...
strncpy(buf, "Hello world!", size);
printf("%s\n", buf);

// 解锁内存区域
if (munlock(buf, size) == -1)
{
printf("Error unlocking memory.\n");
return 1;
}

free(buf);
return 0;
}

在上述的示例代码中,

  • 首先,我们使用 malloc() 函数分配了一个页大小的内存区域,并使用 memset() 函数将其清零;
  • 然后,调用 mlock() 函数,并将指向分配内存区域起始地址的指针以及内存区域的长度作为参数传递给函数。如果函数调用成功,则锁定分配的内存区域,防止其被交换到磁盘上;
  • 接着,就可以对该内存区域进行读写操作【示例代码简单演示了使用 strncpy() 函数向上述的分配内存区域中写入字符串 "Hello world!",并通过 printf() 函数输出该字符串】;
  • 再接着,调用 munlock() 函数解除内存区域的锁定;
  • 最后,释放分配内容,并正常结束程序。

注意: 在使用 mlock() 函数时,我们需要确保指定的内存区域已正确分配并且足够大,以避免锁定错误的内存区域。

7. mmap

7.1 函数说明

函数声明 函数功能
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 它是一个 Linux 系统下的系统调用函数,可以将一个文件或者设备映射到内存中,并返回指向该内存区域的指针

参数:

  • addr : 映射区域开始地址,通常为 NULL,由内核选定
  • length : :映射区域的长度(字节数)
  • prot : 映射区域的保护方式。其可能的取值如下【按位或(|)组合】:
    • PROT_NONE: 区域不能被访问。
    • PROT_READ: 区域可被读取。
    • PROT_WRITE: 区域可被写入。
    • PROT_EXEC: 区域可被执行。
  • flags : 制定映射区域的类型和其他标志。其可能的取值如下【按位或(|)组合】:
    • MAP_SHARED: 允许多个进程共享该映射区域,对映射区域所做的修改将反映到所有共享该区域的进程中。
    • MAP_PRIVATE: 该映射区域只允许当前进程进行访问,对映射区域所做的修改不会反映到其他进程中。
    • MAP_FIXED: 强制将映射区域放置在指定的地址处(如果该地址已经被占用,则会导致错误)。
    • MAP_ANONYMOUS: 创建一个匿名映射区域,不与任何文件关联。
    • MAP_FILE: 将映射区域与文件关联,需要指定文件描述符和偏移量。
    • MAP_LOCKED: 指示内核在物理存储器中锁定映射区域的页面,以确保在访问该区域时不会发生缺页中断。
  • fd : 要映射的文件描述符
  • offset : 文件映射的起始偏移量

7.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
int fd;
char *ptr;

// 打开文件
if ((fd = open("example.txt", O_RDWR)) == -1)
{
printf("Error opening file.\n");
return 1;
}

// 将文件映射到内存中
ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED)
{
printf("Error mapping file.\n");
return 1;
}

// 使用映射区域进行读写操作
strncpy(ptr, "Hello world!", 13);
printf("%s\n", ptr);

// 取消映射并关闭文件
if (munmap(ptr, 4096) == -1)
{
printf("Error unmapping file.\n");
return 1;
}
close(fd);

return 0;
}

上述的示例代码,演示了如何使用 mmap() 函数将一个文件映射到内存中,并使用指针 ptr 访问这个映射区域 :

  • 首先,我们调用 open() 函数打开文件 "example.txt",并检查是否成功打开。
  • 接着,调用 mmap() 函数将文件的前 4096 字节映射到内存中,同时指定保护方式为可读写(PROT_READ | PROT_WRITE)以及共享属性(MAP_SHARED)。
  • 然后,如果映射成功,则使用 strncpy() 函数向映射区域中写入字符串 "Hello world!",并通过 printf() 函数输出该字符串。
  • 最后,调用 munmap() 函数取消映射,并关闭文件。

8. modf,modff,modfl

8.1 函数说明

函数声明 函数功能
double modf(double x, double *iptr); 用于将浮点数 value 拆分为其整数部分和小数部分(double)
float modff(float value, float *iptr); 用于将浮点数 value 拆分为其整数部分和小数部分(float)
long double modfl(long double value, long double *iptr); 用于将浮点数 value 拆分为其整数部分和小数部分(long double)

参数:

  • value : 待处理的浮点数
  • iptr : 用于返回 value 的整数部分

8.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
#include <math.h>

int main()
{
double x = 3.141592653589793;
double ipart;

double fpart = modf(x, &ipart);

printf("x = %f\n", x);
printf("整数部分 = %.0f\n", ipart);
printf("小数部分 = %f\n", fpart);

float y = 2.718281828459045;
float ipart_f;

float fpart_f = modff(y, &ipart_f);

printf("y = %f\n", y);
printf("整数部分 = %.0f\n", ipart_f);
printf("小数部分 = %f\n", fpart_f);

long double z = 1.414213562373095;
long double ipart_l;

long double fpart_l = modfl(z, &ipart_l);

printf("z = %Lf\n", z);
printf("整数部分 = %.0Lf\n", ipart_l);
printf("小数部分 = %Lf\n", fpart_l);

return 0;
}

8.3 运行结果

9. mount

9.1 函数说明

函数声明 函数功能
int mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data); 用于将文件系统挂载到指定的挂载点,并返回挂载点的文件描述符

参数:

  • source : 要挂载的文件系统。可以是一个设备文件名、一个目录路径或者 NULL(表示根据 filesystemtype 参数自动选择默认源)
  • target : 要挂载到的目标点,即挂载点
  • filesystemtype : 要挂载的文件系统类型
  • mountflags : 挂载选项
  • data : 任意与文件系统相关的数据

9.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <sys/mount.h>

int main()
{
int ret;
const char* source = "/dev/sda1";
const char* target = "/mnt/usbdrive";
const char* type = "ext4";

ret = mount(source, target, type, 0, NULL);
if (ret == -1)
{
perror("mount");
return 1;
}

printf("File system mounted successfully!\n");

return 0;
}

在上面的示例代码中,我们调用 mount() 函数将 /dev/sda1 设备文件上的 ext4 文件系统挂载到 /mnt/usbdrive 目录下,并检查挂载操作是否成功。如果 mount() 函数返回值为 -1,则表示挂载失败,通过 perror() 函数输出错误信息并返回 1,程序异常结束;否则,打印 “File system mounted successfully!” 表示挂载成功,并返回 0,程序正常结束。

10. msync

10.1 函数说明

函数声明 函数功能
int msync(void *addr, size_t length, int flags); 用于将指定内存区域的数据同步到文件中

参数:

  • addr : 要同步的内存起始地址
  • length : 要同步的内存区域长度
  • flags : 同步选项,可以是以下值之一:
    • MS_ASYNC: 进行异步写操作(默认选项)
    • MS_SYNC: 进行同步写操作
    • MS_INVALIDATE: 使 cache 内容无效

10.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main()
{
int fd = open("example.txt", O_RDWR);
if (fd == -1)
{
perror("open");
return 1;
}

// 将文件映射到内存中
char* ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED)
{
perror("mmap");
close(fd);
return 1;
}

// 写入新的数据
strncpy(ptr, "Hello world!", 13);

// 同步数据到文件中
int ret = msync(ptr, 4096, MS_SYNC);
if (ret == -1)
{
perror("msync");
munmap(ptr, 4096);
close(fd);
return 1;
}

// 解除映射并关闭文件
ret = munmap(ptr, 4096);
if (ret == -1)
{
perror("munmap");
close(fd);
return 1;
}
close(fd);

return 0;
}

在上面的示例代码中,

  • 首先,我们通过 open() 函数打开文件 "example.txt"
  • 然后,使用 mmap() 函数将文件的前 4096 字节映射到内存中;
  • 接着,调用 strncpy() 函数向映射区域中写入新的数据,并通过 msync() 函数将修改后的数据同步回磁盘文件;
  • 最后,调用 munmap() 函数解除映射并关闭文件。

11. munmap

11.1 函数说明

函数声明 函数功能
int munmap(void *addr, size_t length); 用于取消内存映射区域,并释放与之相关的资源

参数:

  • addr : 要取消映射的内存起始地址
  • length : 要取消映射的内存区域长度

在调用 munmap() 函数后,操作系统将取消指定的内存映射,并回收相应的资源,包括虚拟地址空间和关联的物理内存页(如果存在)。此外,取消映射还可能导致未同步到磁盘文件中的修改数据丢失。

注意: 必须在对映射区域进行任何修改或者访问之前,先使用 mmap() 函数将文件映射到内存中;并在完成所有操作之后,再使用 munmap() 函数解除映射。否则,可能会引发各种错误或者异常情况。

11.2 演示示例

参见 7.2 的 演示示例,这里不再赘述

12. munlock

12.1 函数说明

函数声明 函数功能
int munlock(const void *addr, size_t len); 用于将之前使用mlock()函数锁定的内存区域解锁,使其可被操作系统交换出去或被回收

参数:

  • addr : 待解锁的内存区域的起始地址
  • len : 待解锁的内存区域的长度(以字节为单位)

返回值:

  • 如果成功解锁时,则返回 0
  • 如果失败时,则返回 -1,并设置错误码(errno)。

注意: 只有拥有相应权限的进程才能解锁该内存区域。

12.2 演示示例

参见 6.2 的 演示示例,这里不再赘述

C语言函数大全--m 开头的函数(上)

总览

函数声明 函数功能
void *malloc(size_t size); 用于动态分配内存
int mblen(const char *s, size_t n); 检查多字节字符的长度
size_t mbrlen(const char *s, size_t n, mbstate_t *ps); 检查多字节字符的长度
size_t mbrtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *ps); 将多字节字符转换为宽字符
int mbsinit(const mbstate_t *ps); 检查转换状态是否为起始状态
size_t mbstowcs(wchar_t *pwcs, const char *s, size_t n); 用于将多字节字符序列转换为宽字符序列。
size_t mbsrtowcs(wchar_t *dst, const char **src, size_t len, mbstate_t *ps); 用于将多字节字符序列转换为宽字符序列,并在转换过程中自动更新 mbstate_t 转换状态结构体。
int mbtowc(wchar_t *restrict pwc, const char *restrict s, size_t n); 用于将一个多字节字符 (Multibyte Character) 转换成一个宽字符 (Wide Character)。
void *memccpy(void *restrict dst, const void *restrict src, int c, size_t n); 用于将内存块的内容复制到另一个内存块中,并在指定字符出现时停止复制。
void *memchr(const void *s, int c, size_t n); 用于在某一内存块中查找指定字符的位置。
void *memcpy(void *dest, const void *src, size_t n); 用于将源内存块中的 n 个字节复制到目标内存块中。
int memcmp(const void *s1, const void *s2, size_t n); 用于比较两个内存块的内容是否相同。
void *memmove(void *dest, const void *src, size_t n); 用于将源内存块中的 n 个字节复制到目标内存块中。与 memcpy() 函数不同的是,memmove() 函数在复制过程中会处理内存块重叠的情况。
void *memset(void *s, int c, size_t n); 用于将一个内存块中的所有字节都设置为指定的值。
errno_t memset_s(void *s, rsize_t smax, int c, rsize_t n); C11 标准新增了一个名为 memset_s() 的安全版本函数。与 memset() 函数不同的是,memset_s() 函数会在设置内存块值时检查目标内存块大小,并防止缓冲区溢出、重叠等安全问题。

1. malloc

1.1 函数说明

函数声明 函数功能
void *malloc(size_t size); 用于动态分配内存

参数:

  • size : 需要分配的内存大小(以字节为单位)

返回值:

  • 如果分配成功,返回分配的内存块的指针;
  • 如果分配失败,则返回 NULL。

1.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
char *str = NULL;

// 分配内存
str = (char *)malloc(20 * sizeof(char));

if (str == NULL)
{
printf("Failed to allocate memory.\n");
return 1;
}

// 将字符串复制到内存中
strcpy(str, "Hello, world!");

// 输出字符串
printf("%s\n", str);

// 释放内存
free(str);

return 0;
}

在上面的示例程序中,

  • 我们首先声明一个指向字符型的指针 str,并将其初始化为 NULL
  • 然后使用 malloc() 函数动态分配了 20 字节的内存空间,并将其赋值给 str 指针;
  • 接下来,我们使用 strcpy() 函数将字符串 "Hello, world!" 复制到内存中,并使用 printf() 函数输出字符串;
  • 最后,我们使用 free() 函数释放了分配的内存空间。

1.3 运行结果

2. mblen

2.1 函数说明

函数声明 函数功能
int mblen(const char *s, size_t n); 检查多字节字符的长度

参数:

  • s : 指向待检查的多字节字符或多字节字符序列的指针
  • n : 要检查的最大字节数

注意: 如果 s 是空指针,则返回 0,表示不是多字节字符;否则,如果 n 不足以包含完整的多字节字符,则返回 -1,表示需要更多的输入;否则,返回多字节字符所需的字节数。

2.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

int main()
{
// 设置本地化环境
setlocale(LC_ALL, "");

char str[] = u8"你好,世界!";
int len;

// 检查第一个字符的长度
len = mblen(str, MB_CUR_MAX);

if (len == -1)
{
printf("Failed to determine the length of the multibyte character.\n");
return 1;
}

printf("The length of the first multibyte character is %d bytes.\n", len);

return 0;
}

在上面的示例程序中,

  • 我们首先使用 setlocale() 函数设置本地化环境,以便正确处理多字节字符。
  • 然后我们定义了一个包含中文字符的字符串 str
  • 接着使用 mblen() 函数检查第一个字符的长度,并将其保存到变量 len 中。
  • 最后,我们输出该字符的长度。

2.3 运行结果

3. mbrlen

3.1 函数说明

函数声明 函数功能
size_t mbrlen(const char *s, size_t n, mbstate_t *ps); 检查多字节字符的长度

参数:

  • s : 指向待检查的多字节字符或多字节字符序列的指针
  • n : 要检查的最大字节数
  • ps : 描述转换状态的 mbstate_t 结构体的指针

注意: 如果 s 是空指针,则返回 0,表示不是多字节字符;否则,如果 n 不足以包含完整的多字节字符,则返回 (size_t)-2,表示需要更多的输入;否则,如果 psNULL,则使用默认转换状态;否则,将 ps 的值更新为已经转换的字符数,并返回多字节字符所需的字节数。

3.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <locale.h>

int main()
{
// 设置本地化环境
setlocale(LC_ALL, "");

char str[] = u8"你好,世界!";
int len;

// 检查第一个字符的长度
len = mbrlen(str, MB_CUR_MAX, NULL);

if (len == (size_t)-2) // 特殊的返回值,表示发生了错误
{
printf("Failed to determine the length of the multibyte character.\n");
return 1;
}

printf("The length of the first multibyte character is %d bytes.\n", len);

return 0;
}

在上面的示例程序中,

  • 我们首先使用 setlocale() 函数设置本地化环境,以便正确处理多字节字符。
  • 然后我们定义了一个包含中文字符的字符串 str
  • 接着使用 mbrlen() 函数检查第一个字符的长度,并将其保存到变量 len 中。
  • 最后,我们输出该字符的长度。

3.3 运行结果

4. mbrtowc

4.1 函数说明

函数声明 函数功能
size_t mbrtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *ps); 将多字节字符转换为宽字符

参数:

  • pwc : 一个指向宽字符的指针,表示将要存入转换后的宽字符;
  • s : 一个指向多字节字符或字符序列的指针;
  • n : 一个表示最多转换的字节数的整数;
  • ps : 一个指向转换状态的指针,如果为 NULL,则使用默认转换状态。

返回值:

  • 如果能转换,返回转换的字符数;
  • 如果不能转换,则返回 (size_t)-1

4.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <wchar.h>

int main()
{
// 设置本地化环境
setlocale(LC_ALL, "");

char str[] = u8"你好,世界!";
wchar_t wc;
mbstate_t state = {0};

// 将第一个字符转换为宽字符
size_t len = mbrtowc(&wc, str, MB_CUR_MAX, &state);

if (len == (size_t)-1)
{
printf("Failed to convert multibyte character.\n");
return 1;
}

// 输出宽字符
wprintf(L"The first wide character is: %lc\n", wc);

return 0;
}

在上面的示例程序中,

  • 我们首先使用 setlocale() 函数设置本地化环境,以便正确处理多字节字符。
  • 然后我们定义了一个包含中文字符的字符串 str
  • 接着使用 mbrtowc() 函数将第一个字符转换为宽字符,并将其保存到变量 wc 中。
  • 最后,我们使用 wprintf() 函数输出宽字符。

注意: 在调用 mbrtowc() 函数之前,必须将 mbstate_t 结构体的值初始化为 0。在 C99 标准中,可以使用大括号对结构体进行初始化,这会把结构体或数组的每个元素都初始化为默认值(0NULL)。

4.3 运行结果

5. mbsinit

5.1 函数说明

函数声明 函数功能
int mbsinit(const mbstate_t *ps); 检查转换状态是否为起始状态
参数:
  • ps : 指向 mbstate_t 结构体的指针,表示要检查的转换状态。

注意: 如果 ps 是空指针,则返回非零值(真),表示默认转换状态已经初始化;否则,如果 ps 描述的转换状态是起始状态,则返回非零值(真);否则,返回 0(假)。

5.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <wchar.h>

int main()
{
// 设置本地化环境
setlocale(LC_ALL, "");

char str[] = u8"你好,世界!";
mbstate_t state = {0};

// 检查转换状态是否为起始状态
if (!mbsinit(&state))
{
printf("The conversion state is not initial.\n");
return 1;
}

// 打印转换状态
printf("The conversion state is %s.\n", (mbsinit(&state) ? "initial" : "not initial"));

return 0;
}

在上面的示例程序中,

  • 我们首先定义了一个包含中文字符的字符串 str 和一个转换状态结构体 state
  • 然后我们使用 mbsinit() 函数检查转换状态是否为起始状态;
  • 最后在控制台输出 "The conversion state is initial."

5.3 运行结果

6. mbstowcs

6.1 函数说明

函数声明 函数功能
size_t mbstowcs(wchar_t *pwcs, const char *s, size_t n); 用于将多字节字符序列转换为宽字符序列。
参数:
  • pwcs : 指向存储结果宽字符序列的缓冲区的指针
  • s : 待转换的多字节字符序列
  • n : 缓冲区的最大长度(以宽字符数计)

返回值:

  • 如果成功地将多字节字符序列转换为宽字符序列,则该函数返回实际写入缓冲区中的宽字符数,不包括空字符 \0
  • 如果遇到了无效的多字节字符或编码,或者宽字符缓冲区不足,导致转换失败,则该函数返回 (size_t)-1

6.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <wchar.h>

int main()
{
// 设置本地化环境
setlocale(LC_ALL, "");

char str[] = u8"你好,世界!";
wchar_t wcbuf[20];

// 将多字节字符序列转换为宽字符序列
size_t ret = mbstowcs(wcbuf, str, sizeof(wcbuf)/sizeof(wchar_t));

if (ret == (size_t)-1)
{
printf("Failed to convert multibyte character sequence.\n");
return 1;
}

// 在控制台输出宽字符序列及其长度
wprintf(L"The wide character string is: %ls (%zu characters)\n", wcbuf, ret);

return 0;
}

在上面的示例程序中,我们首先定义了一个包含中文字符的字符串 str 和一个用于存储结果宽字符序列的缓冲区 wcbuf。然后我们使用 mbstowcs() 函数将多字节字符序列转换为宽字符序列,并在控制台输出相应的信息。

注意: 在计算缓冲区大小时,必须将其指定为宽字符数(即 sizeof(wcbuf)/sizeof(wchar_t)),而不是字节数或字符数。这是因为在 Windows 等一些操作系统中,wchar_t 类型并不总是占用固定的字节数,而可能会根据编译器和平台而变化。

6.3 运行结果

7. mbstowcs

7.1 函数说明

函数声明 函数功能
size_t mbsrtowcs(wchar_t *dst, const char **src, size_t len, mbstate_t *ps); 用于将多字节字符序列转换为宽字符序列,并在转换过程中自动更新 mbstate_t 转换状态结构体。

参数:

  • dst: 指向存储结果宽字符序列的缓冲区的指针
  • src : 指向待转换的多字节字符序列的指针的指针
  • len : 缓冲区的最大长度(以宽字符数计)
  • ps : 指向包含转换状态信息的结构体 mbstate_t 的指针

返回值:

  • 如果成功地将多字节字符序列转换为宽字符序列,则该函数返回实际写入缓冲区中的宽字符数,不包括空字符 \0
  • 如果遇到了无效的多字节字符或编码,或者宽字符缓冲区不足,导致转换失败,则该函数返回 (size_t)-1

注意: mbsrtowcs() 函数会自动更新转换状态结构体 mbstate_t,以记录上一次调用的状态并在下一次调用时继续使用。这使得 mbsrtowcs() 函数适用于处理长的、包含部分多字节字符的字符串。它会自动识别和处理多字节字符序列中的部分字符,并等待更多的字节,直到可以完成转换为止。

7.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <wchar.h>

int main()
{
// 设置本地化环境
setlocale(LC_ALL, "");

char str[] = u8"你好,世界!";
wchar_t wcbuf[20];
mbstate_t state = {0};

// 将多字节字符序列转换为宽字符序列
size_t ret = mbsrtowcs(wcbuf, (const char**)&str, sizeof(wcbuf)/sizeof(wchar_t), &state);
if (ret == (size_t)-1)
{
printf("Failed to convert multibyte character sequence.\n");
return 1;
}

// 在控制台输出宽字符序列及其长度
wprintf(L"The wide character string is: %ls (%zu characters)\n", wcbuf, ret);

return 0;
}

8. mbtowc

8.1 函数说明

函数声明 函数功能
int mbtowc(wchar_t *restrict pwc, const char *restrict s, size_t n); 用于将一个多字节字符 (Multibyte Character) 转换成一个宽字符 (Wide Character)。

参数:

  • pwc : 指向存储宽字符的指针。
  • s : 指向要转换的多字节字符的指针。
  • n : 要转换的最大字节数。

返回值:

  • 如果转换成功,则返回转换后的宽字符数;
  • 如果遇到无效的多字节字符,则返回 -1
  • 如果传递了空指针,则返回 0

8.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <wchar.h>

int main(void)
{
setlocale(LC_ALL, "");

char mbstr[] = "Hello, world!";
wchar_t wc;
int len = mbtowc(&wc, mbstr, sizeof(mbstr));
if (len > 0) {
wprintf(L"%lc\n", wc);
} else if (len == 0) {
wprintf(L"Empty string.\n");
} else if (len == -1) {
wprintf(L"Invalid multibyte character.\n");
}

return EXIT_SUCCESS;
}

8.3 运行结果

9. memccpy

9.1 函数说明

函数声明 函数功能
void *memccpy(void *restrict dst, const void *restrict src, int c, size_t n); 用于将内存块的内容复制到另一个内存块中,并在指定字符出现时停止复制。
参数:
  • dst : 要复制到的目标内存块的指针
  • src : 要从中复制数据的源内存块的指针
  • c : 指定的字符值
  • n : 要复制的字节数

返回值:

  • 如果源内存块的前 n 个字节中包含字符 c,则返回指向字符 c 后面一个字节的指针;
  • 否则返回 NULL

9.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
char src[] = "Hello, world!";
char dst[20];
memset(dst, 0, sizeof(dst));

char *p = (char*) memccpy(dst, src, 'o', sizeof(src));
if (p) {
printf("'%c' found at index %ld\n", *(p - 1), p - dst - 1);
} else {
printf("'%c' not found\n", 'o');
}

printf("%s\n", dst);

return 0;
}

9.3 运行结果

10. memchr

10.1 函数说明

函数声明 函数功能
void *memchr(const void *s, int c, size_t n); 用于在某一内存块中查找指定字符的位置。

参数:

  • s : 要进行查找的内存块的起始地址
  • c : 要查找的指定字符,以整数形式表示
  • n : 要查找的字节数,即在前 n 个字节中查找指定字符

10.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
char str[] = "Hello, world!";
char ch = 'w';
char *p;

// 在字符串中查找指定字符
p = (char*) memchr(str, ch, strlen(str));
if (p) {
printf("'%c' found at index %ld\n", ch, p - str);
} else {
printf("'%c' not found\n", ch);
}

return EXIT_SUCCESS;
}

在上述程序中,

  • 我们首先定义了一个字符串 str 和要查找的指定字符 ch
  • 然后使用 memchr() 函数查找字符串 str 中是否包含指定字符 ch
  • 最后如果找到了该字符,则输出它的索引位置;否则,输出未找到的提示信息。

10.3 运行结果

11. memcpy

11.1 函数说明

函数声明 函数功能
void *memcpy(void *dest, const void *src, size_t n); 用于将源内存块中的 n 个字节复制到目标内存块中。

参数:

  • dest : 目标内存块的起始地址
  • src : 源内存块的起始地址
  • n : 要复制的字节数

11.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
char src[] = "Hello, world!";
char dst[20];
memset(dst, 0, sizeof(dst));

// 将源字符串复制到目标字符串中
memcpy(dst, src, strlen(src));

printf("%s\n", dst);

return 1;
}

注意: 在使用 memcpy() 函数进行内存复制时,目标内存块必须足够大,以容纳源内存块中的全部内容。否则,复制过程可能会导致访问非法内存空间,从而导致代码异常或崩溃。因此,在进行内存复制时,应该尽量避免超出目标内存块大小的范围。

11.3 运行结果

12. memcmp

12.1 函数说明

函数声明 函数功能
int memcmp(const void *s1, const void *s2, size_t n); 用于比较两个内存块的内容是否相同。

参数:

  • s1 : 要进行比较的第一个内存块的起始地址
  • s2 : 要进行比较的第二个内存块的起始地址
  • n : 要比较的字节数。

注意: memcmp() 函数会逐一比较两个内存块中对应位置上的字节大小,直到找到差异或者比较完全部字节。

  • 如果两个内存块完全相同,则返回值为 0
  • 如果两个内存块不同,则返回值是两个内存块中第一个不同字节处的差值(s1 中该字节的值减去 s2 中该字节的值)。

12.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
char str1[] = "Hello, world!";
char str2[] = "Hello, everyone!";

// 比较两个字符串
int result = memcmp(str1, str2, strlen(str1));
printf("result = %d\n", result);
if (result == 0) {
printf("Strings are equal\n");
} else if (result < 0) {
printf("String '%s' is smaller than string '%s'\n", str1, str2);
} else {
printf("String '%s' is larger than string '%s'\n", str1, str2);
}

return 1;
}

注意: 在比较两个内存块时,应该确保被比较的内存块中包含足够的字节,并且待比较的字节数不超过内存块大小,否则函数可能会出现异常行为。另外,由于返回值是有符号整数类型,因此在比较时应该将其强制转换为无符号整数类型,以避免出现不必要的错误。

12.3 运行结果

13. memmove

13.1 函数说明

函数声明 函数功能
void *memmove(void *dest, const void *src, size_t n); 用于将源内存块中的 n 个字节复制到目标内存块中。与 memcpy() 函数不同的是,memmove() 函数在复制过程中会处理内存块重叠的情况。

参数:

  • dest : 目标内存块的起始地址
  • src : 源内存块的起始地址
  • n : 要复制的字节数。

注意: memmove() 函数会将源内存块中的前 n 个字节复制到目标内存块中,并返回指向目标内存块起始地址的指针。

13.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
char str[] = "Hello, world!";
char tmp[20];
memset(tmp, 0, sizeof(tmp));

// 将源字符串复制到目标字符串中(处理重叠的情况)
memmove(tmp, str + 6, strlen(str) - 6);

printf("%s\n", tmp);

return 0;
}

注意: 在使用 memmove() 函数进行内存复制时,目标内存块必须足够大,以容纳源内存块中的全部内容。否则,复制过程可能会导致访问非法内存空间,从而导致代码异常或崩溃。此外,由于 memmove() 函数的处理开销较大,因此在不涉及内存块重叠时,应该尽量使用 memcpy() 函数以提高效率。

13.3 运行结果

14. memset,memset_s

14.1 函数说明

函数声明 函数功能
void *memset(void *s, int c, size_t n); 用于将一个内存块中的所有字节都设置为指定的值。
errno_t memset_s(void *s, rsize_t smax, int c, rsize_t n); C11 标准新增了一个名为 memset_s() 的安全版本函数。与 memset() 函数不同的是,memset_s() 函数会在设置内存块值时检查目标内存块大小,并防止缓冲区溢出、重叠等安全问题。

memset 参数:

  • s : 要进行设置的内存块的起始地址
  • c : 要设置的值,以整数形式表示
  • n : 要设置的字节数

memset_s 参数:

  • s : 要进行设置的内存块的起始地址
  • smax : 目标内存块的大小
  • c : 要设置的值,以整数形式表示
  • n : 要设置的字节数

14.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
char str[21] = {'\0'};
memset(str, 'A', sizeof(str) - 1);
printf("%s\n", str);
return 0;
}

14.3 运行结果

参考

  1. [API Reference Document]

C语言函数大全-- l 开头的 Linux 内核函数(链表管理函数)

总览

函数声明 函数功能
void list_add(struct list_head *new, struct list_head *head); 它是 Linux 内核中双向链表操作的一个基本函数,用于将新节点添加到双向链表的头部
void list_add_tail(struct list_head *new, struct list_head *head); 它是 Linux 内核中双向链表操作的一个基本函数,用于将新节点添加到链表尾部。
void list_cut_before(struct list_head *new, struct list_head *head, struct list_head *entry); 它是 Linux 内核中双向链表操作的一个基本函数,用于将一段节点从原始链表中移动到另一个链表中,并将其添加到新链表的头部。
void list_cut_position(struct list_head *new, struct list_head *head, struct list_head *entry); 它是 Linux 内核中双向链表操作的一个基本函数,用于将一段节点从原始链表中移动到另一个链表中,并将其添加到新链表的头部。与list_cut_before不同的是,该函数需要指定要移动的节点的具体位置,而不是直接指定一个节点。
void list_del(struct list_head *entry); 用于从链表中删除一个节点,但不会修改该节点的指针信息。
void list_del_init(struct list_head *entry); 用于从链表中删除一个节点,但会将被删除的节点的指针信息初始化为NULL。
void list_del_init_careful(struct list_head *entry, struct list_head *prev, struct list_head *next); 用于从链表中删除一个节点,但需要指定该节点的前驱节点和后继节点,以确保链表结构正确。
int list_empty(const struct list_head *head); 用于判断链表是否为空,并返回非零值表示为空,返回0表示不为空
int list_empty_careful(const struct list_head *head); 用于判断链表是否为空,但会先检查链表头部的指针是否为空,以避免对空指针进行解引用。
void list_move(struct list_head *list, struct list_head *head); 用于将一个节点移动到另外一个链表的头部。
void list_move_tail(struct list_head *list, struct list_head *head); 用于将一个节点移动到另外一个链表的尾部。
void list_bulk_move_tail(struct list_head *list, int count, struct list_head *head); 用于将多个节点从一个链表移动到另一个链表的尾部。
void list_replace(struct list_head *old, struct list_head *new); 用于用一个新节点替换指定节点。
void list_replace_init(struct list_head *old, struct list_head *new); 除了可以完成 list_replace 做的所有操作外,它还将原来的节点初始化为空。
static inline void list_rotate_left(struct list_head *head) 用于将链表向左旋转一个位置。
void list_rotate_to_front(struct list_head *head, struct list_head *pivot); 用于将指定节点移到链表头部,并旋转链表使得该节点成为新的头部。
void list_splice(struct list_head *list, struct list_head *head); 用于将一个链表中的所有节点插入到另一个链表的指定位置之前。
void list_splice_tail(struct list_head *list, struct list_head *head); 用于将一个链表中的所有节点插入到另一个链表的尾部。
void list_splice_init(struct list_head *list, struct list_head *head); 除了可以完成 list_splice 做的所有操作外,它还将原来的链表初始化为空。
void list_splice_tail_init(struct list_head *list, struct list_head *head); 除了可以完成 list_splice_tail 做的所有操作外,它还将原来的链表初始化为空。
void list_swap(struct list_head *list1, struct list_head *list2); 交换两个链表头部的位置。

1. list_add,list_add_tail

1.1 函数说明

函数声明 函数功能
void list_add(struct list_head *new, struct list_head *head); 它是 Linux 内核中双向链表操作的一个基本函数,用于将新节点添加到双向链表的头部
void list_add_tail(struct list_head *new, struct list_head *head); 它是 Linux 内核中双向链表操作的一个基本函数,用于将新节点添加到链表尾部。
参数:
  • new : 要添加的新节点的指针
  • head : 链表头节点的指针。
    • list_add() 函数会将 new 节点插入到链表头之前,使其成为新的链表头节点。
    • list_add_tail() 函数会根据 链表头节点找到链表尾节点,并将 new 节点添加到链表尾部。

1.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

struct list_head {
struct list_head *next, *prev;
};

struct node {
int data;
struct list_head link;
};

void init_list_head(struct list_head *head)
{
head->next = head->prev = head;
}

void print_list(struct list_head *head)
{
struct node *p;
for (p = (struct node *)head->next; p != (struct node *)head; p = (struct node *)p->link.next) {
printf("%d ", p->data);
}
printf("\n");
}

int main()
{
struct list_head head = { NULL, NULL };
init_list_head(&head); // 用于初始化双向链表头部节点。

struct node *n1 = (struct node *)malloc(sizeof(struct node));
n1->data = 1;
list_add(&n1->link, &head); // 将节点添加到链表的头部

struct node *n2 = (struct node *)malloc(sizeof(struct node));
n2->data = 2;
list_add(&n2->link, &head);

struct node *n3 = (struct node *)malloc(sizeof(struct node));
n3->data = 3;
list_add_tail(&n3->link, &head); // 将节点添加到链表的尾部

printf("The original list is: ");
print_list(&head);

return 0;
}

在上述示例代码中,我们首先定义了 list_headnode 两个结构体,并通过调用 init_list_head() 函数初始化链表头部。然后,我们创建了三个 node 类型的节点,前两个节点分别通过 list_add() 函数将它们添加到链表的头部,最后一个节点通过 list_add_tail() 函数添加到链表的尾部 。最后,我们调用 print_list() 函数打印链表中的元素。

注意: 在使用 list_add()list_add_tail() 函数之前,我们要为每个新节点分配内存空间。

2. list_cut_before,list_cut_position

2.1 函数说明

函数声明 函数功能
void list_cut_before(struct list_head *new, struct list_head *head, struct list_head *entry); 它是 Linux 内核中双向链表操作的一个基本函数,用于将一段节点从原始链表中移动到另一个链表中,并将其添加到新链表的头部。
void list_cut_position(struct list_head *new, struct list_head *head, struct list_head *entry); 它是 Linux 内核中双向链表操作的一个基本函数,用于将一段节点从原始链表中移动到另一个链表中,并将其添加到新链表的头部。与list_cut_before不同的是,该函数需要指定要移动的节点的具体位置,而不是直接指定一个节点。

参数:

  • new : 要添加的新链表头部;
  • head : 原始链表的头部
  • entry : 要移动的节点

list_cut_before() 函数会将 entry 节点及其前面的所有节点从原始链表中移动到 new 所指示的链表中,并将 entry 所在位置的前一个节点作为新链表的头节点。
list_cut_position() 函数会将 entry 节点及其后面的所有节点从原始链表中移动到 new 所指示的链表中,并将 entry 所在位置作为新链表的头节点。

2.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

struct list_head {
struct list_head *next, *prev;
};

struct node {
int data;
struct list_head link;
};

void init_list_head(struct list_head *head)
{
head->next = head->prev = head;
}

void print_list(struct list_head *head)
{
struct node *p;
for (p = (struct node *)head->next; p != (struct node *)head; p = (struct node *)p->link.next) {
printf("%d ", p->data);
}
printf("\n");
}

int main()
{
struct list_head head1 = { NULL, NULL };
init_list_head(&head1);

struct node *n1 = (struct node *)malloc(sizeof(struct node));
n1->data = 1;
list_add_tail(&n1->link, &head1);

struct node *n2 = (struct node *)malloc(sizeof(struct node));
n2->data = 2;
list_add_tail(&n2->link, &head1);

struct node *n3 = (struct node *)malloc(sizeof(struct node));
n3->data = 3;
list_add_tail(&n3->link, &head1);

printf("The original list is: ");
print_list(&head1);

struct list_head head2 = { NULL, NULL };
init_list_head(&head2);

// 移动节点n1和n2到另一个链表中
list_cut_before(&head2, &head1, &n2->link);
printf("The first list after move is: ");
print_list(&head1);
printf("The second list after move is: ");
print_list(&head2);

// 再次移动节点n3到另一个链表中
list_cut_position(&head2, &head1, &n3->link);
printf("The first list after second move is: ");
print_list(&head1);
printf("The second list after second move is: ");
print_list(&head2);

return 0;
}

在上述示例代码中,我们首先定义了 list_headnode 两个结构体,并通过调用 init_list_head() 函数初始化两个链表的头部。然后,我们创建了三个 node 类型的节点并分别将它们添加到第一个链表的尾部。接着,我们利用list_cut_before()函数和 list_cut_position() 函数将链表中的一段节点移动到第二个链表中。最后,我们调用 print_list 函数分别打印两个链表中的元素。

3. list_del,list_del_init,list_del_init_careful

3.1 函数说明

函数声明 函数功能
void list_del(struct list_head *entry); 用于从链表中删除一个节点,但不会修改该节点的指针信息。
void list_del_init(struct list_head *entry); 用于从链表中删除一个节点,但会将被删除的节点的指针信息初始化为NULL。
void list_del_init_careful(struct list_head *entry, struct list_head *prev, struct list_head *next); 用于从链表中删除一个节点,但需要指定该节点的前驱节点和后继节点,以确保链表结构正确。

参数:

  • entry : 要删除的节点
  • prev : 该节点的前驱节点
  • next : 该节点的后继节点

3.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

struct list_head {
struct list_head *next, *prev;
};

struct node {
int data;
struct list_head link;
};

void init_list_head(struct list_head *head)
{
head->next = head->prev = head;
}

void print_list(struct list_head *head)
{
struct node *p;
for (p = (struct node *)head->next; p != (struct node *)head; p = (struct node *)p->link.next) {
printf("%d ", p->data);
}
printf("\n");
}

int main()
{
struct list_head head = { NULL, NULL };
init_list_head(&head);

struct node *n1 = (struct node *)malloc(sizeof(struct node));
n1->data = 1;
list_add_tail(&n1->link, &head);

struct node *n2 = (struct node *)malloc(sizeof(struct node));
n2->data = 2;
list_add_tail(&n2->link, &head);

struct node *n3 = (struct node *)malloc(sizeof(struct node));
n3->data = 3;
list_add_tail(&n3->link, &head);

printf("The original list is: ");
print_list(&head);

// 删除节点n2,但不改变其指针信息
list_del(&n2->link);
printf("The list after delete n2 is: ");
print_list(&head);

// 删除节点n3,并初始化其指针信息为NULL
list_del_init(&n3->link);
printf("The list after delete and init n3 is: ");
print_list(&head);

// 删除节点n1,并指定其前驱和后继节点
list_del_init_careful(&n1->link, &head, head.next);
printf("The list after careful delete n1 is: ");
print_list(&head);

return 0;
}

在上述示例代码中,我们首先定义了 list_headnode 两个结构体,并通过调用 init_list_head() 函数初始化链表头部。然后,我们创建了三个 node 类型的节点并分别将它们添加到链表的尾部。接下来,我们利用 list_del()list_del_init()list_del_init_careful() 函数从链表中删除节点,并打印每次操作后的链表元素。

注意: 在使用这些函数之前,我们要确保被删除的节点在链表中。

4. list_empty,list_empty_careful

4.1 函数说明

函数声明 函数功能
int list_empty(const struct list_head *head); 用于判断链表是否为空,并返回非零值表示为空,返回0表示不为空
int list_empty_careful(const struct list_head *head); 用于判断链表是否为空,但会先检查链表头部的指针是否为空,以避免对空指针进行解引用。

参数:

  • head : 要判断的链表头部

4.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

struct list_head {
struct list_head *next, *prev;
};

struct node {
int data;
struct list_head link;
};

void init_list_head(struct list_head *head)
{
head->next = head->prev = head;
}

void print_list(struct list_head *head)
{
struct node *p;
for (p = (struct node *)head->next; p != (struct node *)head; p = (struct node *)p->link.next) {
printf("%d ", p->data);
}
printf("\n");
}

int main()
{
struct list_head head = { NULL, NULL };
init_list_head(&head);

printf("Is the list empty? %d\n", list_empty(&head));
printf("Is the list empty carefully? %d\n", list_empty_careful(&head));

struct node *n1 = (struct node *)malloc(sizeof(struct node));
n1->data = 1;
list_add_tail(&n1->link, &head);

printf("The list after adding n1: ");
print_list(&head);
printf("Is the list empty? %d\n", list_empty(&head));
printf("Is the list empty carefully? %d\n", list_empty_careful(&head));

struct node *n2 = (struct node *)malloc(sizeof(struct node));
n2->data = 2;
list_add_tail(&n2->link, &head);

printf("The list after adding n2: ");
print_list(&head);
printf("Is the list empty? %d\n", list_empty(&head));
printf("Is the list empty carefully? %d\n", list_empty_careful(&head));

return 0;
}

在上述示例代码中,我们首先定义了 list_headnode 两个结构体,并通过调用init_list_head()函数初始化链表头部。然后,我们利用list_empty()和list_empty_careful()函数分别判断链表是否为空,并打印其返回值。接下来,我们创建了两个node类型的节点并分别将它们添加到链表的尾部。每次添加节点后,我们再次使用list_empty和list_empty_careful函数判断链表是否为空,并打印其返回值。

需要注意的是,在使用这些函数之前,我们要确保链表头部已经初始化。

5. Linux 内核中双向链表遍历相关宏

宏定义 宏描述
#define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) 用于获取一个节点所在结构体的起始地址。
static inline int list_entry_is_head(const struct list_head *entry, const struct list_head *head) { return entry->prev == head; } 用于判断给定节点是否为链表头。
#define list_first_entry(ptr, type, member) list_entry((ptr)->next, type, member) 用于获取链表中第一个节点所在结构体的起始地址。
#define list_first_entry_or_null(ptr, type, member) ({ struct list_head *__head = (ptr); struct list_head *__pos = __head->next; __pos != __head ? list_entry(__pos, type, member) : NULL; }) 用于获取链表中第一个节点所在结构体的起始地址,但会先检查链表是否为空,以避免对空指针进行解引用。
#define list_next_entry(pos, member) list_entry((pos)->member.next, typeof(*(pos)), member) 用于获取链表中紧随给定节点之后的节点所在结构体的起始地址。
#define list_last_entry(ptr, type, member) list_entry((ptr)->prev, type, member) 用于获取链表中最后一个节点所在结构体的起始地址。
#define list_prepare_entry(pos, ptr, member) ((pos) ? : list_entry(ptr, typeof(*pos), member)) 用于准备一个节点的数据结构指针。如果该指针为NULL,则将其初始化为链表的头部。
#define list_prev_entry(pos, member) list_entry((pos)->member.prev, typeof(*(pos)), member) 用于获取链表中紧靠给定节点之前的节点所在结构体的起始地址。
#define list_for_each(pos, head) for (pos = (head)->next; pos != (head); pos = pos->next) 遍历链表中的所有节点
#define list_for_each_continue(pos, head) for (pos = pos->next; pos != (head); pos = pos->next) 从当前节点继续遍历链表中的剩余节点。
#define list_for_each_prev(pos, head) for (pos = (head)->prev; pos != (head); pos = pos->prev) 从链表尾部开始遍历所有节点。
#define list_for_each_safe(pos, n, head) for (pos = (head)->next, n = pos->next; pos != (head); pos = n, n = pos->next) 与list_for_each函数类似,但允许在遍历过程中删除或添加节点。其中,n参数表示要处理的下一个节点。
#define list_for_each_prev_safe(pos, n, head) for (pos = (head)->prev, n = pos->prev; pos != (head); pos = n, n = pos->prev) 与list_for_each_safe函数类似,但遍历顺序是从链表尾部开始。
#define list_for_each_entry(pos, head, member) for (pos = list_first_entry(head, typeof(*pos), member); &pos->member != (head); pos = list_next_entry(pos, member)) 用于在遍历链表时,获取每个节点所在结构体的起始地址。其中,pos参数表示当前节点所在结构体的指针;head参数表示要遍历的链表头部指针;member参数表示每个节点在结构体中的成员名称。
#define list_for_each_entry_reverse(pos, head, member) for (pos = list_last_entry(head, typeof(*pos), member); &pos->member != (head); pos = list_prev_entry(pos, member)) 与list_for_each_entry函数类似,但遍历顺序是从链表尾部开始。
#define list_for_each_entry_continue(pos, head, member) for (pos = list_next_entry(pos, member); &pos->member != (head); pos = list_next_entry(pos, member)) 用于从当前节点继续往后遍历链表,并获取每个节点所在结构体的起始地址。
#define list_for_each_entry_continue_reverse(pos, head, member) for (pos = list_prev_entry(pos, member); &pos->member != (head); pos = list_prev_entry(pos, member)) 与list_for_each_entry_continue函数类似,但遍历顺序是从链表尾部开始。
#define list_for_each_entry_from(pos, head, member) for (; &pos->member != (head); pos = list_next_entry(pos, member)) 用于从某个节点开始遍历链表,并获取每个节点所在结构体的起始地址。其中,pos参数表示当前要遍历的节点所在结构体的指针;head参数表示要遍历的链表头部指针;member参数表示每个节点在结构体中的成员名称。
#define list_for_each_entry_from_reverse(pos, head, member) for (; &pos->member != (head); pos = list_prev_entry(pos, member)) 与list_for_each_entry_from函数类似,但遍历顺序是从链表尾部开始。
#define list_for_each_entry_safe(pos, n, head, member) for (pos = list_first_entry(head, typeof(*pos), member), n = list_next_entry(pos, member); &pos->member != (head); pos = n, n = list_next_entry(n, member)) 与list_for_each_entry函数类似,但允许在遍历过程中删除或添加节点。其中,n参数表示要处理的下一个节点。
#define list_for_each_entry_safe_continue(pos, n, head, member) for (pos = list_next_entry(pos, member), n = list_next_entry(pos, member); &pos->member != (head); pos = n, n = list_next_entry(n, member)) 用于从当前节点继续往后遍历链表,并允许在遍历过程中删除或添加节点。
#define list_for_each_entry_safe_from(pos, n, head, member) for (n = list_next_entry(pos, member); &pos->member != (head); pos = n, n = list_next_entry(n, member)) 用于从某个节点开始遍历链表,并允许在遍历过程中删除或添加节点。
#define list_for_each_entry_safe_reverse(pos, n, head, member) for (pos = list_last_entry(head, typeof(*pos), member), n = list_prev_entry(pos, member); &pos->member != (head); pos = n, n = list_prev_entry(n, member)) 与list_for_each_entry_reverse函数类似,但允许在遍历过程中删除或添加节点。
#define list_is_first(pos, head) ((pos)->prev == (head)) 用于检查当前节点是否为链表中的第一个节点。其中,pos 参数表示要检查的节点指针;head 参数表示链表头部指针。
#define list_is_last(pos, head) ((pos)->next == (head)) 用于检查当前节点是否为链表中的最后一个节点。其中,pos 参数表示要检查的节点指针;head 参数表示链表头部指针。
#define list_is_head(pos, head) ((pos) == (head)) 用于检查当前节点是否为链表头部。其中,pos参数表示要检查的节点指针;head参数表示链表头部指针。
#define list_is_singular(head) (!list_empty(head) && ((head)->next == (head)->prev)) 用于检查链表中是否只有一个节点。其中,head参数表示链表头部指针。
#define list_safe_reset_next(curr, next, member) next = list_entry((curr)->member.next, typeof(*curr), member) 用于安全地重置一个节点的后继节点指针,以便在遍历链表时删除当前节点。其中,curr 参数表示当前节点指针;next 参数表示当前节点的后继节点指针;member 参数表示节点结构体中 struct list_head 成员的名称。

6. list_move,list_move_tail,list_bulk_move_tail

6.1 函数说明

函数声明 函数功能
void list_move(struct list_head *list, struct list_head *head); 用于将一个节点移动到另外一个链表的头部。
void list_move_tail(struct list_head *list, struct list_head *head); 用于将一个节点移动到另外一个链表的尾部。
void list_bulk_move_tail(struct list_head *list, int count, struct list_head *head); 用于将多个节点从一个链表移动到另一个链表的尾部。

参数:

  • list : 要移动的节点指针
  • head : 目标链表头部指针
  • count : 要移动的节点数量

6.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <stdio.h>
#include <stdlib.h>
#include "list.h"

struct my_struct {
int data;
struct list_head list;
};

int main() {
struct list_head a, b;
struct my_struct s1, s2, s3, *pos, *tmp;

// 初始化两个链表
INIT_LIST_HEAD(&a);
INIT_LIST_HEAD(&b);

// 添加三个结构体到链表 a 中
s1.data = 10;
list_add_tail(&s1.list, &a);

s2.data = 20;
list_add_tail(&s2.list, &a);

s3.data = 30;
list_add_tail(&s3.list, &a);

// 将节点 s1 移动到链表 b 的头部
printf("Before move:\n");
printf("List a: ");
list_for_each_entry(pos, &a, list) {
printf("%d ", pos->data);
}
printf("\n");

printf("List b: ");
list_for_each_entry(pos, &b, list) {
printf("%d ", pos->data);
}
printf("\n");

list_move(&s1.list, &b);

printf("After move:\n");
printf("List a: ");
list_for_each_entry(pos, &a, list) {
printf("%d ", pos->data);
}
printf("\n");

printf("List b: ");
list_for_each_entry(pos, &b, list) {
printf("%d ", pos->data);
}
printf("\n");

// 将节点 s2 移动到链表 b 的尾部
printf("Before move_tail:\n");
printf("List a: ");
list_for_each_entry(pos, &a, list) {
printf("%d ", pos->data);
}
printf("\n");

printf("List b: ");
list_for_each_entry(pos, &b, list) {
printf("%d ", pos->data);
}
printf("\n");

list_move_tail(&s2.list, &b);

printf("After move_tail:\n");
printf("List a: ");
list_for_each_entry(pos, &a, list) {
printf("%d ", pos->data);
}
printf("\n");

printf("List b: ");
list_for_each_entry(pos, &b, list) {
printf("%d ", pos->data);
}
printf("\n");

// 将链表 a 中的所有节点移动到链表 b 的尾部
printf("Before bulk_move_tail:\n");
printf("List a: ");
list_for_each_entry(pos, &a, list) {
printf("%d ", pos->data);
}
printf("\n");

printf("List b: ");
list_for_each_entry(pos, &b, list) {
printf("%d ", pos->data);
}
printf("\n");

list_bulk_move_tail(&a, 3, &b);

printf("After bulk_move_tail:\n");
printf("List a: ");
list_for_each_entry(pos, &a, list) {
printf("%d ", pos->data);
}
printf("\n");

printf("List b: ");
list_for_each_entry(pos, &b, list) {
printf("%d ", pos->data);
}
printf("\n");

// 释放内存
list_for_each_entry_safe(pos, tmp, &a, list) {
list_del(&pos->list);
free(pos);
}

list_for_each_entry_safe(pos, tmp, &b, list) {
list_del(&pos->list);
free(pos);
}

return 0;
}

上述示例代码中,我们首先创建了两个链表 ab,然后向链表 a 中添加三个结构体。接着,我们使用 list_move() 函数将节点 s1 从链表 a 移动到链表 b 的头部,使用 list_move_tail() 函数将节点 s2 从链表 a 移动到链表 b 的尾部,最后使用 list_bulk_move_tail() 函数将链表 a 中的所有节点都移动到链表 b 的尾部。

注意: 在上述演示代码的最后,我们需要手动释放所有节点的内存空间,以免造成内存泄漏。

7. list_replace,list_replace_init

7.1 函数说明

函数声明 函数功能
void list_replace(struct list_head *old, struct list_head *new); 用于用一个新节点替换指定节点。
void list_replace_init(struct list_head *old, struct list_head *new); 除了可以完成 list_replace 做的所有操作外,它还将原来的节点初始化为空。

参数:

  • old : 要被替换的节点指针;
  • new : 新节点的指针。

7.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>
#include "list.h"

struct my_struct {
int data;
struct list_head list;
};

int main() {
struct list_head a;
struct my_struct s1, s2, s3, s4;

// 初始化链表
INIT_LIST_HEAD(&a);

// 添加三个结构体到链表中
s1.data = 10;
list_add_tail(&s1.list, &a);

s2.data = 20;
list_add_tail(&s2.list, &a);

s3.data = 30;
list_add_tail(&s3.list, &a);

printf("Before replace:\n");
list_for_each_entry(struct my_struct, pos, &a, list) {
printf("%d ", pos->data);
}
printf("\n");

// 替换第二个节点
s4.data = 40;
list_replace(&s2.list, &s4.list);

printf("After replace:\n");
list_for_each_entry(struct my_struct, pos, &a, list) {
printf("%d ", pos->data);
}
printf("\n");

// 替换第一个节点,并且清空原来的节点
s4.data = 50;
list_replace_init(&s1.list, &s4.list);

printf("After replace_init:\n");
list_for_each_entry(struct my_struct, pos, &a, list) {
printf("%d ", pos->data);
}
printf("\n");

// 释放内存
list_del(&s3.list);
list_del(&s4.list);

return 0;
}

在上面的示例代码中,我们首先创建了一个链表 a,然后向其中添加三个结构体。接着,我们使用 list_replace() 函数将第二个节点 s2 替换成新节点 s4,并打印出替换后的链表元素;然后,我们使用 list_replace_init() 函数将第一个节点 s1 替换成新节点 s4,并清空原来的节点,同样打印出替换后的链表元素。

8. list_rotate_left,list_rotate_to_front

8.1 函数说明

函数声明 函数功能
static inline void list_rotate_left(struct list_head *head) 用于将链表向左旋转一个位置。
void list_rotate_to_front(struct list_head *head, struct list_head *pivot); 用于将指定节点移到链表头部,并旋转链表使得该节点成为新的头部。

参数:

  • head : 链表头部指针
  • pivot : 要移到链表头部的节点指针。

8.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <stdio.h>
#include "list.h"

struct my_struct {
int data;
struct list_head list;
};

int main() {
struct list_head a;
struct my_struct s1, s2, s3;

// 初始化链表
INIT_LIST_HEAD(&a);

// 添加三个结构体到链表中
s1.data = 10;
list_add_tail(&s1.list, &a);

s2.data = 20;
list_add_tail(&s2.list, &a);

s3.data = 30;
list_add_tail(&s3.list, &a);

printf("Before rotate:\n");
list_for_each_entry(struct my_struct, pos, &a, list) {
printf("%d ", pos->data);
}
printf("\n");

// 将第二个节点移到链表头部,并旋转链表
list_rotate_to_front(&a, &s2.list);

printf("After rotate:\n");
list_for_each_entry(struct my_struct, pos, &a, list) {
printf("%d ", pos->data);
}
printf("\n");

// 向左旋转一个位置
list_rotate_left(&a);

printf("After rotate_left:\n");
list_for_each_entry(struct my_struct, pos, &a, list) {
printf("%d ", pos->data);
}
printf("\n");

// 释放内存
list_del(&s1.list);
list_del(&s2.list);
list_del(&s3.list);

return 0;
}

在上面的示例代码中,我们首先创建了一个链表 a,然后向其中添加三个结构体。接着,我们使用 list_rotate_to_front() 函数将第二个节点 s2 移到链表头部并旋转链表,打印出操作后的链表元素;然后,我们使用 list_rotate_left() 函数将链表向左旋转一个位置,同样打印出操作后的链表元素。

9. list_splice,list_splice_tail,list_splice_init,list_splice_tail_init

9.1 函数说明

函数声明 函数功能
void list_splice(struct list_head *list, struct list_head *head); 用于将一个链表中的所有节点插入到另一个链表的指定位置之前。
void list_splice_tail(struct list_head *list, struct list_head *head); 用于将一个链表中的所有节点插入到另一个链表的尾部。
void list_splice_init(struct list_head *list, struct list_head *head); 除了可以完成 list_splice 做的所有操作外,它还将原来的链表初始化为空。
void list_splice_tail_init(struct list_head *list, struct list_head *head); 除了可以完成 list_splice_tail 做的所有操作外,它还将原来的链表初始化为空。
参数:
  • list : 要插入的链表头部指针
  • head :
    • list_splice()list_splice_init() 中表示目标链表插入的位置
    • list_splice_tail()list_splice_tail_init() 中表示目标链表尾部的前一个节点

9.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include <stdio.h>
#include "list.h"

struct my_struct {
int data;
struct list_head list;
};

int main() {
struct list_head a, b;
struct my_struct s1, s2, s3, s4, s5;

// 初始化两个链表
INIT_LIST_HEAD(&a);
INIT_LIST_HEAD(&b);

// 向链表 a 中添加三个结构体
s1.data = 10;
list_add_tail(&s1.list, &a);

s2.data = 20;
list_add_tail(&s2.list, &a);

s3.data = 30;
list_add_tail(&s3.list, &a);

// 向链表 b 中添加两个结构体
s4.data = 40;
list_add_tail(&s4.list, &b);

s5.data = 50;
list_add_tail(&s5.list, &b);

printf("Before splice:\n");
printf("List a:\n");
list_for_each_entry(struct my_struct, pos, &a, list) {
printf("%d ", pos->data);
}
printf("\nList b:\n");
list_for_each_entry(struct my_struct, pos, &b, list) {
printf("%d ", pos->data);
}
printf("\n");

// 将链表 b 中的所有节点插入到链表 a 的头部
list_splice(&b, &a);

printf("After splice:\n");
printf("List a:\n");
list_for_each_entry(struct my_struct, pos, &a, list) {
printf("%d ", pos->data);
}
printf("\nList b:\n");
list_for_each_entry(struct my_struct, pos, &b, list) {
printf("%d ", pos->data);
}
printf("\n");

// 将链表 b 中的所有节点插入到链表 a 的尾部
list_splice_tail(&b, &a);

printf("After splice tail:\n");
printf("List a:\n");
list_for_each_entry(struct my_struct, pos, &a, list) {
printf("%d ", pos->data);
}
printf("\nList b:\n");
list_for_each_entry(struct my_struct, pos, &b, list) {
printf("%d ", pos->data);
}
printf("\n");

// 重新初始化链表 a 并将链表 b 中的所有节点插入到链表 a 的头部
INIT_LIST_HEAD(&a);
list_splice_init(&b, &a);

printf("After splice init:\n");
printf("List a:\n");
list_for_each_entry(struct my_struct, pos, &a, list) {
printf("%d ", pos->data);
}
printf("\nList b:\n");
list_for_each_entry(struct my_struct, pos, &b, list) {
printf("%d ", pos->data);
}
printf("\n");

// 将链表 b 中的所有节点插入到链表 a 的尾部,并初始化链表 b
INIT_LIST_HEAD(&b);
list_splice_tail_init(&a, &b);

printf("After splice tail init:\n");
printf("List a:\n");
list_for_each_entry(struct my_struct, pos, &a, list) {
printf("%d ", pos->data);
}
printf("\nList b:\n");
list_for_each_entry(struct my_struct, pos, &b, list) {
printf("%d ", pos->data);
}
printf("\n");

return 0;
}

上述演示代码中,我们创建了两个链表 ab,并初始化为空。然后,我们向链表 a 中添加三个结构体,向链表 b 中添加两个结构体,并使用 list_for_each_entry 宏分别遍历两个链表并输出节点数据。

接着,我们使用 list_splice() 函数将链表 b 中的所有节点插入到链表 a 的头部,使用 list_splice_tail() 函数将链表 b 中的所有节点插入到链表 a 的尾部,并使用 list_for_each_entry 宏再次遍历两个链表并输出节点数据,可以看到链表 a 中包含了链表 b 中的所有节点。

接下来,我们使用 INIT_LIST_HEAD 宏重新初始化链表 a 并使用 list_splice_init() 函数将链表 b 中的所有节点插入到链表 a 的头部,使用 INIT_LIST_HEAD 宏重新初始化链表 b 并使用 list_splice_tail_init() 函数将链表 a 中的所有节点插入到链表 b 的尾部,并使用 list_for_each_entry 宏再次遍历两个链表并输出节点数据,可以看到两个链表中的节点顺序已经被重新排列。

10. list_swap

10.1 函数说明

函数声明 函数功能
void list_swap(struct list_head *list1, struct list_head *list2); 交换两个链表头部的位置。
参数:
list1list2 分别指向两个要交换头部的链表。

10.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>
#include "list.h"

struct my_struct {
int data;
struct list_head list;
};

int main() {
struct list_head a, b;
struct my_struct s1, s2, s3, s4;

// 初始化两个链表
INIT_LIST_HEAD(&a);
INIT_LIST_HEAD(&b);

// 向链表 a 中添加三个结构体
s1.data = 10;
list_add_tail(&s1.list, &a);

s2.data = 20;
list_add_tail(&s2.list, &a);

s3.data = 30;
list_add_tail(&s3.list, &a);

// 向链表 b 中添加一个结构体
s4.data = 40;
list_add_tail(&s4.list, &b);

printf("Before swap:\n");
printf("List a:\n");
list_for_each_entry(struct my_struct, pos, &a, list) {
printf("%d ", pos->data);
}
printf("\nList b:\n");
list_for_each_entry(struct my_struct, pos, &b, list) {
printf("%d ", pos->data);
}
printf("\n");

// 交换链表 a 和链表 b 的头部
list_swap(&a, &b);

printf("After swap:\n");
printf("List a:\n");
list_for_each_entry(struct my_struct, pos, &a, list) {
printf("%d ", pos->data);
}
printf("\nList b:\n");
list_for_each_entry(struct my_struct, pos, &b, list) {
printf("%d ", pos->data);
}
printf("\n");

return 0;
}

在上述示例中,我们创建了两个链表 ab,并向链表 a 中添加三个节点,向链表 b 中添加一个节点。然后我们使用 list_for_each_entry 宏遍历两个链表并输出节点数据。

接着,我们使用 list_swap() 函数交换链表 a 和链表 b 的头部,并使用 list_for_each_entry 宏再次遍历两个链表并输出节点数据,可以看到链表 a 的头部变成了原来的链表 b 的头部,链表 b 的头部变成了原来的链表 a 的头部。

参考

  1. [The Linux Kernel API]

C语言函数大全--l开头的函数

总览

函数声明 函数功能
long labs(long n); 计算长整型的绝对值
long long int llabs(long long int n); 计算long long int 类型整数的绝对值
double ldexp(double x, int exp); 计算 x 乘以 2 的指定次幂(double)
float ldexpf(float x, int exp); 计算 x 乘以 2 的指定次幂(float)
long double ldexpl(long double x, int exp); 计算 x 乘以 2 的指定次幂(long double)
ldiv_t ldiv(long int numer, long int denom); 计算两个 long int 类型整数的商和余数
lldiv_t lldiv(long long int numer, long long int denom); 计算两个 long long int 类型整数的商和余数
void *lfind(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); 它是标准 C 库函数 <search.h> 中的一个函数,用于在一个数组中查找指定元素。
void line( int x1, int y1, int x2, int y2); 在指定两点间画一直线
void linerel(int dx, int dy); 从当前位置绘制一条指定长度和方向的线段。
void lineto(int x, int y); 从当前位置绘制一条直线到指定位置
struct tm *localtime(const time_t *timep); 将 UNIX 时间戳转换为本地时间
int lock(int fd, int cmd, off_t len); 它是标准 C 库函数 <fcntl.h> 中的一个函数,用于对文件进行加锁或解锁操作
double log(double x); 计算自然对数
double log10(double x); 计算以 10 为底的对数
void longjmp(jmp_buf env, int val); 跳转到指定的程序位置并恢复相应的上下文环境
void lowvideo(void); 用于将文本颜色设置为低对比度模式
unsigned long _lrotl(unsigned long value, int shift); 它是 Windows 系统特有的函数,用于将 32 位无符号整数按位循环左移。
void *lsearch(const void *key, void *base, size_t *nelp, size_t width, int (*compar)(const void *, const void *)); 用于在指定的数组中查找指定元素,并返回该元素在数组中的地址
long lseek(int handle, long offset, int fromwhere); 设置文件操作指针,即改变文件读取或写入的位置
char *ltoa(long value, char *str, int radix); 用于将长整型数值转换为字符串格式
char *lltoa(long long value, char *str, int radix); 用于将长长整型数值转换为字符串格式

1. labs,llabs

1.1 函数说明

函数声明 函数功能
long labs(long n); 计算长整型的绝对值
long long int llabs(long long int n); 计算long long int 类型整数的绝对值

1.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
long result;
long x = -12345678L;
result= labs(x);
printf("number: %ld , abs value: %ld\n", x, result);

long long resultL;
long long int xL = -1234567890123456789;
resultL = llabs(xL);
printf("The absolute value of %lld is %lld\n", xL, resultL);

return 0;
}

1.3 运行结果

2. ldexp,ldexpf,ldexpl

2.1 函数说明

函数声明 函数功能
double ldexp(double x, int exp); 计算 x 乘以 2 的指定次幂(double)
float ldexpf(float x, int exp); 计算 x 乘以 2 的指定次幂(float)
long double ldexpl(long double x, int exp); 计算 x 乘以 2 的指定次幂(long double)

2.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <math.h>

int main()
{
int n = 3;
double x = 3.5, result;
result = ldexp(x, n);

float xf = 3.5f, resultf;
resultf = ldexpf(xf, n);

long double xL = 3.5L, resultL;
resultL = ldexpl(xL, n);

printf("ldexp(%lf, %d) = %lf\n", x, n, result);
printf("ldexpf(%f, %d) = %f\n", xf, n, resultf);
printf("ldexpl(%Lf, %d) = %Lf\n", xL, n, resultL);

return 0;
}

注意:ldexp,ldexpf,ldexpl 函数会对参数进行溢出和下溢处理,因此可以处理很大或很小的数值。

2.3 运行结果

3. ldiv,lldiv

3.1 函数说明

函数声明 函数功能
ldiv_t ldiv(long int numer, long int denom); 计算两个 long int 类型整数的商和余数
lldiv_t lldiv(long long int numer, long long int denom); 计算两个 long long int 类型整数的商和余数

参数:

  • numer : 被除数
  • denom : 除数

ldiv 函数的返回值类型 ldiv_t 是一个结构体类型,定义如下:

1
2
3
4
typedef struct {
long int quot; // 商
long int rem; // 余数
} ldiv_t;

lldiv 函数的返回值类型 lldiv_t 是一个结构体类型,定义如下:

1
2
3
4
typedef struct {
long long int quot; // 商
long long int rem; // 余数
} lldiv_t;

3.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <stdlib.h>

int main()
{
long int numer = 1234567890;
long int denom = 987654321;
ldiv_t result;

result = ldiv(numer, denom);

printf("%ld / %ld = %ld, %ld %% %ld = %ld\n", numer, denom,
result.quot, numer, denom, result.rem);

long long int numerL = 1234567890123456789LL;
long long int denomL = 987654321LL;
lldiv_t resultL;

resultL = lldiv(numerL, denomL);

printf("%lld / %lld = %lld, %lld %% %lld = %lld\n", numerL, denomL,
resultL.quot, numerL, denomL, resultL.rem);

return 0;
}

注意: 如果 denom 参数为零,则 ldiv() 函数会产生一个异常情况。此外,如果两个参数中有一个或两个都是负数,则商和余数的计算规则将根据 C 标准进行调整。

3.3 运行结果

4. lfind

4.1 函数说明

函数声明 函数功能
void *lfind(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); 它是标准 C 库函数 <search.h> 中的一个函数,用于在一个数组中查找指定元素。

参数:

  • key : 要查找的元素
  • base : 要查找的数组的首地址
  • nmemb : 数组元素个数
  • size : 每个数组元素的大小(以字节为单位)
  • compar : 比较函数,用于比较数组元素和要查找的元素。compar 函数需要返回一个整数值,表示两个元素之间的关系:
    • 如果第一个元素小于第二个元素,则返回一个负数。
    • 如果第一个元素等于第二个元素,则返回零。
    • 如果第一个元素大于第二个元素,则返回一个正数。

4.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <stdlib.h>
#include <search.h>

int compare(const void *a, const void *b)
{
return (*(int*)a - *(int*)b);
}

int main()
{
int arr[] = {1, 2, 3, 4, 5};
int n = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < n; i++)
printf("%d ", arr[i]);

unsigned int * number = (unsigned int *)&n;
int key = 2;
int *result;

result = (int *)lfind(&key, arr, number, sizeof(int), compare);

if (result != NULL) {
printf("\n%d is found at index %lld\n", key, result - arr);
} else {
printf("\n%d is not found in the array\n", key);
}

return 0;
}

注意: lfind() 函数使用线性搜索算法,因此对于大规模数据可能不太适用。除此之外,该函数还有一些变种函数,例如 bsearch()tfind() 等,也可以用于在数组或树结构中查找元素。

4.3 运行结果

5. line

5.1 函数说明

函数声明 函数功能
void line( int x1, int y1, int x2, int y2); 在指定两点间画一直线
参数:

(x1, y1) : 第一个点的坐标
(x2, y3) : 第二个点的坐标

5.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <graphics.h>

int main(void)
{
int gdriver = DETECT, gmode;
int xmax, ymax;

initgraph(&gdriver, &gmode, "");

setcolor(getmaxcolor());
xmax = getmaxx();
ymax = getmaxy();

// 在(0,0) 和(xmax, ymax)之间画一直线
line(0, 0, xmax, ymax);

// 在(0,ymax) 和(xmax, 0)之间画一直线
line(0, ymax, xmax, 0);

/* clean up */
getch();
closegraph();
return 0;
}

5.3 运行结果

6. linerel

6.1 函数说明

函数声明 函数功能
void linerel(int dx, int dy); 从当前位置绘制一条指定长度和方向的线段。
参数:
  • dx : 线段在 X 轴上的位移量
  • dy : 线段在 Y 轴上的位移量

6.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <graphics.h>

int main()
{
int gd = DETECT, gm;
initgraph(&gd, &gm, "");

moveto(100, 100); // 将当前点移动到 (100, 100)
linerel(50, 0); // 绘制长度为 50,方向为水平(X 轴正方向)的线段
linerel(0, 50); // 绘制长度为 50,方向为垂直(Y 轴正方向)的线段
linerel(-50, 0); // 绘制长度为 50,方向为水平(X 轴反方向)的线段
linerel(0, -50); // 绘制长度为 50,方向为垂直(Y 轴反方向)的线段

getch();
closegraph();

return 0;
}

6.3 运行结果

7. lineto

7.1 函数说明

函数声明 函数功能
void lineto(int x, int y); 从当前位置绘制一条直线到指定位置
参数:

x : 线段终点的 X 坐标
y : 线段终点的 Y 坐标

7.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <graphics.h>

int main()
{
int gd = DETECT, gm;
initgraph(&gd, &gm, "");

moveto(100, 100); // 将当前点移动到 (100, 100)
lineto(150, 150); // 绘制一条从当前点到 (150, 150) 的线段
lineto(200, 100); // 绘制一条从当前点到 (200, 100) 的线段
lineto(150, 50); // 绘制一条从当前点到 (150, 50) 的线段
lineto(100, 100); // 绘制一条从当前点到 (150, 50) 的线段

getch();
closegraph();

return 0;
}

7.3 运行结果

8. localtime

8.1 函数说明

函数声明 函数功能
struct tm *localtime(const time_t *timep); 将 UNIX 时间戳转换为本地时间
参数:
  • timep : 指向 time_t 类型的指针,表示要转换的 UNIX 时间戳

返回值:

  • struct tm * : 一个指向 struct tm 类型的指针,该结构体包含了表示本地时间的各个字段,例如年、月、日、时、分、秒等。

8.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <time.h>

int main()
{
time_t now;
struct tm *local;

now = time(NULL); // 获取当前时间戳
local = localtime(&now); // 将当前时间戳转换为本地时间

printf("Current date and time: %s\n", asctime(local));

return 0;
}

注意: 在使用 localtime() 函数时需要注意结构体中的字段值是否正确,例如月份、星期等的表示方式可能因不同系统而异。

8.3 运行结果

9. lock

9.1 函数说明

函数声明 函数功能
int lock(int fd, int cmd, off_t len); 它是标准 C 库函数 <fcntl.h> 中的一个函数,用于对文件进行加锁或解锁操作
参数:
  • fd : 文件描述符
  • cmd : 要执行的加锁或解锁操作(例如 F_LOCK、F_ULOCK 等)
  • len : 要锁定的字节数。

返回值:

返回一个整数值表示操作是否成功,若成功则返回 0,否则返回 -1

9.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <fcntl.h>

int main()
{
int fd;
char buf[128];
int nbytes;

fd = open("test.txt", O_RDWR | O_CREAT, 0666);
if (fd == -1) {
perror("open");
return 1;
}

// 加锁
if (lock(fd, F_LOCK, 0) == -1) {
perror("lock");
return 1;
}

// 写入数据
sprintf(buf, "Hello, world!\n");
nbytes = write(fd, buf, sizeof(buf));
if (nbytes == -1) {
perror("write");
return 1;
}

// 解锁
if (lock(fd, F_ULOCK, 0) == -1) {
perror("unlock");
return 1;
}

close(fd);

return 0;
}

上述示例程序中,首先通过 open() 函数打开一个名为 test.txt 的文件,并设置文件访问模式为可读写。接着,调用 lock() 函数对该文件进行加锁操作,保护写入数据的过程。然后,通过 write() 函数将数据写入到文件中。最后,调用 lock() 函数对该文件进行解锁操作,释放锁定的资源。

注意: 在使用 lock() 函数时需要注意加锁和解锁的顺序、范围等问题,否则可能会造成死锁或其他问题。此外,该函数只适用于文件系统,不能用于套接字等其他类型的文件描述符。

10. log

10.1 函数说明

函数声明 函数功能
double log(double x); 计算自然对数
参数:
  • x : 要计算自然对数的数字。

返回值:

  • x 的自然对数,即 ln(x)

10.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <math.h>

int main()
{
double x = 2.0;
double result = log(x);

printf("The natural logarithm of %lf is %lf.\n", x, result);

return 0;
}

注意: 由于 log() 函数接受的参数和返回值都是 double 类型,因此在使用时需要保证传入的参数类型正确,避免发生精度损失等问题。同时, log() 函数的参数不能为负数或零,否则会产生不可预知的行为。

10.3 运行结果

11. log10

11.1 函数说明

函数声明 函数功能
double log10(double x); 计算以 10 为底的对数
参数:
  • x : 要计算以 10 为底的对数的数字

返回值:

  • x 的以 10 为底的对数,即 log10(x)

11.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <math.h>

int main()
{
double x = 100.0;
double result = log10(x);

printf("The logarithm base 10 of %lf is %lf.\n", x, result);

return 0;
}

11.3 运行结果

12. longjmp

12.1 函数说明

函数声明 函数功能
void longjmp(jmp_buf env, int val); 跳转到指定的程序位置并恢复相应的上下文环境
参数:
  • env : 保存上下文环境的缓冲区
  • val : 跳转时返回的值

注意:在使用 longjmp() 函数之前,需要先调用 setjmp() 函数来设置上下文环境,并将其保存在 jmp_buf 数据类型中。然后,在程序执行过程中,如果需要跳转到之前设定的位置,就可以使用 longjmp() 函数进行跳转和上下文恢复。

12.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdio.h>
#include <setjmp.h>

jmp_buf buf;

void do_something()
{
printf("do_something() start.\n");

// 跳转到 setjmp() 处
longjmp(buf, 1);

printf("do_something() end.\n");
}

int main()
{
int val = 0;

// 设置上下文环境
if (setjmp(buf) == 0) {
printf("setjmp() called.\n");
do_something();
} else {
printf("longjmp() called.\n");
val = 1;
}

printf("Program ends with value %d.\n", val);

return 0;
}

上述示例程序中,首先在主函数中调用 setjmp() 函数设置上下文环境,并将其保存在 buf 变量中。然后,程序调用 do_something() 函数,在其中调用 longjmp() 函数跳转到之前设定的位置,并返回值为 1

由于 longjmp() 调用后不会返回到调用它的位置,因此 do_something() 函数在被调用后并未执行完毕,而是直接跳转到了 setjmp() 所在的位置。当程序回到 setjmp() 处时,检测到了从 longjmp() 跳转过来的信号,并返回值为 1,表示跳转成功。

最后,程序输出 "Program ends with value 1.",结束运行。

注意: 使用 longjmp()setjmp() 函数进行跳转时,必须保证跳转的目标位置和之前设置的上下文环境是兼容的,否则可能会导致程序崩溃或其他严重问题。同时,尽管 longjmp() 可以快速跳出当前函数或代码块,但在实际应用中应该尽量避免使用它,以免造成代码逻辑混乱和难以调试的问题。

12.3 运行结果

13. lowvideo

13.1 函数说明

函数声明 函数功能
void lowvideo(void); 用于将文本颜色设置为低对比度模式

13.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <conio.h>

int main(void)
{
clrscr(); // 清空屏幕
highvideo(); // 将文本颜色设置为高对比度模式
cprintf("High Intesity Text\r\n");
lowvideo(); // 将文本颜色设置为低对比度模式
gotoxy(1,2); // 将光标移动到指定的坐标 (x, y),其中 x 和 y 分别为列和行数
cprintf("Low Intensity Text\r\n");

return 0;
}

当该程序运行时,首先清空了控制台屏幕,然后将文本颜色设置为高对比度模式并输出一段文本。接着,将文本颜色设置为低对比度模式,并将光标移动到第二行第一个字符位置,输出另外一段文本。最后,程序执行结束,并返回 0。

注意: <conio.h> 头文件中的函数在不同的操作系统和编译器下可能会有所不同,并且并非所有的平台都支持低对比度文本模式。在实际应用中,应该避免过度使用低对比度模式,以免影响用户体验和可读性。

14. _lrotl

14.1 函数说明

函数声明 函数功能
unsigned long _lrotl(unsigned long value, int shift); 它是 Windows 系统特有的函数,用于将 32 位无符号整数按位循环左移。
参数:
  • value : 要进行循环左移的 32 位无符号整数
  • shift : 左移的位数

返回值:

  • 左移后的结果。

14.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdlib.h>
#include <stdio.h>

int main()
{
unsigned long result;
unsigned long value = 2;

result = _lrotl(value, 2);
printf("The value %lu rotated left one bit is: %lu\n", value, result);

return 0;
}

注意: _lrotl() 函数是 Windows 系统特有的函数,在其他操作系统或编译器下可能不可用或使用方式有所不同。此外,由于该函数只适用于 32 位无符号整数,如果需要对 64 位整数进行位移操作,则需要使用其他函数。

14.3 运行结果

15. lsearch

15.1 函数说明

函数声明 函数功能
void *lsearch(const void *key, void *base, size_t *nelp, size_t width, int (*compar)(const void *, const void *)); 用于在指定的数组中查找指定元素,并返回该元素在数组中的地址
参数:
  • key : 要查找的元素指针
  • base : 要进行查找的数组首地址
  • nelp : 当前数组中元素的个数
  • width : 数组中每个元素所占用的字节数
  • compar : 比较函数指针,用于比较两个元素的大小关系。

15.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <stdlib.h>
#include <search.h>

int compare(const void *a, const void *b)
{
return *(int *)a - *(int *)b;
}

int main()
{
int arr[] = { 3, 1, 4, 1, 5, 9, 2, 6, 5 };
int len = sizeof(arr) / sizeof(int);

int key = 5;
int *result = lsearch(&key, arr, &len, sizeof(int), compare);

if (result != NULL) {
printf("Found %d at index %ld.\n", *result, result - arr);
} else {
printf("%d not found in the array.\n", key);
}

return 0;
}

上述示例程序中,首先定义了一个整型数组 arr 并初始化为 { 3, 1, 4, 1, 5, 9, 2, 6, 5 }。然后,将要查找的元素值 key 设置为 5,并调用 lsearch() 函数在数组中查找该元素。如果找到了,则输出该元素在数组中的下标;否则输出未找到的提示。

注意: lsearch() 函数在查找数组元素时,只能够找到第一个匹配的元素,并返回其地址。如果数组中存在多个相同的元素,则无法区分它们的位置。此外,使用 lsearch() 函数进行查找时,必须保证数组已经按照指定的比较函数从小到大排好序,否则可能会导致查找失败或找到错误的元素。

15.3 运行结果

16. lseek

16.1 函数说明

函数声明 函数功能
long lseek(int handle, long offset, int fromwhere); 设置文件操作指针,即改变文件读取或写入的位置
参数:
  • handle : 文件描述符
  • offset : 偏移量
  • whence : 偏移量的参考位置
    • SEEK_SET: 从文件开头开始计算偏移量(即绝对位置)
    • SEEK_CUR: 从当前位置开始计算偏移量(即相对位置)
    • SEEK_END: 从文件结尾开始计算偏移量(即反向偏移)

返回值:

  • 如果成功,则返回新的文件指针位置(即距离文件开头的字节数);
    • 如果发生错误,则返回 -1。

16.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main()
{
int fd = open("temp.txt", O_RDWR);
if (fd == -1) {
printf("Failed to open the file.\n");
return -1;
}

off_t pos = lseek(fd, 5, SEEK_SET);
if (pos == -1) {
printf("Failed to seek the file.\n");
close(fd);
return -1;
}

char buf[10];
ssize_t nread = read(fd, buf, 5);
if (nread == -1) {
printf("Failed to read the file.\n");
close(fd);
return -1;
}

buf[nread] = '\0';
printf("Read %ld bytes from position %ld: %s\n", nread, pos, buf);

close(fd);
return 0;
}

上述示例程序中,首先使用 open() 函数打开名为 "temp.txt" 的文件,并获取其文件描述符。然后,调用 lseek() 函数将文件指针移动到距离文件开头 5 个字节处。接着,调用 read() 函数从该位置开始读取 5 个字节的数据,并输出读取结果。

注意: lseek() 函数只能够对可寻址的文件进行操作,如磁盘文件、终端设备等,而不能对无法随机访问的流式数据进行操作,如管道、套接字等。同时,在使用 lseek() 函数时应该注意文件操作模式和文件共享模式,以免影响其他进程或线程的文件访问。

16.3 运行结果

17. ltoa

17.1 函数说明

函数声明 函数功能
char *ltoa(long value, char *str, int radix); 用于将长整型数值转换为字符串格式
参数:
  • value : 要转换的长整型数值
  • str : 保存转换结果的字符缓冲区指针
  • radix : 要转换的进制数(如2进制、 10 进制、16 进制等),取值范围为 2~36

返回值:

  • 指向转换结果的指针(即 str 参数的值)

17.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

int main()
{
long value = 123456789L;
char str[20];

ltoa(value, str, 2);
printf("The result of converting %ld to binary string is: %s\n", value, str);

ltoa(value, str, 10);
printf("The result of converting %ld to decimal string is: %s\n", value, str);

ltoa(value, str, 16);
printf("The result of converting %ld to hexadecimal string is: %s\n", value, str);

return 0;
}

注意: ltoa() 函数在将长整型数值转换为字符串时,会将负数转换为相应的带符号字符串。如果要对无符号长整型进行转换,则需要使用其他函数或技巧。此外,由于 ltoa() 函数没有对输出缓冲区溢出进行检查,因此在使用时应该确保缓冲区足够大,以免发生错误。

17.3 运行结果

18. lltoa

18.1 函数说明

函数声明 函数功能
char *lltoa(long long value, char *str, int radix); 用于将长长整型数值转换为字符串格式
参数:
  • value : 要转换的长长整型数值
  • str : 保存转换结果的字符缓冲区指针
  • radix : 要转换的进制数(如 2进制、10 进制、16 进制等),取值范围为 2~36

返回值:

  • 指向转换结果的指针(即 str 参数的值)

18.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>

int main()
{
long long value = 123456789012345LL;
char str[20];

lltoa(value, str, 2);
printf("The result of converting %lld to binary string is: %s\n", value, str);

lltoa(value, str, 10);
printf("The result of converting %lld to decimal string is: %s\n", value, str);

lltoa(value, str, 16);
printf("The result of converting %lld to hexadecimal string is: %s\n", value, str);

return 0;
}

18.3 运行结果

参考

  1. [API Reference Document]