Huazie

一半诗意 一半烟火

总览

函数声明 函数功能
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]

总览

函数声明 函数功能
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]

总览

函数声明 函数功能
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]

总览

函数声明 函数功能
int kbhit(void); 在控制台中检测是否有按键被按下
void *kcalloc(size_t n, size_t size, gfp_t flags); 它是 Linux 内核中的一个函数,用于在内核空间分配一块连续的指定大小的内存,它与标准库函数 calloc() 的功能类似。
void keep(void *ptr); 它是 Linux 内核中的一个函数,用于防止编译器将指定的符号优化掉。
asmlinkage int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags); 它是 Linux 内核中的一个函数,用于在内核空间中创建一个新进程。
void kfree(void *ptr); 它是 Linux 内核中的一个函数,用于释放使用 kmalloc() 或者 kzalloc() 函数分配的内存空间。
int kill(pid_t pid, int sig); 向指定进程或进程组发送一个信号
int kill_proc(pid_t pid, int sig, int priv); 它是 Linux 内核中的一个函数,用于向指定进程发送信号。
void *kmalloc(size_t size, gfp_t flags); 它是 Linux 内核中的一个函数,用于在内核中分配指定大小的内存空间。
void *kmap(struct page *page); 它是 Linux 内核中的一个函数,用于将一个页映射到内核虚拟地址空间。
void *kmap_high(struct page *page); 它是 Linux 内核中的一个函数,用于将高端内存映射到内核虚拟地址空间中。
void *kmem_cache_alloc(struct kmem_cache *cachep, int flags); 它是 Linux 内核中的一个函数,用于从指定的内存缓存中分配一个对象
struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *)); 它是 Linux 内核中的一个函数,用于创建一个内存缓存区,可以用于高效地分配和释放指定大小的对象。
void kmem_cache_free(struct kmem_cache *cachep, void *objp); 它是 Linux 内核中的一个函数,用于将之前使用 kmem_cache_alloc() 函数分配的对象释放回内存缓存池,以便下次再次分配使用。
void kmem_cache_destroy(struct kmem_cache *cachep); 它是 Linux 内核中的一个函数,用于销毁之前使用 kmem_cache_create() 函数创建的内存缓存区。
void *kmem_cache_zalloc(struct kmem_cache *cache, gfp_t flags); 它是 Linux 内核中的一个函数,用于从指定内存缓存区中分配一块指定大小的内存,并将其清零。
void *kmemdup(const void *src, size_t len, gfp_t flags); 它是 Linux 内核中的一个函数,用于在内核空间中将一段指定大小的内存复制到另一段新的内存中,并返回这段新内存的指针。
void kprintf(const char *format, ...); 用于嵌入式系统中输出调试信息
void *krealloc(const void *ptr, size_t new_size, gfp_t flags); 它是 Linux 内核中的一个函数,用于动态调整已分配内存块的大小。
size_t ksize(const void *ptr); 它是 Linux 内核中的一个函数,用于获取已分配内存块的大小。
char *kstrdup(const char *s, gfp_t flags); 它是 Linux 内核中的一个函数,用于在内核空间中复制一个以 NULL 结尾的字符串,并返回这个新的字符串指针。
char *kstrndup(const char *s, size_t len, gfp_t flags); 它是 Linux 内核中的一个函数,用于在内核空间中复制一个以 NULL 结尾的字符串的一部分,并返回这个新的字符串指针。
void kstat_irqs_cpu(int cpu, int *irqs, unsigned long *stime); 它是 Linux 内核中的一个函数,用于查询指定 CPU 的中断统计信息。
struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, const char *namefmt, ...); 它是 Linux 内核中的一个函数,用于创建一个内核线程。
int kthread_stop(struct task_struct *k); 它是 Linux 内核中的一个函数,用于停止由 kthread_create() 函数创建的内核线程。
void kunmap_high(struct page *page); 它是 Linux 内核中的一个函数,用于取消一个高端内存映射。
void *kzalloc(size_t size, gfp_t flags); 它是 Linux 内核中的一个函数,用于分配指定大小的内存空间,并将其初始化为零。

1. kbhit

1.1 函数说明

函数声明 函数功能
int kbhit(void); 在控制台中检测是否有按键被按下

如果有按键被按下,该函数返回非零值,否则返回 0。

1.2 演示示例

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

int main()
{
int ch;

printf("Press any key to continue...\n");
while (!kbhit()) {
// 等待用户按键
}
ch = getch(); // 获取用户按下的键值
printf("You pressed the '%c' key\n", ch);

return 0;
}

1.3 运行结果

2. kcalloc

2.1 函数说明

函数声明 函数功能
void *kcalloc(size_t n, size_t size, gfp_t flags); 它是 Linux 内核中的一个函数,用于在内核空间分配一块连续的指定大小的内存,它与标准库函数 calloc() 的功能类似。

参数:

  • n : 要分配的元素个数
  • size : 每个元素的大小
  • flags : 用于控制内存分配行为的标志

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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

int my_init(void)
{
int *arr;

printk(KERN_INFO "Initializing module...\n");

/* allocate and initialize array */
arr = kcalloc(10, sizeof(int), GFP_KERNEL);
if (!arr) {
printk(KERN_ERR "Failed to allocate memory\n");
return -ENOMEM;
}
for (int i = 0; i < 10; i++) {
arr[i] = i + 1;
}

/* print array */
printk(KERN_INFO "Array contents:\n");
for (int i = 0; i < 10; i++) {
printk(KERN_INFO "%d ", arr[i]);
}
printk(KERN_INFO "\n");

/* free memory */
kfree(arr);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中使用 kcalloc() 函数分配了一个大小为 40 字节的整型数组,并将其初始化为 1 到 10 的连续整数。在之后,打印了数组内容并释放了内存。

注意: 在使用 kcalloc() 函数时,必须确保请求的内存大小不会超过系统可用的物理内存大小,并且可以正确地处理内存分配失败等异常情况。另外,分配的内存应在不再需要时及时释放,以免造成内存泄漏等问题。

3. keep

3.1 函数说明

函数声明 函数功能
void keep(void *ptr); 它是 Linux 内核中的一个函数,用于防止编译器将指定的符号优化掉。

参数:

  • ptr: 是指向要保留的符号的指针。

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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

static int my_symbol __attribute__((__used__));

int my_init(void)
{
printk(KERN_INFO "Initializing module...\n");

my_symbol = 123;

/* do something with my_symbol */

keep(&my_symbol); // 保留符号

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中定义了一个整型变量 my_symbol,并且对其进行了初始化。然后,在处理完该变量之后,调用 keep() 函数保留该符号,以避免编译器将其优化掉。

注意: 在使用 keep() 函数时,必须确保所保留的符号不会被优化掉,否则可能会导致程序出错或崩溃。另外,由于 keep() 函数只是防止编译器优化符号,并不会改变其可见性或访问权限,因此在使用该函数时,应该确保所保留的符号在需要的位置上是可见和可访问的。

4. kernel_thread

4.1 函数说明

函数声明 函数功能
asmlinkage int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags); 它是 Linux 内核中的一个函数,用于在内核空间中创建一个新进程。

参数:

  • fn : 指向线程处理函数的指针
  • arg : 传递给线程处理函数的参数
  • flags : 用于控制进程创建方式的标志。

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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>

static int my_thread_func(void *data)
{
printk(KERN_INFO "my_thread_func started\n");

/* do something in the thread */

printk(KERN_INFO "my_thread_func finished\n");
return 0;
}

int my_init(void)
{
printk(KERN_INFO "Initializing module...\n");

kernel_thread(my_thread_func, NULL, CLONE_KERNEL);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中使用 kernel_thread() 函数创建了一个新进程,并将其入口函数设置为 my_thread_func()。该进程可以执行任何需要在内核空间中进行的操作。在 my_exit() 函数中,什么也没做。

需要注意的是,在使用 kernel_thread() 函数时,必须确保线程处理函数能够正确地完成自己的任务并且及时退出,否则可能会导致内核崩溃或其他问题。另外,使用内核进程时需要特别小心,因为它们与内核数据结构和操作高度相关,并且可能会影响系统的稳定性和安全性。

5. kfree

5.1 函数说明

函数声明 函数功能
void kfree(void *ptr); 它是 Linux 内核中的一个函数,用于释放使用 kmalloc() 或者 kzalloc() 函数分配的内存空间。

参数:

  • ptr : 指向要释放的内存块的指针。

5.2 演示示例

参考 7.2 所示

注意: 在使用 kfree() 函数释放内存时,必须确保所释放的内存是由 kmalloc() 或者 kzalloc() 函数分配的,否则可能会导致内核崩溃或其他问题。另外,使用 kfree() 函数释放一个指针之后,应该将其设置为 NULL,以避免出现悬挂指针(dangling pointer)等问题。

6. kill

6.1 函数说明

函数声明 函数功能
int kill(pid_t pid, int sig); 向指定进程或进程组发送一个信号

参数:

  • pid: 目标进程的 ID(进程ID或进程组ID)
  • sig: 要发送的信号编号

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
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void my_handler(int signum)
{
printf("Received signal %d\n", signum);
}

int main()
{
pid_t pid;
int ret;

pid = fork(); // 创建子进程
if (pid == 0) {
// 子进程
printf("Child process started\n");
sleep(10); // 等待父进程发送信号
exit(0);
} else if (pid > 0) {
// 父进程
printf("Parent process started\n");
sleep(5); // 等待子进程创建完毕
ret = kill(pid, SIGINT); // 向子进程发送 SIGINT 信号
if (ret == -1) {
perror("kill failed");
exit(EXIT_FAILURE);
}
printf("Signal sent successfully\n");
wait(NULL); // 等待子进程结束
printf("Child process finished\n");
exit(EXIT_SUCCESS);
} else {
perror("fork failed");
exit(EXIT_FAILURE);
}

return 0;
}

上述示例程序中,我们首先创建一个子进程,并在子进程中等待 10 秒钟。然后,在父进程中发送 SIGINT 信号给子进程,并等待子进程结束。当子进程收到 SIGINT 信号时,会调用 my_handler() 函数来处理信号。

7. kill_proc

7.1 函数说明

函数声明 函数功能
int kill_proc(pid_t pid, int sig, int priv); 它是 Linux 内核中的一个函数,用于向指定进程发送信号。

参数:

  • pid : 要接收信号的进程的 PID
  • sig : 要发送的信号
  • priv : 表示是否对目标进程进行权限检查的标志。

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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>

static struct task_struct *my_thread;

static int my_thread_func(void *data)
{
printk(KERN_INFO "my_thread_func started\n");

/* send signal to current process */
kill_proc(current->pid, SIGTERM, 1);

printk(KERN_INFO "my_thread_func finished\n");
return 0;
}

int my_init(void)
{
printk(KERN_INFO "Initializing module...\n");

my_thread = kthread_create(my_thread_func, NULL, "my_thread");
if (IS_ERR(my_thread)) {
printk(KERN_ERR "kthread_create failed\n");
return -1;
}

wake_up_process(my_thread);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");

if (my_thread) {
kthread_stop(my_thread);
}
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中使用 kthread_create() 函数创建了一个名为 my_thread 的新内核线程,并将其启动。该线程的入口函数是 my_thread_func(),在这个函数中可以执行任何需要在内核空间中进行的操作。在这个函数中,使用 kill_proc() 函数向当前进程发送了 SIGTERM 信号,以请求终止当前进程。

注意: 在使用 kill_proc() 函数时,必须确保目标进程存在且具有对应的权限,否则可能会导致系统出现不可预期的行为。另外,在发送信号之前,还需要先获得目标进程的 PID,这通常可以通过 /proc 文件系统中的相关接口或者其他方式来实现。

8. kmalloc

8.1 函数说明

函数声明 函数功能
void *kmalloc(size_t size, gfp_t flags); 它是 Linux 内核中的一个函数,用于在内核中分配指定大小的内存空间。

参数:

  • size: 表示要分配的内存大小
  • flags: 表示一组标志位,用于控制内存分配方式。

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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>

int my_init(void)
{
void *ptr;

printk(KERN_INFO "Initializing module...\n");

ptr = kmalloc(1024, GFP_KERNEL);
if (!ptr) {
printk(KERN_ERR "kmalloc failed\n");
return -1;
}

/* do something with ptr */

kfree(ptr);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中我们使用 kmalloc() 函数分配了一个大小为 1024 字节的内存空间,并且对于成功与否进行了检查。然后,在处理完该内存区域之后,使用 kfree() 函数释放了所占用的内存。

注意: 在使用 kmalloc() 函数分配内存时,必须确保所分配的内存不会造成内核堆栈溢出或其他安全问题。另外,由于 kmalloc() 函数返回的内存地址可能不是连续的,因此在使用该函数分配大块内存时,需要特别注意内存对齐和分配方式等问题。

9. kmap,kmap_high

9.1 函数说明

函数声明 函数功能
void *kmap(struct page *page); 它是 Linux 内核中的一个函数,用于将一个页映射到内核虚拟地址空间。
void *kmap_high(struct page *page); 它是 Linux 内核中的一个函数,用于将高端内存映射到内核虚拟地址空间中。

参数:

  • page : 要映射的物理页面的指针。

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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/highmem.h>

int my_init(void)
{
struct page *page;
void *ptr;

printk(KERN_INFO "Initializing module...\n");

/* allocate high memory page */
page = alloc_pages(GFP_HIGHUSER, 0);
if (!page) {
printk(KERN_ERR "Failed to allocate page\n");
return -ENOMEM;
}

/* map page to kernel virtual address space */
ptr = kmap_high(page);
if (!ptr) {
printk(KERN_ERR "Failed to map page\n");
__free_pages(page, 0);
return -EFAULT;
}

/* do something with mapped memory */
*(char *)ptr = 'A';
printk(KERN_INFO "Value at mapped address: %c\n", *(char *)ptr);

/* unmap and free page */
kunmap_high(ptr);
__free_pages(page, 0);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中使用 alloc_pages() 函数分配了一块高端内存物理页面,并使用 kmap_high() 函数将其映射到内核虚拟地址空间中。在之后,对映射的内存进行操作并打印它的值。最后,使用 kunmap_high() 函数将映射解除并使用 __free_pages() 函数释放页面。

注意: 在使用 kmap_high() 函数时,必须确保请求的页面大小不会超过系统可用的物理内存大小,并且可以正确地处理异常情况。另外,使用高端内存应格外小心,因为它们与物理内存管理和 DMA 操作相关,可能会影响系统的稳定性和安全性。

10. kmem_cache_alloc

10.1 函数说明

函数声明 函数功能
void *kmem_cache_alloc(struct kmem_cache *cachep, int flags); 它是 Linux 内核中的一个函数,用于从指定的内存缓存中分配一个对象

参数:

  • cachep: 指向所需缓存区的指针
  • flags: 用于控制内存分配方式的标志。

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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>

struct my_struct {
int a;
char b;
};

static struct kmem_cache *my_cachep;

int my_init(void)
{
struct my_struct *obj;

printk(KERN_INFO "Initializing module...\n");

my_cachep = kmem_cache_create("my_cache", sizeof(struct my_struct), 0, SLAB_HWCACHE_ALIGN, NULL);
if (!my_cachep) {
printk(KERN_ERR "kmem_cache_create failed\n");
return -1;
}

obj = kmem_cache_alloc(my_cachep, GFP_KERNEL);
if (!obj) {
printk(KERN_ERR "kmem_cache_alloc failed\n");
return -1;
}

obj->a = 123;
obj->b = 'A';

printk(KERN_INFO "obj->a = %d, obj->b = '%c'\n", obj->a, obj->b);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");

kmem_cache_free(my_cachep, obj);

kmem_cache_destroy(my_cachep);
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,首先使用 kmem_cache_create() 函数创建了一个名为 my_cache 的内存缓存区,该缓存区能够容纳 struct my_struct 类型的对象。然后,在 my_init() 函数中使用 kmem_cache_alloc() 函数从缓存区中分配了一个 struct my_struct 类型的对象,并进行了初始化操作。最后,在 my_exit() 函数中使用 kmem_cache_free() 函数释放了该对象所占用的内存,然后销毁了整个缓存区。

注意: kmem_cache_alloc() 函数和其他 Linux 内核函数在用户空间下无法直接使用,通常需要编写内核模块来调用这些函数。

11. kmem_cache_create

11.1 函数说明

函数声明 函数功能
struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *)); 它是 Linux 内核中的一个函数,用于创建一个内存缓存区,可以用于高效地分配和释放指定大小的对象。

参数:

  • name : 缓存区的名称
  • size : 要分配的对象的大小
  • align : 对齐方式
  • flags : 标志位
  • ctor : 构造函数指针。

11.2 演示示例

参考 10.2 所示

注意: 在使用 kmem_cache_create() 函数时,必须确保请求的内存大小不会超过系统可用的物理内存大小,并且可以正确地处理内存分配失败等异常情况。另外,使用内存缓存区应格外小心,因为它们与内核数据结构和操作高度相关,并且可能会影响系统的稳定性和安全性。

12. kmem_cache_free

12.1 函数说明

函数声明 函数功能
void kmem_cache_free(struct kmem_cache *cachep, void *objp); 它是 Linux 内核中的一个函数,用于将之前使用 kmem_cache_alloc() 函数分配的对象释放回内存缓存池,以便下次再次分配使用。

参数:

  • cachep: 指向之前使用的缓存区的指针
  • objp: 要释放的对象的指针。

12.2 演示示例

参考 10.2 所示

注意: kmem_cache_free() 函数和其他 Linux 内核函数在用户空间下无法直接使用,通常需要编写内核模块来调用这些函数。

13. kmem_cache_destroy

13.1 函数说明

函数声明 函数功能
void kmem_cache_destroy(struct kmem_cache *cachep); 它是 Linux 内核中的一个函数,用于销毁之前使用 kmem_cache_create() 函数创建的内存缓存区。

参数:

  • cachep: 指向要销毁的缓存区的指针

13.2 演示示例

参考 10.2 所示

注意: 在销毁缓存区之前,必须确保所有从缓存区中分配的内存都已经被释放,否则可能会导致内存泄漏或其他问题。

14. kmem_cache_zalloc

14.1 函数说明

函数声明 函数功能
void *kmem_cache_zalloc(struct kmem_cache *cache, gfp_t flags); 它是 Linux 内核中的一个函数,用于从指定内存缓存区中分配一块指定大小的内存,并将其清零。

参数:

  • cache : 要分配内存的缓存区
  • flags : 用于控制内存分配行为的标志

14.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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>

static struct kmem_cache *my_cache;

struct my_struct {
int value;
};

int my_init(void)
{
struct my_struct *obj;

printk(KERN_INFO "Initializing module...\n");

/* create cache */
my_cache = kmem_cache_create("my_cache", sizeof(struct my_struct), 0, 0, NULL);
if (!my_cache) {
printk(KERN_ERR "kmem_cache_create failed\n");
return -ENOMEM;
}

/* allocate and initialize object */
obj = kmem_cache_zalloc(my_cache, GFP_KERNEL);
if (!obj) {
printk(KERN_ERR "kmem_cache_zalloc failed\n");
kmem_cache_destroy(my_cache);
return -ENOMEM;
}
obj->value = 666;

/* print object value */
printk(KERN_INFO "Object value: %d\n", obj->value);

/* free object and destroy cache */
kmem_cache_free(my_cache, obj);
kmem_cache_destroy(my_cache);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中使用 kmem_cache_create() 函数创建了一个名为 my_cache 的内存缓存区,并使用 kmem_cache_zalloc() 函数从中分配了一块大小为 sizeof(struct my_struct) 的内存。在之后,将这块内存的值设置为 666 并打印它。最后,使用 kmem_cache_free() 函数释放内存并销毁缓存区。

注意: 在使用 kmem_cache_zalloc() 函数时,必须确保请求的内存大小不会超过系统可用的物理内存大小,并且可以正确地处理内存分配失败等异常情况。另外,分配的内存应在不再需要时及时释放,以免造成内存泄漏等问题。

15. kmemdup

15.1 函数说明

函数声明 函数功能
void *kmemdup(const void *src, size_t len, gfp_t flags); 它是 Linux 内核中的一个函数,用于在内核空间中将一段指定大小的内存复制到另一段新的内存中,并返回这段新内存的指针。

参数:

  • src : 要复制的源内存地址
  • len : 要复制的内存字节数
  • flags : 用于控制内存分配行为的标志

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
27
28
29
30
31
32
33
34
35
36
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>

int my_init(void)
{
char *src = "Hello, world!";
char *dst;

printk(KERN_INFO "Initializing module...\n");

/* allocate and copy memory */
dst = kmemdup(src, strlen(src) + 1, GFP_KERNEL);
if (!dst) {
printk(KERN_ERR "kmemdup failed\n");
return -ENOMEM;
}

/* print copied memory */
printk(KERN_INFO "Copied string: %s\n", dst);

/* free memory */
kfree(dst);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中使用 kmemdup() 函数将一段字符串 Hello, world! 复制到另一段新的内存中。在之后,打印了这段复制的内存,并释放了它。

注意: 在使用 kmemdup() 函数时,必须确保请求的内存大小不会超过系统可用的物理内存大小,并且可以正确地处理内存分配失败等异常情况。另外,分配的内存应在不再需要时及时释放,以免造成内存泄漏等问题。

16. kprintf

16.1 函数说明

函数声明 函数功能
void kprintf(const char *format, ...); 用于嵌入式系统中输出调试信息

注意: 该函数原型和使用方法与标准库中的 printf() 函数类似。不同的是,kprintf() 函数通常需要根据具体的嵌入式系统进行修改,以适应不同的输出方式。

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <stdio.h>
#include <stdarg.h>
#include <fcntl.h>
#include <termios.h>

int uart_fd = -1; // 串口文件描述符

void kprintf(const char *format, ...)
{
va_list ap;
char buf[256];

va_start(ap, format);
vsnprintf(buf, sizeof(buf), format, ap);
va_end(ap);

if (uart_fd != -1) {
write(uart_fd, buf, strlen(buf));
}
}

int init_uart(const char *devname)
{
struct termios tio;

uart_fd = open(devname, O_RDWR | O_NOCTTY);
if (uart_fd == -1) {
perror("open failed");
return -1;
}

memset(&tio, 0, sizeof(tio));
cfmakeraw(&tio);
cfsetspeed(&tio, B115200);
tcsetattr(uart_fd, TCSANOW, &tio);

return 0;
}

int main()
{
int ret;

ret = init_uart("/dev/ttyS0"); // 打开 ttyS0 串口
if (ret == -1) {
exit(EXIT_FAILURE);
}

kprintf("Hello, world!\n"); // 输出调试信息

close(uart_fd); // 关闭串口文件描述符
return 0;
}

上述示例程序中,首先通过 init_uart() 函数打开了 ttyS0 串口,并将其设置为 RAW 模式和波特率 115200。然后,在 main() 函数中调用了 kprintf() 函数来输出一条调试信息。该函数会将调试信息写入 ttyS0 串口中,并发送到外部设备(如 PC)上。

注意:kprintf() 函数通常需要进行一定的修改以适应具体的嵌入式系统和调试工具,上面仅提供一个简单的示例,不能直接在所有系统中使用。

17. krealloc

17.1 函数说明

函数声明 函数功能
void *krealloc(const void *ptr, size_t new_size, gfp_t flags); 它是 Linux 内核中的一个函数,用于动态调整已分配内存块的大小。

参数:

  • ptr : 指向原内存块的指针
  • new_size : 新的内存块大小
  • flags : 用于控制内存分配行为的标志。

17.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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>

int my_init(void)
{
char *buf = kmalloc(16, GFP_KERNEL);

printk(KERN_INFO "Initializing module...\n");

if (!buf) {
printk(KERN_ERR "kmalloc failed\n");
return -ENOMEM;
}

/* resize buffer */
buf = krealloc(buf, 32, GFP_KERNEL);
if (!buf) {
printk(KERN_ERR "krealloc failed\n");
kfree(buf);
return -ENOMEM;
}

/* print buffer size */
printk(KERN_INFO "Buffer size: %lu\n", ksize(buf));

/* free buffer */
kfree(buf);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中使用 kmalloc() 函数分配了一块大小为 16 字节的内存,并使用 krealloc() 函数将其调整为大小为 32 字节。在之后,打印了这块内存的大小并释放了它。

注意: 在使用 krealloc() 函数时,必须确保操作合法且不会导致内存泄漏或其他问题。另外,应当避免过度依赖动态内存分配而导致系统性能下降或出现其他问题。

18. ksize

18.1 函数说明

函数声明 函数功能
size_t ksize(const void *ptr); 它是 Linux 内核中的一个函数,用于获取已分配内存块的大小。

参数:

  • ptr : 指向已分配内存块的指针。

18.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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>

int my_init(void)
{
char *buf = kmalloc(16, GFP_KERNEL);

printk(KERN_INFO "Initializing module...\n");

if (!buf) {
printk(KERN_ERR "kmalloc failed\n");
return -ENOMEM;
}

/* print buffer size */
printk(KERN_INFO "Buffer size: %lu\n", ksize(buf));

/* free buffer */
kfree(buf);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中使用 kmalloc() 函数分配了一块大小为 16 字节的内存,并使用 ksize() 函数获取了它的大小。在之后,打印了这块内存的大小并释放了它。

19. kstrdup

19.1 函数说明

函数声明 函数功能
char *kstrdup(const char *s, gfp_t flags); 它是 Linux 内核中的一个函数,用于在内核空间中复制一个以 NULL 结尾的字符串,并返回这个新的字符串指针。

参数:

  • s : 要复制的源字符串
  • flags : 用于控制内存分配行为的标志

19.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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>

int my_init(void)
{
const char *src = "Hello, world!";
char *dst;

printk(KERN_INFO "Initializing module...\n");

/* duplicate string */
dst = kstrdup(src, GFP_KERNEL);
if (!dst) {
printk(KERN_ERR "kstrdup failed\n");
return -ENOMEM;
}

/* print duplicated string */
printk(KERN_INFO "Duplicated string: %s\n", dst);

/* free memory */
kfree(dst);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中使用 kstrdup() 函数将一个字符串 Hello, world! 复制到另一段新的内存中。在之后,打印了这段复制的字符串,并释放了它。

注意: 在使用 kstrdup() 函数时,必须确保源字符串以 NULL 结尾,并且请求的内存大小不会超过系统可用的物理内存大小,并且可以正确地处理内存分配失败等异常情况。另外,分配的内存应在不再需要时及时释放,以免造成内存泄漏等问题。

20. kstrndup

20.1 函数说明

函数声明 函数功能
char *kstrndup(const char *s, size_t len, gfp_t flags); 它是 Linux 内核中的一个函数,用于在内核空间中复制一个以 NULL 结尾的字符串的一部分,并返回这个新的字符串指针。

参数:

  • s : 要复制的源字符串
  • len : 要复制的字符串长度
  • flags : 用于控制内存分配行为的标志

20.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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>

int my_init(void)
{
const char *src = "Hello, world!";
char *dst;

printk(KERN_INFO "Initializing module...\n");

/* duplicate string */
dst = kstrndup(src, 5, GFP_KERNEL);
if (!dst) {
printk(KERN_ERR "kstrndup failed\n");
return -ENOMEM;
}

/* print duplicated string */
printk(KERN_INFO "Duplicated string: %s\n", dst);

/* free memory */
kfree(dst);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中使用 kstrndup() 函数将一个字符串 Hello, world!前五个字符 复制到另一段新的内存中。在之后,打印了这段复制的字符串,并释放了它。

注意: 在使用 kstrndup() 函数时,必须确保源字符串以 NULL 结尾,并且请求的内存大小不会超过系统可用的物理内存大小,并且可以正确地处理内存分配失败等异常情况。另外,分配的内存应在不再需要时及时释放,以免造成内存泄漏等问题。

21. kstat_irqs_cpu

21.1 函数说明

函数声明 函数功能
void kstat_irqs_cpu(int cpu, int *irqs, unsigned long *stime); 它是 Linux 内核中的一个函数,用于查询指定 CPU 的中断统计信息。

参数:

cpu : 要查询的 CPU 编号
irqs : 用于保存中断计数值的数组
stime : 用于保存中断处理时间戳的变量

21.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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

int my_init(void)
{
int irqs[NR_IRQS];
unsigned long stime;

printk(KERN_INFO "Initializing module...\n");

/* get IRQ statistics for CPU 0 */
kstat_irqs_cpu(0, irqs, &stime);

/* print IRQ statistics */
printk(KERN_INFO "IRQ statistics for CPU 0:\n");
for (int i = 0; i < NR_IRQS; i++) {
if (irqs[i] > 0) {
printk(KERN_INFO "IRQ %d: count=%d\n", i, irqs[i]);
}
}

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中使用 kstat_irqs_cpu() 函数查询了 CPU 0 的中断统计信息,并将结果打印到内核日志中。

注意: 在使用 kstat_irqs_cpu() 函数时,必须确保传递给该函数的参数是正确的,并且具有足够的权限来访问相关的数据结构。另外,中断统计信息可能会随着时间的推移而发生变化,因此需要在使用之前及时更新数据。

22. kthread_create

22.1 函数说明

函数声明 函数功能
struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, const char *namefmt, ...); 它是 Linux 内核中的一个函数,用于创建一个内核线程。

参数:

threadfn : 指向线程处理函数的指针
data : 传递给线程处理函数的参数
namefmt : 用于命名线程的格式化字符串(类似于 printf() 函数的格式化字符串)。

22.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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kthread.h>

static struct task_struct *my_thread;

static int my_thread_func(void *data)
{
printk(KERN_INFO "my_thread_func started\n");

while (!kthread_should_stop()) {
/* do something */
}

printk(KERN_INFO "my_thread_func finished\n");
return 0;
}

int my_init(void)
{
printk(KERN_INFO "Initializing module...\n");

my_thread = kthread_create(my_thread_func, NULL, "my_thread");
if (IS_ERR(my_thread)) {
printk(KERN_ERR "kthread_create failed\n");
return -1;
}

wake_up_process(my_thread);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");

if (my_thread) {
kthread_stop(my_thread);
}
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中使用 kthread_create() 函数创建了一个名为 my_thread 的新内核线程,并将其启动。该线程的入口函数是 my_thread_func(),在这个函数中可以执行任何需要在内核空间中进行的操作。在 my_exit() 函数中,使用 kthread_stop() 函数停止该线程的运行。

注意: 在使用 kthread_create() 函数时,必须确保线程处理函数能够正确地完成自己的任务并且及时退出,否则可能会导致内核崩溃或其他问题。另外,使用内核线程时需要特别小心,因为它们与内核数据结构和操作高度相关,并且可能会影响系统的稳定性和安全性。

23. kthread_stop

23.1 函数说明

函数声明 函数功能
int kthread_stop(struct task_struct *k); 它是 Linux 内核中的一个函数,用于停止由 kthread_create() 函数创建的内核线程。

参数:

k : 指向要停止的内核线程的指针。

23.2 演示示例

参考 22.2 中所示

注意: 在使用 kthread_stop() 函数时,必须确保线程处理函数能够正确地响应该函数并及时退出,并且不会造成内核资源泄漏等问题,否则可能会导致内核崩溃或其他问题。另外,在使用 kthread_stop() 函数之前,需要先调用 kthread_should_stop() 函数来检查线程是否应该停止。

24. kunmap_high

24.1 函数说明

函数声明 函数功能
void kunmap_high(struct page *page); 它是 Linux 内核中的一个函数,用于取消一个高端内存映射。

参数:

  • page : 要取消映射的高端内存页

24.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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/highmem.h>

int my_init(void)
{
struct page *page;
char *kaddr;

printk(KERN_INFO "Initializing module...\n");

/* allocate high memory */
page = alloc_highmem_pages(1);
if (!page) {
printk(KERN_ERR "alloc_highmem_pages failed\n");
return -ENOMEM;
}

/* map high memory to kernel address space */
kaddr = kmap(page);
if (!kaddr) {
printk(KERN_ERR "kmap failed\n");
free_highmem_page(page);
return -ENOMEM;
}

/* print mapped address */
printk(KERN_INFO "Mapped address: %pK\n", kaddr);

/* unmap high memory */
kunmap_high(page);

/* free high memory */
free_highmem_page(page);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中使用 alloc_highmem_pages() 函数分配了一块高端内存页,并使用 kmap() 函数将其映射到内核虚拟地址空间。在之后,打印了这个映射的内核地址,并使用 kunmap_high() 函数取消了这个映射。最后,释放了这块高端内存页。

注意: 在使用高端内存时,必须确保操作合法且不会导致内存泄漏或其他问题。另外,在使用 kmap()kunmap_high() 函数时,必须确保映射和取消映射是成对出现的,否则可能会导致内存泄漏或其他问题。

25. kzalloc

25.1 函数说明

函数声明 函数功能
void *kzalloc(size_t size, gfp_t flags); 它是 Linux 内核中的一个函数,用于分配指定大小的内存空间,并将其初始化为零。

参数:

  • size: 表示要分配的内存大小
  • flags: 表示一组标志位,用于控制内存分配方式。

25.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
c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>

int my_init(void)
{
void *ptr;

printk(KERN_INFO "Initializing module...\n");

ptr = kzalloc(1024, GFP_KERNEL);
if (!ptr) {
printk(KERN_ERR "kzalloc failed\n");
return -1;
}

/* do something with ptr */

kfree(ptr);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中使用 kzalloc() 函数分配了一个大小为 1024 字节的内存空间,并将其初始化为零。然后,在处理完该内存区域之后,使用 kfree() 函数释放了所占用的内存。

注意: 虽然可以使用 kmalloc() 函数分配内存然后手动初始化为零,但是使用 kzalloc() 函数可以更加高效和简单地完成这个操作。另外,由于 kzalloc() 函数返回的内存地址是经过清零的,因此在使用该函数分配内存时,无需显式调用 memset() 等函数进行初始化操作。

参考

  1. [API Reference Document]
  2. 《Linux内核API完全参考手册》

总览

函数声明 函数功能
double j0 (double x); 计算 x 的 第一类 0 阶贝塞尔函数(double)
float j0f (float x); 计算 x 的 第一类 0 阶贝塞尔函数(float)【笔者本地windows环境,无此函数】
double j1 (double x); 计算 x 的 第一类 1 阶贝塞尔函数(double)
float j1f (float x); 计算 x 的 第一类 1 阶贝塞尔函数(float)【笔者本地windows环境,无此函数】
double jn (int n, double x); 计算 x第一类 n 阶贝塞尔函数(double)
float jnf (int n, float x); 计算 x第一类 n 阶贝塞尔函数(float)【笔者本地windows环境,无此函数】
double jrand48(); 生成伪随机数序列
int join(pthread_t thread, void **retval); 等待线程退出并回收资源
typedef _JBTYPE jmp_buf[_JBLEN]; 它是一个数组类型,保存跳转目标地址的缓冲区。通常与 setjmp 和 longjmp 函数一起使用,用于实现非局部跳转
u32 jhash(const void *key, u32 length, u32 initval); 它是 Linux 内核头文件 linux/jhash.h 中的一个函数,用于实现一种高效的哈希算法。
unsigned long volatile jiffies; 它是 Linux 内核中的一个全局变量,表示内核启动后经过的节拍数。其中 volatile 关键字用于告知编译器在访问这个变量时不要使用缓存,以确保能够正确读取最新值。
u64 jiffies_64; 它是 Linux 内核中的一个全局变量,类似于 jiffies,但是支持更大的取值范围。其中 u6464 位无符号整型。
clock_t jiffies_delta_to_clock_t(unsigned long delta); 它是 Linux 内核头文件 linux/jiffies.h 中的一个函数,用于计算两个 jiffies 值之间的时间差,并将结果转换为 clock_t 类型的值。
unsigned long jiffies_delta_to_msecs(unsigned long delta); 它是 Linux 内核头文件 linux/jiffies.h 中的一个函数,用于计算两个 jiffies 值之间的时间差,并将结果转换为毫秒数
clock_t jiffies_to_clock_t(unsigned long jiffies); 它是 Linux 内核头文件 linux/jiffies.h 中的一个函数,用于将 jiffies 值(内核节拍数)转换为 clock_t 类型的值。
unsigned long jiffies_to_msecs(const unsigned long j); 它是 Linux 内核头文件 linux/jiffies.h 中的一个函数,用于将 jiffies 值(内核节拍数)转换为毫秒数。
clock_t jiffies64_to_clock_t(u64 jiffies); 它是 Linux 内核头文件 linux/jiffies.h 中的一个函数,用于将 64 位 jiffies 值(内核节拍数)转换为 clock_t 类型的值。
u64 jiffies64_to_msecs(const u64 jiffies); 它是 Linux 内核头文件 linux/jiffies.h 中的一个函数,用于将 64 位 jiffies 值(内核节拍数)转换为毫秒数。
void jiffies_update_wallclock(void); 它是 Linux 内核头文件 linux/time.h 中的一个函数,用于更新系统时钟的时间戳。

1. j0,j0f

1.1 函数说明

函数声明 函数功能
double j0 (double x); 计算 x 的 第一类 0 阶贝塞尔函数(double)
float j0f (float x); 计算 x 的 第一类 0 阶贝塞尔函数(float)【笔者本地windows环境,无此函数】

注意: 如果操作成功,则返回 x第一类 0 阶贝塞尔函数;如果 xNaN 值,则返回 NaN 值;如果 x 太大或发生溢出范围错误,则返回 0 并将 errno 设置为 ERANGE

1.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <math.h>
int main()
{
double x = 10.0, result;
result = j0(x);

printf("%lf 的 第一类 0 阶贝塞尔函数 : %lf", x, result);

return 0;
}

1.3 运行结果

2. j1,j1f

2.1 函数说明

函数声明 函数功能
double j1 (double x); 计算 x 的 第一类 1 阶贝塞尔函数(double)
float j1f (float x); 计算 x 的 第一类 1 阶贝塞尔函数(float)【笔者本地windows环境,无此函数】

注意: 如果操作成功,则返回 x第一类 1 阶贝塞尔函数;如果 xNaN 值,则返回 NaN 值;如果 x 太大或发生溢出范围错误,则返回 0 并将 errno 设置为 ERANGE

2.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <math.h>
int main()
{
double x = 10.0, result;
result = j1(x);

printf("%lf 的 第一类 1 阶贝塞尔函数 : %lf", x, result);

return 0;
}

2.3 运行结果

3. jn,jnf

3.1 函数说明

函数声明 函数功能
double jn (int n, double x); 计算 x第一类 n 阶贝塞尔函数(double)
float jnf (int n, float x); 计算 x第一类 n 阶贝塞尔函数(float)【笔者本地windows环境,无此函数】

注意: 如果操作成功,则返回 x第一类 n 阶贝塞尔函数;如果 xNaN 值,则返回 NaN 值;如果 x 太大或发生溢出范围错误,则返回 0 并将 errno 设置为 ERANGE

3.2 演示示例

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

void jnPrint(int n, double x);

int main()
{
double x = 10.0;
jnPrint(2, x);
jnPrint(3, x);
jnPrint(4, x);
return 0;
}

void jnPrint(int n, double x)
{
double result = jn(n, x);
printf("%lf 的 第一类 %d 阶贝塞尔函数 : %lf\n", x, n, result);
}

3.3 运行结果

4. jrand48

4.1 函数说明

函数声明 函数功能
double jrand48(); 生成伪随机数序列

jrand48 函数是一个生成伪随机数序列的函数,并且它是可重入的,即可以在多个线程中同时调用而不会出现冲突。

4.2 演示示例

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

int main() {
// 初始化种子
srand48(time(NULL));

// 生成10个随机数
for (int i = 0; i < 5; ++i) {
double r = jrand48();
printf("%f\n", r);
}

return 0;
}

上述程序首先通过 srand48 函数初始化随机数生成器的种子,这里使用了当前系统时间作为种子。然后循环调用 jrand48 函数 5 次,每次输出一个伪随机数。注意,由于 jrand48 函数返回的是一个双精度浮点数(范围在 [0, 1) 内),因此输出时需要使用 %f 格式化符号。

5. join

5.1 函数说明

函数声明 函数功能
int join(pthread_t thread, void **retval); 等待线程退出并回收资源

C 语言中,join 函数不是标准库函数,也不是 POSIX 标准的函数。然而,一些操作系统(如 UNIX/Linux)提供了 join 函数用于等待线程退出并回收资源。在 POSIX 线程中,相应的函数是 pthread_join

5.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 <pthread.h>

void *thread_func(void *arg) {
printf("Thread is running...\n");
pthread_exit(NULL);
}

int main() {
pthread_t thread;
if (pthread_create(&thread, NULL, thread_func, NULL)) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
printf("Main thread is waiting for the child thread to exit...\n");
join(thread, NULL);
printf("Child thread has exited.\n");
return EXIT_SUCCESS;
}

上述程序创建了一个新线程,并且主线程等待新线程退出后才继续执行。在新线程中,打印一条消息并调用 pthread_exit 函数退出线程。在主线程中,调用 join 函数等待新线程退出,并通过 NULL 参数指示不需要返回值。最终输出一条消息表示新线程已经退出。

6. jmp_buf

6.1 类型说明

类型定义 描述
typedef _JBTYPE jmp_buf[_JBLEN]; 它是一个数组类型,保存跳转目标地址的缓冲区。通常与 setjmp 和 longjmp 函数一起使用,用于实现非局部跳转

6.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 <setjmp.h>

jmp_buf env;

void func() {
printf("Entering func...\n");
longjmp(env, 1);
printf("This line will not be executed.\n");
}

int main() {
int ret = setjmp(env);
if (ret == 0) {
printf("Jumping to func...\n");
func();
} else {
printf("Returning from longjmp with value %d\n", ret);
}
return EXIT_SUCCESS;
}

上述程序定义了一个名为 envjmp_buf 类型变量,用于保存当前执行状态。在主函数中,通过调用 setjmp 函数将当前状态保存到
env 中,并返回 0。然后调用 func 函数,该函数打印一条消息并调用 longjmp 函数恢复之前保存的状态,这里传入参数值为 1。由于 longjmp 函数会导致程序跳转到 setjmp 函数继续执行,因此后面的 printf 语句会输出 "Returning from longjmp with value 1"

需要注意的是,在使用 jmp_bufsetjmplongjmp 函数时需要遵循特定的使用规范,否则可能会导致未定义行为或错误。

6.3 运行结果

7. jhash

7.1 函数说明

函数声明 函数功能
u32 jhash(const void *key, u32 length, u32 initval); 它是 Linux 内核头文件 linux/jhash.h 中的一个函数,用于实现一种高效的哈希算法。

参数:

  • key : 要进行哈希的数据
  • length : 数据的长度(以字节为单位)
  • initval : 哈希值的初始值。

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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/jhash.h>

int my_init(void)
{
char data[] = "Hello, world!";
u32 hash;

printk(KERN_INFO "Initializing module...\n");

/* calculate hash value */
hash = jhash(data, strlen(data), 0);
printk(KERN_INFO "Hash value: %u\n", hash);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中定义了一个字符串 data,并使用 jhash() 函数计算出其哈希值,并打印出来。

注意: 虽然 jhash() 函数可以用于快速查找和管理数据结构等,但在使用时必须充分理解其作用原理和使用方法,避免因为错误使用导致哈希冲突或其他问题。同时,应当根据具体情况选择合适的哈希算法,并考虑其效率和安全性等方面的因素。

8. jiffies,jiffies_64

8.1 变量说明

变量声明 变量描述
unsigned long volatile jiffies; 它是 Linux 内核中的一个全局变量,表示内核启动后经过的节拍数。其中 volatile 关键字用于告知编译器在访问这个变量时不要使用缓存,以确保能够正确读取最新值。
u64 jiffies_64; 它是 Linux 内核中的一个全局变量,类似于 jiffies,但是支持更大的取值范围。其中 u6464 位无符号整型。

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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/jiffies.h>

int my_init(void)
{
u64 start = jiffies_64;
u64 end;

printk(KERN_INFO "Initializing module...\n");

/* do some work */
mdelay(1000);

/* calculate elapsed time in jiffies_64 */
end = jiffies_64 - start;
printk(KERN_INFO "Elapsed time: %llu jiffies_64\n", end);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中获取了当前的 jiffies_64 值,并使用 mdelay() 函数让程序阻塞一段时间。在之后,通过计算当前的 jiffies_64 值与之前的 jiffies_64 值之差,计算出经过的时间并打印出来。

注意: jiffiesjiffies_64 值每隔一段时间就会发生一次溢出,在处理 jiffiesjiffies_64 值时必须注意这个问题,避免计算结果错误。另外,jiffiesjiffies_64 变量只能在内核空间中使用,不能在用户空间中使用。

9. jiffies_delta_to_clock_t

9.1 函数说明

函数声明 函数功能
clock_t jiffies_delta_to_clock_t(unsigned long delta); 它是 Linux 内核头文件 linux/jiffies.h 中的一个函数,用于计算两个 jiffies 值之间的时间差,并将结果转换为 clock_t 类型的值。

参数:

  • delta : 要计算的 jiffies 时间差值。

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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/jiffies.h>

int my_init(void)
{
unsigned long start = jiffies;
unsigned long end;
clock_t ticks;

printk(KERN_INFO "Initializing module...\n");

/* do some work */
mdelay(1000);

/* calculate elapsed time in jiffies and convert to ticks */
end = jiffies;
ticks = jiffies_delta_to_clock_t(end - start);
printk(KERN_INFO "Elapsed time: %ld ticks\n", (long)ticks);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中获取了当前的 jiffies 值,并使用 mdelay() 函数让程序阻塞一段时间。在之后,通过计算当前的 jiffies 值与之前的 jiffies 值之差,并调用 jiffies_delta_to_clock_t() 函数,计算出经过的时间并打印出来。

注意: 在使用 jiffies_delta_to_clock_t() 函数时,返回值类型是 clock_t,不同于 jiffies_delta_to_msecs() 函数的返回值类型是 unsigned long。另外,clock_t 的定义可能因系统而异,应当根据具体情况进行处理。

10. jiffies_delta_to_msecs

10.1 函数说明

函数声明 函数功能
unsigned long jiffies_delta_to_msecs(unsigned long delta); 它是 Linux 内核头文件 linux/jiffies.h 中的一个函数,用于计算两个 jiffies 值之间的时间差,并将结果转换为毫秒数

参数:

  • delta : 要计算的 jiffies 时间差值。

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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/jiffies.h>

int my_init(void)
{
unsigned long start = jiffies;
unsigned long end;
unsigned long msecs;

printk(KERN_INFO "Initializing module...\n");

/* do some work */
mdelay(1000);

/* calculate elapsed time in jiffies and convert to milliseconds */
end = jiffies;
msecs = jiffies_delta_to_msecs(end - start);
printk(KERN_INFO "Elapsed time: %lu ms\n", msecs);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中获取了当前的 jiffies 值,并使用 mdelay() 函数让程序阻塞一段时间。在之后,通过计算当前的 jiffies 值与之前的 jiffies 值之差,并调用 jiffies_delta_to_msecs() 函数,计算出经过的时间并打印出来。

注意: 在使用 jiffies_delta_to_msecs() 函数时,返回值类型是 unsigned long,不同于 jiffies_delta_to_clock_t() 函数的返回值类型是 clock_t。另外,由于 jiffies 的精度限制,计算结果可能存在一定的误差。

11. jiffies_to_clock_t

11.1 函数说明

函数声明 函数功能
clock_t jiffies_to_clock_t(unsigned long jiffies); 它是 Linux 内核头文件 linux/jiffies.h 中的一个函数,用于将 jiffies 值(内核节拍数)转换为 clock_t 类型的值。

参数:

  • jiffies: 要转换的 jiffies 值,它是 Linux 内核中的一个全局变量,表示内核启动后经过的节拍数。

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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/jiffies.h>

int my_init(void)
{
unsigned long j = jiffies;
clock_t ticks;

printk(KERN_INFO "Initializing module...\n");

/* do some work */
mdelay(1000);

/* calculate elapsed time in ticks */
ticks = jiffies_to_clock_t(jiffies - j);
printk(KERN_INFO "Elapsed time: %ld ticks\n", (long)ticks);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中获取了当前的 jiffies 值,并使用 mdelay() 函数让程序阻塞一段时间。在之后,通过计算当前的 jiffies 值与之前的 jiffies 值之差并调用 jiffies_to_clock_t() 函数,计算出经过的时间,并打印出来。

注意: 在使用 jiffies_to_clock_t() 函数时,返回值类型是 clock_t,不同于 jiffies_to_msecs() 函数的返回值类型是 unsigned long。另外,clock_t 的定义可能因系统而异,应当根据具体情况进行处理。

12. jiffies_to_msecs

12.1 函数说明

函数声明 函数功能
unsigned long jiffies_to_msecs(const unsigned long j); 它是 Linux 内核头文件 linux/jiffies.h 中的一个函数,用于将 jiffies 值(内核节拍数)转换为毫秒数。

参数:

  • j: 要转换的 jiffies 值,它是 Linux 内核中的一个全局变量,表示内核启动后经过的节拍数。

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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/jiffies.h>

int my_init(void)
{
unsigned long j = jiffies;
unsigned long ms;

printk(KERN_INFO "Initializing module...\n");

/* do some work */
mdelay(1000);

/* calculate elapsed time in milliseconds */
ms = jiffies_to_msecs(jiffies - j);
printk(KERN_INFO "Elapsed time: %lu ms\n", ms);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中获取了当前的 jiffies 值,并使用 mdelay() 函数让程序阻塞一段时间。在之后,通过计算当前的 jiffies 值与之前的 jiffies 值之差并调用 jiffies_to_msecs() 函数,计算出经过的时间,并打印出来。

注意: 在使用 jiffies_to_msecs() 函数时,必须十分小心地处理 jiffies 值的溢出等问题,以免计算结果错误。另外,jiffies_to_msecs() 函数只能用于内核空间中,不能在用户空间中使用。

13. jiffies64_to_clock_t

13.1 函数说明

函数声明 函数功能
clock_t jiffies64_to_clock_t(u64 jiffies); 它是 Linux 内核头文件 linux/jiffies.h 中的一个函数,用于将 64 位 jiffies 值(内核节拍数)转换为 clock_t 类型的值。

参数:

  • jiffies : 要转换的 64jiffies 值。

13.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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/jiffies.h>

int my_init(void)
{
u64 start = jiffies_64;
u64 end;
clock_t ticks;

printk(KERN_INFO "Initializing module...\n");

/* do some work */
mdelay(1000);

/* calculate elapsed time in jiffies_64 and convert to ticks */
end = jiffies_64;
ticks = jiffies64_to_clock_t(end - start);
printk(KERN_INFO "Elapsed time: %ld ticks\n", (long)ticks);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中获取了当前的 64jiffies_64 值,并使用 mdelay() 函数让程序阻塞一段时间。在之后,通过计算当前的 64jiffies_64 值与之前的 jiffies_64 值之差,并调用 jiffies64_to_clock_t() 函数,计算出经过的时间并打印出来。

14. jiffies64_to_msecs

14.1 函数说明

函数声明 函数功能
u64 jiffies64_to_msecs(const u64 jiffies); 它是 Linux 内核头文件 linux/jiffies.h 中的一个函数,用于将 64 位 jiffies 值(内核节拍数)转换为毫秒数。

参数:

  • jiffies : 要转换的 64jiffies 值。

14.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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/jiffies.h>

int my_init(void)
{
u64 start = jiffies_64;
u64 end;
u64 msecs;

printk(KERN_INFO "Initializing module...\n");

/* do some work */
mdelay(1000);

/* calculate elapsed time in jiffies_64 and convert to milliseconds */
end = jiffies_64;
msecs = jiffies64_to_msecs(end - start);
printk(KERN_INFO "Elapsed time: %llu ms\n", msecs);

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中获取了当前的 64jiffies_64 值,并使用 mdelay() 函数让程序阻塞一段时间。在之后,通过计算当前的 64jiffies_64 值与之前的 jiffies_64 值之差,并调用 jiffies64_to_msecs() 函数,计算出经过的时间并打印出来。

15. jiffies_update_wallclock

15.1 函数说明

函数声明 函数功能
void jiffies_update_wallclock(void); 它是 Linux 内核头文件 linux/time.h 中的一个函数,用于更新系统时钟的时间戳。

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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/jiffies.h>

int my_init(void)
{
printk(KERN_INFO "Initializing module...\n");

/* do some work */
mdelay(1000);

/* update wall clock */
jiffies_update_wallclock();

return 0;
}

void my_exit(void)
{
printk(KERN_INFO "Exiting module...\n");
}

MODULE_LICENSE("GPL");
module_init(my_init);
module_exit(my_exit);

上述示例程序中,在 my_init() 函数中使用 mdelay() 函数让程序阻塞一段时间,在之后调用 jiffies_update_wallclock() 函数更新系统时钟的时间戳。

注意: jiffies_update_wallclock() 函数只能在内核空间中使用,不能在用户空间中使用。另外,如果系统使用了 NTP 等网络时间同步服务,可能无法通过 jiffies_update_wallclock() 函数来准确更新系统时钟。

参考

  1. [MATH-标准C库]
  2. 《Linux内核API完全参考手册》

总览

函数声明 函数功能
unsigned imagesize(int left, int top, int right, int bottom); 获取保存位图像所需的字节数
void initgraph( int *graphdriver, int *graphmode, char *pathtodriver ); 初始化图形系统
int inport(int protid); 从硬件端口中输入
void insline(void); 在文本窗口中插入一个空行
int installuserdriver(char *name, int (*detect)(void)); 安装设备驱动程序到BGI设备驱动程序表中
int installuserfont( char *name ); 安装未嵌入BGI系统的字体文件(CHR)
int int86(int intr_num, union REGS *inregs, union REGS *outregs); 通用8086软中断接口
int int86x(int intr_num, union REGS *insegs, union REGS *outregs, struct SREGS *segregs); 通用8086软中断接口
int intdos(union REGS *inregs, union REGS *outregs); 通用DOS接口
int intdosx(union REGS *inregs, union REGS *outregs, struct SREGS *segregs); 通用DOS中断接口
void intr(int intr_num, struct REGPACK *preg); 改变软中断接口
int ioctl(int fd, int cmd, ...) ; 控制 I/O 设备
int isatty(int handle); 检查设备类型
int ilogb (double x); 获取 x 的对数的积分部分(double)
int ilogbf (float x); 获取 x 的对数的积分部分(float)
int ilogbl (long double x); 获取 x 的对数的积分部分(long double)
int isalnum(int c); 检查字符 c 是否为字母或数字
int isalpha(int c); 检查字符 c 是否为(大写或小写)字母
int isdigit(int c); 检查字符 c 是否为数字(0 - 9)
int isinf(double x); 检查浮点数 x 是否为无穷大(正无穷或负无穷)
int isnan(double x); 检查浮点数 x 是否为非数字(NaN)
int isspace(int c); 检查字符 c 是否为空白字符,如空格(’ ‘)、制表符(’\t’)、换行符(’\n’)等。
char * itoa(int value, char *string, int radix); 把一整数转换为字符串

1. imagesize

1.1 函数说明

函数声明 函数功能
unsigned imagesize(int left, int top, int right, int bottom); 获取保存位图像所需的字节数

参数:

  • left :矩形区域左边界的 x 坐标(水平方向起始位置)。
  • top :矩形区域上边界的 y 坐标(垂直方向起始位置)。
  • right :矩形区域右边界的 x 坐标(水平方向结束位置,需满足 right >= left)。
  • bottom :矩形区域下边界的 y 坐标(垂直方向结束位置,需满足 bottom >= top)。

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>

#define ARROW_SIZE 10

void draw_arrow(int x, int y);

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

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

errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}

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);
}

上述代码利用图形库实现了一个在屏幕上移动箭头图形的动画效果。

1.3 运行结果

2. initgraph

2.1 函数说明

函数声明 函数功能
void initgraph( int *graphdriver, int *graphmode, char *pathtodriver); 初始化图形系统

参数:

  • int *graphdriver : 一个指向整数的指针。用于指定要使用的图形驱动程序。图形驱动程序是一组软件代码,负责与特定的图形硬件进行通信。常见的图形驱动程序有 DETECT(自动检测系统中可用的图形硬件并选择合适的驱动程序)、EGAVGA_driver(用于 EGA 或 VGA 显示器)等。
  • int *graphmode :一个指向整数的指针。用于指定要使用的图形模式。不同的图形驱动程序支持多种图形模式,每种模式具有不同的分辨率、颜色深度等特性。如果 graphdriver 参数设置为 DETECT,则可以将 graphmode 指针指向的变量初始化为 0,让系统自动选择合适的图形模式。
  • char *pathtodriver :一个指向字符的指针。用于指定图形驱动程序文件的路径。图形驱动程序通常以 .BGI 为扩展名的文件形式存在。如果该参数设置为 ""(空字符串),则系统会在当前目录下查找图形驱动程序文件。

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
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
int gdriver = DETECT, gmode, errorcode;
// 初始化图形系统
initgraph(&gdriver, &gmode, "");

errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}

line(0, 0, getmaxx(), getmaxy());

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

2.3 运行结果

3. inport

3.1 函数说明

函数声明 函数功能
int inport(int protid); 从硬件端口中输入,即从指定的 I/O 端口读取一个 16 位(2 字节)的数据

参数:

  • portid :要读取数据的 I/O 端口号。不同的硬件设备会使用不同的端口号,比如,键盘控制器常用的端口号是 0x60。在使用 inport 函数时,需要根据具体的硬件设备和操作需求来确定正确的端口号。

返回值:
从指定 I/O 端口读取到的 16 位数据。若读取过程中出现错误,返回值的具体情况可能因系统和硬件而异。

3.2 演示示例

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

int main()
{
int result;
int port = 0; // 串行端口 0
// 从硬件端口中输入
result = inport(port);
printf("Word read from port %d = 0x%X\n", port, result);
return 0;
}

4. insline

4.1 函数说明

函数声明 函数功能
void insline(void); 在当前光标位置插入一行空行,原光标所在行及其下方的所有行都会向下移动一行 【在 Turbo C 等早期 C 编译器的图形库或控制台操作库中使用】

4.2 演示示例

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

int main()
{
clrscr();
cprintf("INSLINE inserts an empty line in the text window\r\n");
cprintf("at the cursor position using the current text\r\n");
cprintf("background color. All lines below the empty one\r\n");
cprintf("move down one line and the bottom line scrolls\r\n");
cprintf("off the bottom of the window.\r\n");
cprintf("\r\nPress any key to continue:");
gotoxy(1, 3);
getch();
// 在文本窗口中插入一个空行
insline();
getch();
return 0;
}

5. installuserdriver

5.1 函数说明

函数声明 函数功能
int installuserdriver(char *name, int (*detect)(void)); 安装设备驱动程序到BGI设备驱动程序表中

注意: 该函数在 WinBGI 中不可用

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
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
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

int huge detectEGA(void);
void checkerrors(void);

int main(void)
{
int gdriver, gmode;
// 安装用户编写的设备驱动程序
gdriver = installuserdriver("EGA", detectEGA);
// 必须强制使用检测程序
gdriver = DETECT;
// 检查是否有任何安装错误
checkerrors();
// 初始化图形程序
initgraph(&gdriver, &gmode, "");
// 检查是否有任何初始化错误
checkerrors();
// 画一条对象线
line(0, 0, getmaxx(), getmaxy());

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

/*
检测EGA或VGA卡
*/
int huge detectEGA(void);
{
int driver, mode, sugmode = 0;
detectgraph(&driver, &mode);
if ((driver == EGA) || (driver == VGA))
return sugmode; // 返回建议的视频模式编号
else
return grError; // 返回错误代码
}

/*
检查并报告任何图形错误
*/
void checkerrors(void)
{
// 获取上次图形操作的读取结果
int errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}
}

6. installuserfont

6.1 函数说明

函数声明 函数功能
int installuserfont( char *name ); 安装未嵌入BGI系统的字体文件(CHR)

注意: 该函数在 WinBGI 中不可用

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
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

void checkerrors(void);

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

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

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

checkerrors();

// 安装用户定义的字体文件
userfont = installuserfont("USER.CHR");

checkerrors();

// 选择用户字体
settextstyle(userfont, HORIZ_DIR, 4);

outtextxy(midx, midy, "Testing!");

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

/*
检查并报告任何图形错误
*/
void checkerrors(void)
{
// 获取上次图形操作的读取结果
int errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}
}

7. int86

7.1 函数说明

函数声明 函数功能
int int86(int intr_num, union REGS *inregs, union REGS *outregs); 通用8086软中断接口,用于在 DOS 环境下执行软件中断调用

参数:

  • int intr_num :指定要执行的中断号。在 DOS 系统中,不同的中断号对应着不同的功能。例如,中断号 0x10 通常用于 BIOS 的视频服务,中断号 0x21 用于 DOS 系统功能调用。
  • union REGS *inregs :一个指向 union REGS 联合体的指针,用于传递输入参数到中断服务程序。union REGS 联合体包含了多个寄存器的成员,允许你设置不同寄存器的值,以满足特定中断服务的要求。
  • union REGS *outregs :一个指向 union REGS 联合体的指针。用于接收 DOS 中断服务程序返回的结果。当 intdosx 函数调用完成后,DOS 中断服务程序会将返回值存储在相应的寄存器中,通过 outregs 可以获取这些返回值。

union REGS 联合体:

1
2
3
4
5
6
7
8
9
10
11
12
union REGS {
struct {
unsigned char al, ah;
unsigned char bl, bh;
unsigned char cl, ch;
unsigned char dl, dh;
} h;
struct {
unsigned int ax, bx, cx, dx;
unsigned int si, di, cflag;
} x;
};

返回值: 中断服务程序执行的状态。

  • 0 表示执行成功。
  • 非零值表示执行过程中出现错误。具体的错误含义可能因中断号和中断服务程序而异。

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
#include <stdio.h>
#include <conio.h>
#include <dos.h>

#define VIDEO 0x10

void movetoxy(int x, int y)
{
union REGS regs;

regs.h.ah = 2; /* set cursor postion */
regs.h.dh = y;
regs.h.dl = x;
regs.h.bh = 0; /* video page 0 */
int86(VIDEO, &regs, &regs);
}

int main(void)
{
clrscr();
movetoxy(35, 10);
printf("Hello\n");
return 0;
}

8. int86x

8.1 函数说明

函数声明 函数功能
int int86x(int intr_num, union REGS *insegs, union REGS *outregs, struct SREGS *segregs); 通用8086软中断接口,是一个在 DOS 环境下使用的函数,主要用于执行指定中断号的软件中断。

参数:

  • int intr_num参考7中所示
  • union REGS *inregs参考7中所示
  • union REGS *outregs参考7中所示
  • struct SREGS *segregs : 一个指向 struct SREGS 结构体的指针。
    作用:用于传递和接收段寄存器的值。struct SREGS 结构体包含了多个段寄存器成员,如 ds、es、ss 等。在某些 DOS 功能调用中,可能需要设置或获取段寄存器的值,这时就可以使用 segregs 参数。

struct SREGS 结构体:

1
2
3
4
5
6
struct SREGS {
unsigned int es;
unsigned int cs;
unsigned int ss;
unsigned int ds;
};

返回值: 中断服务程序执行的状态。

  • 0 表示执行成功。
  • 非零值表示执行过程中出现错误。具体的错误含义可能因中断号和中断服务程序而异。

8.2 演示示例

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

int main(void)
{
char filename[80];
union REGS inregs, outregs;
struct SREGS segregs;

printf("Enter filename: ");
gets(filename);
inregs.h.ah = 0x43;
inregs.h.al = 0x21;
inregs.x.dx = FP_OFF(filename);
segregs.ds = FP_SEG(filename);
int86x(0x21, &inregs, &outregs, &segregs);
printf("File attribute: %X\n", outregs.x.cx);
return 0;
}

9. intdos

9.1 函数说明

函数声明 函数功能
int intdos(union REGS *inregs, union REGS *outregs); 执行一次 DOS 软件中断(通常是中断向量号为 0x21 的中断)

参数:

  • union REGS *inregs :一个指向 union REGS 联合体的指针。用于向 DOS 中断服务程序传递输入参数。union REGS 联合体把 8 位寄存器(如 AH、AL 等)和 16 位寄存器(如 AX、BX 等)组合在一起,方便开发者设置不同大小的寄存器值。在调用 intdos 之前,你要依据具体的 DOS 功能需求,把相应的参数存于 inregs 所指向的联合体中。
  • union REGS *outregs :一个指向 union REGS 联合体的指针。用于接收 DOS 中断服务程序返回的结果。当 intdosx 函数调用完成后,DOS 中断服务程序会将返回值存储在相应的寄存器中,通过 outregs 可以获取这些返回值。

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
#include <stdio.h>
#include <dos.h>

/*
删除文件,成功返回0,失败返回非0。
*/
int delete_file(char near *filename)
{
union REGS regs;
int ret;
regs.h.ah = 0x41;
regs.x.dx = (unsigned) filename;
ret = intdos(&regs, &regs);

// 如果设置了进位标志,则出现错误
return(regs.x.cflag ? ret : 0);
}

int main(void)
{
int err;
err = delete_file("NOTEXIST.$$$");
if (!err)
printf("Able to delete NOTEXIST.$$$\n");
else
printf("Not Able to delete NOTEXIST.$$$\n");
return 0;
}

10. intdosx

10.1 函数说明

函数声明 函数功能
int intdosx(union REGS *inregs, union REGS *outregs, struct SREGS *segregs); 它主要作用是触发一个 DOS 软件中断(通常是中断号 0x21),并通过寄存器传递参数和获取返回值,从而实现对 DOS 系统功能的调用。

参数:

  • union REGS *inregs参考 9 中所示
  • union REGS *outregs参考 9 中所示
  • struct SREGS *segregs : 一个指向 struct SREGS 结构体的指针。
    作用:用于传递和接收段寄存器的值。struct SREGS 结构体包含了多个段寄存器成员,如 ds、es、ss 等。在某些 DOS 功能调用中,可能需要设置或获取段寄存器的值,这时就可以使用 segregs 参数。

返回值:

DOS 中断调用的状态。通常,返回值为 0 表示调用成功,非零值表示调用过程中出现错误。

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
#include <stdio.h>
#include <dos.h>

/*
删除文件,成功返回0,失败返回非0。
*/
int delete_file(char far *filename)
{
union REGS regs;
struct SREGS sregs;
int ret;
regs.h.ah = 0x41;
regs.x.dx = FP_OFF(filename);
sregs.ds = FP_SEG(filename);
ret = intdosx(&regs, &regs, &sregs);

// 如果设置了进位标志,则出现错误
return(regs.x.cflag ? ret : 0);
}

int main(void)
{
int err;
err = delete_file("NOTEXIST.$$$");
if (!err)
printf("Able to delete NOTEXIST.$$$\n");
else
printf("Not Able to delete NOTEXIST.$$$\n");
return 0;
}

11. intr

11.1 函数说明

函数声明 函数功能
void intr(int intr_num, struct REGPACK *preg); 它是一个在早期 DOS 环境下用于进行软件中断调用的函数

参数:

  • int intr_num :要调用的中断号。在 DOSBIOS 系统中,不同的中断号对应着不同的功能。例如,中断号 0x10 通常用于 BIOS 的视频服务,中断号 0x21 用于 DOS 系统功能调用。
  • struct REGPACK *preg :这是一个指向 struct REGPACK 结构体的指针。struct REGPACK 结构体用于存储和传递 CPU 寄存器的值。在调用中断之前,你可以通过该结构体设置输入参数(即设置寄存器的值);中断调用完成后,该结构体中的值会被更新为中断服务程序返回的结果。
1
2
3
4
5
6
7
8
9
10
11
12
struct REGPACK {
unsigned r_ax;
unsigned r_bx;
unsigned r_cx;
unsigned r_dx;
unsigned r_bp;
unsigned r_si;
unsigned r_di;
unsigned r_ds;
unsigned r_es;
unsigned r_flags;
};

这个结构体包含了多个成员,分别对应不同的 CPU 寄存器。例如:

  • r_ax 对应 AX 寄存器。
  • r_bx 对应 BX 寄存器。
  • 以此类推,r_cx、r_dx 等分别对应 CX、DX 等寄存器。

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
#include <stdio.h>
#include <string.h>
#include <dir.h>
#include <dos.h>

#define CF 1 // 进位标志

int main(void)
{
char directory[80];
struct REGPACK reg;

printf("Enter directory to change to: ");
gets(directory);
reg.r_ax = 0x3B << 8; // 将3Bh转换为AH
reg.r_dx = FP_OFF(directory);
reg.r_ds = FP_SEG(directory);
intr(0x21, &reg);
if (reg.r_flags & CF)
printf("Directory change failed\n");
getcwd(directory, 80);
printf("The current directory is: %s\n", directory);
return 0;
}

12. ioctl

12.1 函数说明

函数声明 函数功能
int ioctl(int fd, int cmd, ...) ; 控制 I/O 设备

参数:

  • fd :文件描述符
  • cmd : 交互协议,设备驱动将根据 cmd 执行对应操作
  • : 可变参数arg,依赖 cmd 指定长度以及类型

12.2 演示示例

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

int main(void)
{
int stat = ioctl(0, 8, 0, 0);
if (!stat)
printf("Drive %c is removable.\n", getdisk() + 'A');
else
printf("Drive %c is not removable.\n", getdisk() + 'A');
return 0;
}

13. isatty

13.1 函数说明

函数声明 函数功能
int isatty(int handle); 用于检查给定的文件描述符(file descriptor)是否关联到一个终端设备(teletype,即 TTY)

它通常用于判断程序是否运行在交互式终端环境中,从而决定是否启用终端相关的功能(如彩色输出、交互式输入等)。

参数:

  • handle :文件描述符(file descriptor),例如:0(标准输入)1(标准输出)2(标准错误)

13.2 演示示例

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

int main(void)
{
int handle;

handle = fileno(stdprn);
if (isatty(handle))
printf("Handle %d is a device type\n", handle);
else
printf("Handle %d isn't a device type\n", handle);
return 0;
}

13.3 运行结果

14. ilogb,ilogbf,ilogbfl

14.1 函数说明

函数声明 函数功能
int ilogb (double x); 获取 x 的对数的积分部分(double)
int ilogbf (float x); 获取 x 的对数的积分部分(float)
int ilogbl (long double x); 获取 x 的对数的积分部分(long double)

如果计算成功,则返回 x 的对数的整数部分。如果 x0,则此函数返回FP_ILOGB0 并报告错误。如果 x 是NaN值,则此函数返回 FP_ILOGBNAN 并报告错误。如果 x 是正无穷大或负无穷大,此函数将返回 INT_MAX 并报告错误。

14.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 <math.h>

int main()
{
int result;
double x = 15.0;
result = ilogb(x);
printf("The integral part of the logarithm of double value %lf is %d\n", x, result);

float xf = 15.0f;
result = ilogbf(xf);
printf("The integral part of the logarithm of float value %f is %d\n", xf, result);

long double xL = 15.0L;
result = ilogbl(xL);
printf("The integral part of the logarithm of long double value %Lf is %d\n", xL, result);

return 0;
}

14.3 运行结果

15. isalnum

15.1 函数说明

函数声明 函数功能
int isalnum(int c); 检查字符 c 是否为字母或数字

参数:

  • c : 待检查的字符

返回值:

  • 若字符为字母或数字,返回非零值;
  • 否则返回 0。

15.2 演示示例

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

int main()
{
char ch = 'a';
if (isalnum(ch))
{
printf("%c 是字母或数字\n", ch);
}
return 0;
}

从上面示例中,我们可以看到 char 类型的 ch 变量传入了 isalnum 函数,而它函数声明中的入参是 int 类型,那这是为什么呢?

这其实同 C 语言的自动类型转换有关。 我们知道在 C 语言中,char 类型本质上是一种整数类型,它占用一个字节(8 位)的存储空间,用于存储字符的 ASCII 码值(或其他字符编码值)。例如,字符 'a'ASCII 码值是 97。当将一个 char 类型的变量作为参数传递给 isalnum 这种期望 int 类型参数的函数时,C 编译器会自动将 char 类型的值提升为 int 类型。这种转换是隐式的,不需要程序员显式地进行类型转换操作。例如,当执行 isalnum('A') 时,字符 'A'ASCII 码值 97 会被自动提升为 int 类型的 97,然后传递给 isalnum 函数进行处理。

15.3 运行结果

16. isalpha

16.1 函数说明

函数声明 函数功能
int isalpha(int c); 检查字符 c 是否为(大写或小写)字母

参数:

  • c : 待检查的字符

返回值:

  • 如果字符是(大写或小写)字母,返回非零值;
  • 否则返回 0。

16.2 演示示例

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

int main()
{
char ch = 'A';
if (isalpha(ch))
{
printf("%c 是字母\n", ch);
}
return 0;
}

16.3 运行结果

17. isdigit

17.1 函数说明

函数声明 函数功能
int isdigit(int c); 检查字符 c 是否为数字(0 - 9)

参数:

  • c : 待检查的字符

返回值:

  • 若字符是数字,返回非零值;
  • 否则返回 0。

17.2 演示示例

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

int main()
{
char ch = '5';
if (isdigit(ch))
{
printf("%c 是数字\n", ch);
}
return 0;
}

17.3 运行结果

18. isinf

18.1 函数说明

函数声明 函数功能
int isinf(double x); 检查浮点数 x 是否为无穷大(正无穷或负无穷)

参数:

  • x : 待检查的浮点数

返回值:

  • 如果是无穷大,返回非零值;
  • 否则返回 0。

18.2 演示示例

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

int main()
{
double num = 1.0 / 0.0;
if (isinf(num))
{
printf("该数是无穷大\n");
}
return 0;
}

18.3 运行结果

19. isnan

19.1 函数说明

函数声明 函数功能
int isnan(double x); 检查浮点数 x 是否为非数字(NaN)

参数:

  • x : 待检查的浮点数

返回值:

  • 如果是 NaN,返回非零值;
  • 否则返回 0。

19.2 演示示例

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

int main()
{
double num = sqrt(-1.0);
if (isnan(num))
{
printf("该数是 NaN\n");
}
return 0;
}

19.3 运行结果

20. isspace

20.1 函数说明

函数声明 函数功能
int isspace(int c); 检查字符 c 是否为空白字符,如空格(’ ‘)、制表符(’\t’)、换行符(’\n’)等。

参数:

  • c : 待检查的字符

返回值:

  • 若字符是空白字符,返回非零值;
  • 否则返回 0。

20.2 演示示例

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

int main()
{
char ch = ' ';
if (isspace(ch))
{
printf("%c 是空白字符\n", ch);
}
return 0;
}

20.3 运行结果

21. itoa

21.1 函数说明

函数声明 函数功能
char * itoa(int value, char *string, int radix); 把一整数转换为字符串

参数:

  • value : 被转换的整数
  • string : 转换后储存的字符数组
  • radix : 转换进制数,如2,8,10,16 进制等,大小应在2-36之间

21.2 演示示例

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

int main(void)
{
int number = 12345;
char string[25];

itoa(number, string, 2);
printf("integer = %d string = %s\n", number, string);

itoa(number, string, 8);
printf("integer = %d string = %s\n", number, string);

itoa(number, string, 10);
printf("integer = %d string = %s\n", number, string);

itoa(number, string, 16);
printf("integer = %d string = %s\n", number, string);
return 0;
}

21.3 运行结果

参考

  1. [API Reference Document]
  2. [ioctl]

总览

函数声明 函数功能
double hypot(double x, double y); 计算直角三角形的斜边长(double)
float hypotf (float x, float y); 计算直角三角形的斜边长(float)
long double hypot(long double x, long double y); 计算直角三角形的斜边长(long double)
#define HUGE_VAL _HUGE 正浮点常量表达式(double),这些表达式与浮点函数和运算符在溢出时返回的值相比较
#define HUGE_VALF __INFF 正浮点常量表达式(float),这些表达式与浮点函数和运算符在溢出时返回的值相比较
#define HUGE_VALL __INFL 正浮点常量表达式(long double),这些表达式与浮点函数和运算符在溢出时返回的值相比较
void harderr(int (*fptr)()); 建立一个硬件错误处理程序
void hardresume(int rescode); 硬件错误处理函数
void hardretn(int rescode); 硬件错误处理函数
void highvideo(void); 选择高亮度文本字符
int hcreate(size_t nel); 根据条目数创建哈希表。
int hcreate_r(size_t nel, struct hsearch_data *htab); 根据条目数及其描述创建哈希表。
ENTRY *hsearch(ENTRY item, ACTION action); 添加或搜索哈希条目。
int hsearch_r (ENTRY item, ACTION action, ENTRY ** retval, struct hsearch_data * htab ) 搜索哈希表。
void hdestroy(void); 销毁哈希表,释放用于创建哈希表的内存。
void hdestroy_r(struct hsearch_data *htab); 销毁哈希表,释放指定哈希表所占用的内存。

1. hypot,hypotf,hypotl

1.1 函数说明

函数声明 函数功能
double hypot(double x, double y); 计算直角三角形的斜边长(double)
float hypotf (float x, float y); 计算直角三角形的斜边长(float)
long double hypot(long double x, long double y); 计算直角三角形的斜边长(long double)

1.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 <math.h>

int main(void)
{
double result, x = 3.0, y = 4.0;
result = hypot(x, y);

float resultf, xf = 3.0, yf = 4.0;
resultf = hypotf(xf, yf);

long double resultL, xL = 3.0, yL = 4.0;
resultL = hypotl(xL, yL);

printf("The hypotenuse of a right triangle whose legs are %lf and %lf is %lf\n", x, y, result);
printf("The hypotenuse of a right triangle whose legs are %f and %f is %f\n", xf, yf, resultf);
printf("The hypotenuse of a right triangle whose legs are %Lf and %Lf is %Lf\n", xL, yL, resultL);

return 0;
}

1.3 运行结果

2. HUGE_VAL,HUGE_VALF,HUGE_VALL

2.1 函数说明

宏定义 宏描述
#define HUGE_VAL _HUGE 正浮点常量表达式(double),这些表达式与浮点函数和运算符在溢出时返回的值相比较
#define HUGE_VALF __INFF 正浮点常量表达式(float),这些表达式与浮点函数和运算符在溢出时返回的值相比较
#define HUGE_VALL __INFL 正浮点常量表达式(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
22
#include<stdio.h>
#include<math.h>
int main()
{
double result = 1.0/0.0;
printf("1.0/0.0 = %lf\n", result);
if (result == HUGE_VAL)
puts("1.0/0.0 == HUGE_VAL\n");

float resultf = 1.0f/0.0f;
printf("1.0f/0.0f = %f\n", resultf);
if (resultf == HUGE_VALF)
puts("1.0f/0.0f == HUGE_VALF\n");

long double resultL = 1.0L/0.0L;
printf("1.0L/0.0L = %Lf\n", resultL);
if (resultL == HUGE_VALL)
puts("1.0L/0.0L == HUGE_VALL\n");

return 0;
}

2.3 运行结果

3. harderr,hardresume,hardretn

3.1 函数说明

函数声明 函数功能
void harderr(int (*fptr)()); 建立一个硬件错误处理程序
void hardresume(int rescode); 硬件错误处理函数
void hardretn(int rescode); 硬件错误处理函数

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
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
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#define IGNORE 0
#define RETRY 1
#define ABORT 2
int buf[500];

//定义捕获磁盘问题的错误消息
static char *err_msg[] = {
"write protect",
"unknown unit",
"drive not ready",
"unknown command",
"data error (CRC)",
"bad request",
"seek error",
"unknown media type",
"sector not found",
"printer out of paper",
"write fault",
"read fault",
"general failure",
"reserved",
"reserved",
"invalid disk change"
};

int error_win(char *msg)
{
int retval;
cputs(msg);

// 提示用户按键中止、重试、忽略
while(1)
{
retval= getch();
if (retval == 'a' || retval == 'A')
{
retval = ABORT;
break;
}
if (retval == 'r' || retval == 'R')
{
retval = RETRY;
break;
}
if (retval == 'i' || retval == 'I')
{
retval = IGNORE;
break;
}
}

return(retval);
}

/*
pragma warn-par 减少了由于处理程序未使用参数errval、bp 和 si而产生的警告。
*/
#pragma warn -par

int handler(int errval,int ax,int bp,int si)
{
static char msg[80];
unsigned di;
int drive;
int errorno;
di= _DI;

// 如果这不是磁盘错误,那么是另一个设备出现故障
if (ax < 0)
{
error_win("Device error");
// 返回到直接请求中止的程序
hardretn(ABORT);
}
// 否则就是磁盘错误
drive = ax & 0x00FF;
errorno = di & 0x00FF;
sprintf(msg, "Error: %s on drive %c\r\nA)bort, R)etry, I)gnore: ", err_msg[errorno], 'A' + drive);
// 通过dos中断0x23返回程序,并由用户输入中止、重试或忽略。
hardresume(error_win(msg));
return ABORT;
}

#pragma warn +par

int main(void)
{
// 在硬件问题中断上安装我们的处理程序
harderr(handler);
clrscr();
printf("Make sure there is no disk in drive A:\n");
printf("Press any key ....\n");
getch();
printf("Trying to access drive A:\n");
printf("fopen returned %p\n", fopen("A:temp.dat", "w"));
return 0;
}

上述程序是一个基于DOS环境的磁盘错误处理示例。在磁盘操作出现错误时,向用户显示具体的错误消息,并提供 “中止”“重试”“忽略” 三种处理选项,根据用户的选择进行相应的处理。

4. highvideo

4.1 函数说明

函数声明 函数功能
void highvideo(void); 选择高亮度文本字符

4.2 演示示例

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

int main(void)
{
clrscr();

lowvideo();
cprintf("Low Intensity text\r\n");
highvideo();
gotoxy(1,2);
cprintf("High Intensity Text\r\n");

return 0;
}

上述利用 <conio.h> 头文件中的函数实现特定的控制台文本显示效果。

  • 首先通过 clrscr() 函数清空控制台窗口。
  • 然后调用 lowvideo() 将文本显示设置为低亮度,使用 cprintf() 输出低亮度的 "Low Intensity text" 文本并换行。
  • 接着调用 highvideo() 把文本显示切换为高亮度,利用 gotoxy(1, 2) 将光标定位到第 1 列第 2 行,再用 cprintf() 输出高亮度的 "High Intensity Text" 文本并换行,以此直观展示控制台中低亮度和高亮度两种不同的文本显示效果。

不过需要留意,conio.h 并非标准 C 库的一部分,它主要在像 Turbo C 这类旧的编译器中使用,而在现代开发环境里可能不被支持。

5. hcreate,hcreate_r

5.1 函数说明

函数声明 函数功能
int hcreate(size_t nel); 根据条目数创建哈希表。
int hcreate_r(size_t nel, struct hsearch_data *htab); 根据条目数及其描述创建哈希表。

入参:

  • net : 哈希表中允许的最大项数。
  • htab : 哈希表的结构体数据。

返回值:

  • 如果操作成功,则返回一个非零值;
  • 如果操作失败,则返回 0 并将 errno 设置为一个值。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <stdlib.h>
#include <search.h>

char *data[] = { "alpha", "bravo", "charlie", "delta",
"echo", "foxtrot", "golf", "hotel", "india", "juliet",
"kilo", "lima", "mike", "november", "oscar", "papa",
"quebec", "romeo", "sierra", "tango", "uniform",
"victor", "whisky", "x-ray", "yankee", "zulu"
};

int main(void)
{
ENTRY e, *ep;
int i;

hcreate(30);

for (i = 0; i < 24; i++) {
e.key = data[i];
// 数据只是一个整数,而不是指向某个东西的指针
e.data = (void *) i;
ep = hsearch(e, ENTER);
// 这里不应该有失败场景
if (ep == NULL) {
fprintf(stderr, "entry failed\n");
exit(EXIT_FAILURE);
}
}

for (i = 22; i < 26; i++) {
// 从表中打印两个条目,并显示其中两个不在表中
e.key = data[i];
ep = hsearch(e, FIND);
printf("%9.9s -> %9.9s:%d\n", e.key, ep ? ep->key : "NULL", ep ? (int)(ep->data) : 0);
}
hdestroy();
exit(EXIT_SUCCESS);
}

6. hsearch,hsearch_r

6.1 函数说明

函数声明 函数功能
ENTRY *hsearch(ENTRY item, ACTION action); 添加或搜索哈希条目。
int hsearch_r (ENTRY item, ACTION action, ENTRY ** retval, struct hsearch_data * htab ) 搜索哈希表。

注意:
hsearchhsearch_r 函数根据指定的操作在哈希表中搜索条目。如果操作为 FIND,则仅执行搜索操作。如果操作为 ENTER,则未找到的条目将添加到哈希表中。hsearch_r 函数与 hsearch 函数的不同之处在于,指向找到的项的指针以 *retval 形式返回,而不是作为函数结果。

入参:

  • item: 要搜索的哈希表条目。
  • action: 功能操作。ENTER 表示已添加条目,FIND 表示已搜索条目。
  • retval: 指向找到的项的指针。
  • htab : 哈希表的结构体数据。

hsearch 函数返回值:

  • 如果操作成功,则返回指向哈希表的指针。

hsearch_r 函数返回值:

  • 如果操作成功,则返回一个非零值;
  • 如果操作失败,则返回 0。

6.2 演示示例

参考 5.2

7. hdestroy,hdestroy_r

7.1 函数说明

函数声明 函数功能
void hdestroy(void); 销毁哈希表,释放用于创建哈希表的内存。
void hdestroy_r(struct hsearch_data *htab); 销毁哈希表,释放指定哈希表所占用的内存。

7.2 演示示例

参考 5.2

8. htonl, htons

8.1 函数说明

函数声明 函数功能
uint32_t htonl(uint32_t hostlong); 将 uint32_t(32位整数,如IPv4地址)从主机字节序转为网络字节序。
uint16_t htons(uint16_t hostshort); 将 uint16_t(16位整数,如端口号)从主机字节序转为网络字节序(大端)。

8.2 演示示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <stdint.h> // 提供 uint16_t, uint32_t 类型
#include <arpa/inet.h> // Linux/macOS 头文件(网络字节序转换)

int main() {
// 示例1:转换16位端口号(host to network short)
uint16_t host_port = 0x1234; // 主机字节序的端口(假设是小端模式)
uint16_t net_port = htons(host_port);
printf("主机端口: 0x%04x -> 网络端口: 0x%04x\n", host_port, net_port);

// 示例2:转换32位IP地址(host to network long)
uint32_t host_ip = 0x12345678; // 主机字节序的IP地址
uint32_t net_ip = htonl(host_ip);
printf("主机IP: 0x%08x -> 网络IP: 0x%08x\n", host_ip, net_ip);

return 0;
}

参考

  1. [API Reference Document]
  2. [highvideo]
  3. [hcreate,hsearch,hdestroy,hcreate_r,hsearch_r,hdestroy_r]
  4. [UTILS-标准C库]

总览

函数声明 函数功能
char * getmodename(int mode_name); 获取指定的图形模式名
void getmoderange(int graphdriver, int *lomode, int *himode); 获取给定图形驱动程序的模式范围
void getpalette(struct palettetype *palette); 获取有关当前调色板的信息
int getpixel(int x, int y); 获取得指定像素的颜色
char *gets(char *str); 从标准输入流中读取字符串,直至遇到到换行符或EOF时停止,并将读取的结果存放在 buffer 指针所指向的字符数组中。
换行符不作为读取串的内容,读取的换行符被转换为 '\0' 空字符,并由此来结束字符串。
void gettextsettings(struct textsettingstype *textinfo); 获取有关当前图形文本字体的信息
void getviewsettings(struct viewporttype *viewport); 获取有关当前视区的信息
int getw(FILE *strem); 从 stream 所指向文件读取下一个整数
int getx(void); 获取当前图形位置的 x 坐标
int gety(void); 获取当前图形位置的 y 坐标
struct tm *gmtime(long *clock); 把日期和时间转换为格林尼治标准时间(GMT)
void graphdefaults(void); 将所有图形设置复位为它们的缺省值
char * grapherrormsg(int errorcode); 返回一个错误信息串的指针
int graphresult(void); 返回最后一次不成功的图形操作的错误代码
int getmaxwidth(void); 获取屏幕的最大宽度
int getmaxheight(void); 获取屏幕的最大高度
int getdisplaycolor( int color ); 根据 color ,返回要显示的颜色值
int getwindowwidth(); 获取图形界面窗口宽度
int getwindowheight(void); 获取图形界面窗口高度
bool getrefreshingbgi(void); 获取刷新基础图形界面标识

1. getmodename

1.1 函数说明

函数声明 函数功能
char * getmodename(int mode_name); 获取指定的图形模式名

参数:

  • mode_name : 当前图形模式的模式代码。不同的图形模式对应不同的分辨率、颜色深度和显示模式。

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
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>

int main(void)
{
int gdriver = DETECT, gmode, errorcode;
int midx, midy, mode;
char numname[80], modename[80];

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

errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}

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

mode = getgraphmode();
sprintf(numname, "%d is the current mode number.", mode);
sprintf(modename, "%s is the current graphics mode.", getmodename(mode));

settextjustify(CENTER_TEXT, CENTER_TEXT);
outtextxy(midx, midy, numname);
outtextxy(midx, midy+2*textheight("W"), modename);

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

1.3 运行结果

2. getmoderange

2.1 函数说明

函数声明 函数功能
void getmoderange(int graphdriver, int *lomode, int *himode); 获取给定图形驱动程序的模式范围

参数:

  • graphdriver : 图形驱动程序的标识符。不同的图形驱动程序有不同的标识符,用于指定你希望使用的图形硬件或软件环境。例如,在某些图形库中,特定的数字或宏定义(如DETECT)可以用来自动检测可用的图形驱动程序。
  • lomode : 一个指向整数的指针,用于接收指定图形驱动程序支持的最低显示模式编号。
  • himode : 一个指向整数的指针,用于接收指定图形驱动程序支持的最高显示模式编号。

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
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
int gdriver = DETECT, gmode, errorcode;
int midx, midy, low, high;
char mrange[80];

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

errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}

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

getmoderange(gdriver, &low, &high);

sprintf(mrange, "This driver supports modes %d~%d", low, high);

settextjustify(CENTER_TEXT, CENTER_TEXT);
outtextxy(midx, midy, mrange);

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

2.3 运行结果

3. getpalette

3.1 函数说明

函数声明 函数功能
void getpalette(struct palettetype *palette); 获取有关当前调色板的信息

参数:

  • palette : 一个指向palettetype结构体的指针。palettetype结构体通常包含了一系列元素,每个元素代表调色板中的一个颜色条目。在标准的图形库中(如BorlandBGI图形库),palettetype结构体可能包含多个unsigned char类型的成员,每个成员对应调色板中的一个颜色通道(如红色、绿色、蓝色),以及可能的其他信息(如亮度或透明度)。

注意: palettetype结构体的确切定义可能依赖于你使用的图形库。在某些实现中,它可能是一个简单的数组,每个元素代表一个颜色(可能是RGB值的一个组合),或者是一个更复杂的结构体,包含了关于每个颜色条目的更多信息。

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
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
int gdriver = DETECT, gmode, errorcode;
struct palettetype pal;
char psize[80], pval[20];
int i, ht;
int y = 10;

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

errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}

getpalette(&pal);

sprintf(psize, "The palette has %d modifiable entries.", pal.size);

outtextxy(0, y, psize);
if (pal.size != 0)
{
ht = textheight("W");
y += 2*ht;
outtextxy(0, y, "Here are the current values:");
y += 2*ht;
for (i=0; i < pal.size; i++, y+=ht)
{
sprintf(pval, "palette[%02d]: 0x%02X", i, pal.colors[i]);
outtextxy(0, y, pval);
}
}

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

3.3 运行结果

4. getpixel

4.1 函数说明

函数声明 函数功能
int getpixel(int x, int y); 获取得指定像素的颜色

参数:

  • x : 想要获取的像素颜色值的点的横坐标。坐标原点(0, 0)通常位于屏幕的左上角。
  • y : 想要获取的像素颜色值的点的纵坐标。

返回值:

函数返回一个整数,该整数代表指定坐标 (x, y) 上像素的颜色编码。颜色编码的具体含义取决于你使用的图形库和当前的图形设置。在某些图形库中,这个整数可能直接代表一个RGB颜色值,其中不同的位或字节表示红色、绿色和蓝色通道的强度。在其他情况下,这个整数可能是一个索引值,指向当前调色板中的一个颜色条目。

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
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#define PIXEL_COUNT 1000
#define DELAY_TIME 100

int main(void)
{
int gdriver = DETECT, gmode, errorcode;
int midx, midy, x, y, color, maxx, maxy, maxcolor;
char mPixel[50];

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

errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}

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

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

while (!kbhit())
{
srand((unsigned)time(NULL));
x = rand() % maxx;
y = rand() % maxy;
color = rand() % maxcolor;
putpixel(x, y, color);

sprintf(mPixel, "color of pixel at (%d,%d) = %d", x, y, getpixel(x, y));
settextjustify(CENTER_TEXT, CENTER_TEXT);
outtextxy(midx, midy, mPixel);

delay(DELAY_TIME);

cleardevice();
}

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

4.3 运行结果

5. gets

5.1 函数说明

函数声明 函数功能
char *gets(char *str); 从标准输入流中读取字符串,直至遇到到换行符或EOF时停止,并将读取的结果存放在 buffer 指针所指向的字符数组中。
换行符不作为读取串的内容,读取的换行符被转换为 '\0' 空字符,并由此来结束字符串。

注意: gets 函数可以无限读取,易发生溢出。如果溢出,多出来的字符将被写入到堆栈中,这就覆盖了堆栈原先的内容,破坏一个或多个不相关变量的值。

5.2 演示示例

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

int main()
{
char string[80];

printf("Input a string:");
gets(string);
printf("The string input was: %s\n", string);
return 0;
}

5.3 运行结果

6. gettextsettings

6.1 函数说明

函数声明 函数功能
void gettextsettings(struct textsettingstype *textinfo); 获取有关当前图形文本字体的信息

参数:

  • textinfo : 一个指向 textsettingstype 结构体的指针。该结构体用于存储当前的文本设置。textsettingstype 结构体的具体定义取决于你使用的图形库。在不同的图形库中,这个结构体可能包含不同的成员,以反映该库支持的文本设置选项。

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
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

// 文本字体
char *font[] = { "DEFAULT_FONT",
"TRIPLEX_FONT",
"SMALL_FONT",
"SANS_SERIF_FONT",
"GOTHIC_FONT"
};

// 文本方向
char *dir[] = { "HORIZ_DIR", "VERT_DIR" };

// 文本水平对齐方式
char *hjust[] = { "LEFT_TEXT", "CENTER_TEXT", "RIGHT_TEXT" };

// 文本垂直对齐方式
char *vjust[] = { "BOTTOM_TEXT", "CENTER_TEXT", "TOP_TEXT" };

int main()
{
int gdriver = DETECT, gmode, errorcode;
struct textsettingstype textinfo;
int midx, midy, ht;
char fontstr[80], dirstr[80], sizestr[80];
char hjuststr[80], vjuststr[80];

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

errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}

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

// 获取有关当前图形文本字体的信息
gettextsettings(&textinfo);

sprintf(fontstr, "%s is the text style.", font[textinfo.font]);
sprintf(dirstr, "%s is the text direction.", dir[textinfo.direction]);
sprintf(sizestr, "%d is the text size.", textinfo.charsize);
sprintf(hjuststr, "%s is the horizontal justification.", hjust[textinfo.horiz]);
sprintf(vjuststr, "%s is the vertical justification.", vjust[textinfo.vert]);

ht = textheight("W");
settextjustify(CENTER_TEXT, CENTER_TEXT);
outtextxy(midx, midy, fontstr);
outtextxy(midx, midy+2*ht, dirstr);
outtextxy(midx, midy+4*ht, sizestr);
outtextxy(midx, midy+6*ht, hjuststr);
outtextxy(midx, midy+8*ht, vjuststr);

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

6.3 运行结果

7. getviewsettings

7.1 函数说明

函数声明 函数功能
void getviewsettings(struct viewporttype *viewport); 获取有关当前视区的信息

参数:

  • viewport : 一个指向 viewporttype 结构体的指针。该结构体用于存储当前的视口设置。调用 getviewsettings 函数后,这个结构体将被填充为当前的视口参数。 viewporttype 结构体的具体定义可能依赖于你使用的图形库,但通常它会包含以下成员:
    • left, top: 这两个成员定义了视口的左上角坐标。坐标原点通常位于屏幕的左上角。
    • right, bottom: 这两个成员定义了视口的右下角坐标。
    • clip: 一个用于指示视口是否启用裁剪的标志。如果启用了裁剪,那么任何在视口之外的图形输出都将被忽略。

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
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>

char *clip[] = { "OFF", "ON" };

int main()
{
int gdriver = DETECT, gmode, errorcode;
struct viewporttype viewinfo;
int midx, midy, ht;
char topstr[80], botstr[80], clipstr[80];

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

errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}

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

// 获取有关当前视区的信息
getviewsettings(&viewinfo);

sprintf(topstr, "(%d, %d) is the upper left viewport corner.", viewinfo.left, viewinfo.top);
sprintf(botstr, "(%d, %d) is the lower right viewport corner.", viewinfo.right, viewinfo.bottom);
sprintf(clipstr, "Clipping is turned %s.", clip[viewinfo.clip]);

settextjustify(CENTER_TEXT, CENTER_TEXT);
ht = textheight("W");
outtextxy(midx, midy, topstr);
outtextxy(midx, midy+2*ht, botstr);
outtextxy(midx, midy+4*ht, clipstr);

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

7.3 运行结果

8. getw

8.1 函数说明

函数声明 函数功能
int getw(FILE *strem); 从 stream 所指向文件读取下一个整数

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
#include <stdio.h>
#include <stdlib.h>

#define FNAME "test.txt"

int main(void)
{
FILE *fp;
int word;

fp = fopen(FNAME, "wb");
if (fp == NULL)
{
printf("Error opening file %s\n", FNAME);
exit(1);
}

word = 94;
putw(word,fp);
if (ferror(fp))
printf("Error writing to file\n");
else
printf("Successful write\n");
fclose(fp);

fp = fopen(FNAME, "rb");
if (fp == NULL)
{
printf("Error opening file %s\n", FNAME);
exit(1);
}

word = getw(fp);
if (ferror(fp))
printf("Error reading file\n");
else
printf("Successful read: word = %d\n", word);

fclose(fp);
unlink(FNAME);

return 0;
}

8.3 运行结果

9. getx,gety

9.1 函数说明

函数声明 函数功能
int getx(void); 获取当前图形位置的 x 坐标
int gety(void); 获取当前图形位置的 y 坐标

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
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>

int main(void)
{
int gdriver = DETECT, gmode, errorcode;
char msg[80];

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

errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}

moveto(getmaxx() / 2, getmaxy() / 2);

sprintf(msg, "<-(%d, %d) is the here.", getx(), gety());

outtext(msg);

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

9.3 运行结果

10. gmtime

10.1 函数说明

函数声明 函数功能
struct tm *gmtime(long *clock); 把日期和时间转换为格林尼治标准时间(GMT)

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
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <dos.h>

// 太平洋标准时间和夏令时
char *tzstr = "TZ=PST8PDT";

int main(void)
{
time_t t;
struct tm *gmt, *area;
putenv(tzstr); // 用来改变或增加环境变量的内容
tzset(); // UNIX时间兼容函数
// 获取当前的系统时间,其值表示从协调世界时(Coordinated Universal Time)
// 1970年1月1日00:00:00(称为UNIX系统的Epoch时间)到当前时刻的秒数。
t = time(NULL);
area = localtime(&t); // 把从1970-1-1零点零分到当前时间系统所偏移的秒数时间转换为本地时间
// asctime 把timeptr指向的tm结构体中储存的时间转换为字符串
printf("Local time is: %s", asctime(area));
// 把日期和时间转换为格林尼治标准时间(GMT)
gmt = gmtime(&t);
printf("GMT is: %s", asctime(gmt));
return 0;
}

10.3 运行结果

11. graphdefaults

11.1 函数说明

函数声明 函数功能
void graphdefaults(void); 将所有图形设置复位为它们的缺省值

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
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

int main(void)
{
int gdriver = DETECT, gmode, errorcode;
int maxx, maxy;

initgraph(&gdriver, &gmode, "c:\\bor\\Borland\\bgi");
errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}

maxx = getmaxx();
maxy = getmaxy();

setlinestyle(DOTTED_LINE, 0, 3);
line(0, 0, maxx, maxy);
outtextxy(maxx/2, maxy/3, "Before default values are restored.");
getch();

// 将所有图形设置复位为它们的缺省值
graphdefaults();

cleardevice();

line(0, 0, maxx, maxy);
outtextxy(maxx/2, maxy/3, "After restoring default values.");

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

11.3 运行结果

12. grapherrormsg

12.1 函数说明

函数声明 函数功能
char * grapherrormsg(int errorcode); 返回一个错误信息串的指针

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
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>

#define NONSENSE -50

int main(void)
{
// FORCE AN ERROR TO OCCUR
int gdriver = NONSENSE, gmode, errorcode;

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

errorcode = graphresult();

if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}

line(0, 0, getmaxx(), getmaxy());

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

12.3 运行结果

13. graphresult

13.1 函数说明

函数声明 函数功能
int graphresult(void); 返回最后一次不成功的图形操作的错误代码

13.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 <graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

int main(void)
{
// request auto detection
int gdriver = DETECT, gmode, errorcode;

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

errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}

line(0, 0, getmaxx(), getmaxy());

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

13.3 运行结果

14. getmaxwidth,getmaxheight

14.1 函数说明

函数声明 函数功能
int getmaxwidth(void); 获取屏幕的最大宽度
int getmaxheight(void); 获取屏幕的最大高度

14.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 <graphics.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
int gdriver = DETECT, gmode, errorcode;
int midx, midy;
char ch[80];

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

errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}

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

sprintf(ch, "maxwidth = %d, maxheight = %d", getmaxwidth(), getmaxheight());

settextjustify(CENTER_TEXT, CENTER_TEXT);
outtextxy(midx, midy, ch);

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

14.3 运行结果

15. getdisplaycolor

15.1 函数说明

函数声明 函数功能
int getdisplaycolor( int color ); 根据 color ,返回要显示的颜色值

注意: color = -1 , 则返回 WHITE = 15 的颜色值;color < - 1 或 color > 15,则输出一个8位整数。

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
27
28
29
30
31
32
33
34
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
int gdriver = DETECT, gmode, errorcode;
int midx, midy;
char ch[80];

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

errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}

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

sprintf(ch, "color = %d, displaycolor(-1) = %d, displaycolor(16) = %d", getcolor(), getdisplaycolor(-1), getdisplaycolor(16));

settextjustify(CENTER_TEXT, CENTER_TEXT);
outtextxy(midx, midy, ch);

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

15.3 运行结果

16. getwindowwidth,getwindowheight

16.1 函数说明

函数声明 函数功能
int getwindowwidth(void); 获取图形界面窗口宽度
int getwindowheight(void); 获取图形界面窗口高度

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
34
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
int gdriver = DETECT, gmode, errorcode;
int midx, midy, low, high;
char ch[80];

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

errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}

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

sprintf(ch, "windowwidth = %d, windowheight = %d", getwindowwidth(), getwindowheight());

settextjustify(CENTER_TEXT, CENTER_TEXT);
outtextxy(midx, midy, ch);

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

16.3 运行结果

17. getrefreshingbgi

17.1 函数说明

函数声明 函数功能
bool getrefreshingbgi(void); 获取刷新基础图形界面标识

17.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 <graphics.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
int gdriver = DETECT, gmode, errorcode;
int midx, midy, low, high;
char ch[80];

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

errorcode = graphresult();
if (errorcode != grOk)
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1);
}

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

sprintf(ch, "refreshingbgi = %d", getrefreshingbgi());

settextjustify(CENTER_TEXT, CENTER_TEXT);
outtextxy(midx, midy, ch);

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

17.3 运行结果

参考

  1. [API Reference Document]
  2. [gets]
0%