函数和关键字

本篇主要介绍:自定义函数宏函数字符串处理函数关键字

自定义函数

基本用法

实现一个 add() 函数。请看示例:

#include <stdio.h>

// 自定义函数,用于计算两个整数的和
int add(int a, int b) { // a, b 叫形参
int sum = a + b;
return sum;
} int main() {
int num1 = 3;
int num2 = 5; // 调用自定义函数计算两个整数的和
int result = add(num1, num2); // num1, num2 叫实参 printf("两个整数的和为:%d\n", result); return 0;
}

其中a, b 叫形参,num1, num2 叫实参

Tip:形参和实参的个数不同,笔者编译器报错如下(一个说给函数的参数少,一个说给函数的参数多了):

// 3个形参,2个实参
int add(int a, int b, int c) {} // error: too few arguments to function call, expected 3, have 2
int result = add(num1, num2);
// 2个形参,3个实参
int add(int a, int b) {} // error: too many arguments to function call, expected 2, have 3
int result = add(num1, num2, num1);

函数调用过程

函数调用过程:

  1. 通过函数名找到函数的入口地址
  2. 给形参分配内存空间
  3. 传参。包含值传递地址传递(比如js中的对象)
  4. 执行函数体
  5. 返回数据
  6. 释放空间。例如栈空间

请看示例:

#include <stdio.h>

// 2. 给形参分配内存空间
// 3. 传参:值传递和地址传递(比如js中的对象)
// 4. 执行函数体
// 5. 返回数据
// 6. 释放空间。例如栈空间:局部变量 a,b,sum
int add(int a, int b) {
int sum = a + b;
return sum;
} int main() {
int num1 = 3;
int num2 = 5; // 1. 通过函数名找到函数的入口地址
int result = add(num1, num2); printf("add() 的地址:%p\n", add);
printf("%d\n", result); return 0;
}

输出:

add() 的地址:0x401130
8

练习-sizeof

题目:以下两次 sizeof 输出的值相同吗?

#include <stdio.h>

void printSize(int arr[]) {
printf("Size of arr: %zu\n", sizeof(arr));
} int main() {
int nums[] = {1, 2, 3, 4, 5}; printf("Size of nums: %zu\n", sizeof(nums));
printSize(nums); return 0;
}

运行:

开始运行...
// sizeof(arr) 获取的是指针类型 int * 的大小(在此例中是8字节)
/workspace/CProject-test/main.c:4:40: warning: sizeof on array function parameter will return size of 'int *' instead of 'int[]' [-Wsizeof-array-argument]
printf("Size of arr: %zu\n", sizeof(arr));
^
/workspace/CProject-test/main.c:3:20: note: declared here
void printSize(int arr[]) {
^
1 warning generated.
Size of nums: 20
Size of arr: 8 运行结束。

结果:输出不相同,一个是数组的大小,一个却是指针类型的大小。

结果分析:将一个数组作为函数的参数传递时,它会被隐式地转换为指向数组首元素的指针,然后在函数中使用 sizeof 运算符获取数组大小时,实际上返回的是指针类型的大小((通常为4或8字节,取决于系统架构)),而不是整个数组的大小。

宏函数

宏函数是C语言中的一种预处理指令,用于在编译之前将代码片段进行替换

之前我们用 #define 定义了常量:#define MAX_NUM 100。定义宏函数就是将常量改为函数。就像这样

#include <stdio.h>
// 无参
#define PRINT printf("hello\n") // 有参
#define PRINT2(n) printf("%d\n", n) int main() { // 无参调用
PRINT;
// 有参调用
PRINT2(10);
return 0;
}

输出:hello 10

编译流程

宏函数发生在编译的第一步。

编译可以分为以下几个步骤:

  • 预处理(Preprocessing):在这一步中,预处理器将对源代码进行处理。它会展开宏定义、处理条件编译指令(如 #if、#ifdef 等)、包含头文件等操作。处理后的代码会生成一个被称为预处理文件(通常以 .i 或 .ii 为扩展名)。
  • 编译(Compilation):在这一步中,编译器将预处理后的代码翻译成汇编语言。它会进行词法分析、语法分析、语义分析和优化等操作,将高级语言的代码转化为低级机器可以理解的形式。输出的文件通常以 .s 为扩展名,是一个汇编语言文件。
  • 汇编(Assembly):汇编器将汇编语言代码转换为机器语言指令。它将每条汇编语句映射到对应的机器语言指令,并生成一个目标文件(通常以 .o 或 .obj 为扩展名),其中包含已汇编的机器指令和符号表信息。
  • 链接(Linking):如果程序涉及多个源文件,以及使用了外部库函数或共享的代码模块,链接器将合并和解析这些文件和模块。它会将目标文件与库文件进行链接,解析符号引用、处理重定位等。最终生成可执行文件(或共享库),其中包含了完整的机器指令。

这些步骤并非一成不变,具体的编译过程可能因为编译器工具链和目标平台的不同而有所差异。但是大致上,这是一个常见的编译流程。

宏函数 vs 普通函数

用普通函数和宏函数实现平方的功能,代码分别如下:

int square(int x) {
return x * x;
}
#define SQUARE(x) ((x)*(x))

宏函数在编译过程中被简单地替换为相应的代码片段。它没有函数调用的开销,可以直接插入到调用的位置,这样可以提高代码执行效率

这发生在预处理阶段,不会进行类型检查错误检查,可能导致意外的行为或结果。例如:宏函数中需打印字符串,而参数传递数字1:

#include <stdio.h>

#define PRINT2(n) printf("%s\n", n)

int main() {

    PRINT2(1);
return 0;
}

编译有告警,执行文件还是生成了:

pjl@pjl-pc:~/ph$ gcc demo-3.c -o demo-3
demo-3.c: In function ‘main’:
demo-3.c:3:26: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
3 | #define PRINT2(n) printf("%s\n", n)
| ^~~~~~
......
7 | PRINT2(1);
| ~
| |
| int
demo-3.c:7:5: note: in expansion of macro ‘PRINT2’
7 | PRINT2(1);
| ^~~~~~
demo-3.c:3:28: note: format string is defined here
3 | #define PRINT2(n) printf("%s\n", n)
| ~^
| |
| char *
| %d

但运行还是报错:

pjl@pjl-pc:~/ph$ ./demo-3
段错误 (核心已转储)

普通函数具备了类型检查作用域错误检查等功能,可以更加安全可靠地使用。但是函数调用需要一定的开销,涉及保存现场、跳转等操作。例如:

#define ADD(a, b) (a + b)

int result = ADD(3, 5);

编译器会将宏函数展开为 (3 + 5),并直接插入到 ADD(3, 5) 的位置,避免了函数调用的开销。

练习

题目:请问以下输出什么?

#include <stdio.h>

#define SQUARE(x) x * x

int main() {
int result = SQUARE(1 + 2);
printf("%d\n", result); return 0;
}

输出:5。

分析:

// 1 + 2 * 1 + 2
#define SQUARE(x) x * x

如果希望输出 9 可以用括号,就像这样:

//(1 + 2) * (1 + 2)
#define SQUARE(x) (x) * (x)

字符串处理函数

以下几个字符串处理函数都来自 <string.h> 库函数。

strlen()

strlen() - 用于获取字符串的长度,即字符串中字符的个数(不包括结尾的空字符'\0')

语法:

#include <string.h>

size_t strlen(const char *str);

示例:

#include <stdio.h>
#include <string.h> int main() {
char str[] = "Hello, world!";
size_t length = strlen(str);
// Length of the string: 13
printf("Length of the string: %zu\n", length); return 0;
}

Tip: %zu只用于格式化输出 size_t 类型的格式控制符

size_t

size_t是无符号整数类型。unsigned int 也是无符号整数,两者还是有区别的。

size_t 被定义为足够大以容纳系统中最大可能的对象大小的无符号整数类型,可以处理比 unsigned int更大的值。

在涉及到内存分配、数组索引、循环迭代等需要表示大小的场景中,建议使用size_t类型,以保证代码的可移植性和兼容性。尽管许多编译器将size_t 定义为 unsigned int,但不依赖于它与unsigned int之间的精确关系是一个好的编程实践。

strcpy()

strcpy - 将源字符串(src)复制到目标字符串(dest)中,包括字符串的结束符\0。语法:

char *strcpy(char *dest, const char *src);

示例:

#include <stdio.h>
#include <string.h> int main() {
char source[] = "Hello, world!";
char destination[20];
strcpy(destination, source);
// Destination: Hello, world!
printf("Destination: %s\n", destination); return 0;
}

比如destination之前有字符串,而且比source要长,你说最后输出什么?

char source[] = "Hello, world!";
char destination[20] = "world, Hello!XXXXXXX";
strcpy(destination, source);

输出不变。source 拷贝的时候会将结束符\0也复制到目标,destination 最后是Hello, world!\0XXXXXX。如果不需要拷贝结束符,可以使用 strncpy()

strncpy()

strncpy - 将源字符串的指定长度复制到目标字符串中。比如不拷贝源字符的结束符:

示例:

#include <stdio.h>
#include <string.h> int main() {
char source[] = "Hello, world!";
char destination[20] = "world, Hello!XXXXXXX"; // 将源字符串的指定长度复制到目标字符串中,不要source的结束符
strncpy(destination, source, sizeof(source)-1);
// Destination: Hello, world!XXXXXXX
printf("Destination: %s\n", destination); return 0;
}

最后输出:Destination: Hello, world!XXXXXXX

strcat()

strcat - 将源字符串(src)连接到目标字符串(dest)的末尾,形成一个新的字符串。语法:

char *strcat(char *dest, const char *src);

示例:

#include <stdio.h>
#include <string.h> int main() {
char destination[20] = "Hello";
char source[] = ", world!"; strcat(destination, source);
// Destination: Hello, world!
printf("Destination: %s\n", destination); return 0;
}
strncat()

strncat - 将源字符串连接到目标字符串的末尾,并限制连接的字符数量。

示例:

#include <stdio.h>
#include <string.h> int main() {
char destination[20] = "Hello";
char source[] = ", world!"; strncat(destination, source, 3);
// Destination: Hello, w
printf("Destination: %s\n", destination); return 0;
}

strcmp()

strcmp - 用于比较两个字符串的大小。

  • 字符串的比较是按照字典顺序进行的,根据每个字符的 ASCII 值进行比较。
  • 比如 apple 和 applea比较,第一次是 a 等于 a,继续比较,直到第六次 \0 和 a 比较,\0 的 ASCII 是0,而a 是97(可打印字符的ASCII值通常位于32-126之间),所以 applea 更大
  • 大小写敏感。例如 A 的 ASCII 是 65。

例如:比较字符串 str1 和字符串 str2 的大小

  • 如果 str1 小于 str2,则返回一个负整数(通常是 -1)。
  • 如果 str1 大于 str2,则返回一个正整数(通常是 1)。
  • 如果 str1 等于 str2,则返回 0。

示例:

#include <stdio.h>
#include <string.h> int main() {
char str1[] = "apple";
char str2[] = "applea"; int result = strcmp(str1, str2); if (result < 0) {
printf("str1 is less than str2\n");
} else if (result > 0) {
printf("str1 is greater than str2\n");
} else {
printf("str1 is equal to str2\n");
} return 0;
}

输出:str1 is less than str2

strncmp()

strncmp - 比较两个字符串的前n个字符是否相等

示例:

#include <stdio.h>
#include <string.h> int main() {
char str1[] = "apple";
char str2[] = "applea"; // int result = strcmp(str1, str2);
int result = strncmp(str1, str2, strlen(str1)); if (result < 0) {
printf("str1 is less than str2\n");
} else if (result > 0) {
printf("str1 is greater than str2\n");
} else {
printf("str1 is equal to str2\n");
} return 0;
}

输出:str1 is equal to str2

strchr()

strchr - 在一个字符串中查找指定字符第一次出现的位置。语法:

// str是要搜索的字符串;
// c是要查找的字符。
char* strchr(const char* str, int c);

函数返回值:

  • 如果找到指定字符,则返回该字符在字符串中的地址(指针)。
  • 如果未找到指定字符,则返回NULL。

Tip:可以通过地址相减返回字符在字符串中出现的索引值。请看示例:

#include <stdio.h>
#include <string.h> int main() {
char str[] = "Hello, world!";
char* result = strchr(str, 'o'); if (result != NULL) {
// 返回字符'o'的位置,并输出字符 'o' 到结尾的字符串
printf("找到了字符'o',位置为:%s\n", result);
// 地址相减,返回字符第一次出现的索引
printf("找到了字符'o',位置为:%ld\n", result - str);
}
else {
printf("未找到字符'o'\n");
} return 0;
}

输出:

开始运行...

找到了字符'o',位置为:o, world!
找到了字符'o',位置为:4 运行结束。
strrchr

strrchr - 相对strchr()逆序查找。

修改上述示例(strchr)一行代码:

- char* result = strchr(str, 'o');
+ char* result = strrchr(str, 'o');

运行:

开始运行...

找到了字符'o',位置为:orld!
找到了字符'o',位置为:8 运行结束。

strstr

strstr - 用于在一个字符串中查找指定子字符串的第一次出现位置。语法:

char* strstr(const char* str1, const char* str2);

示例:

#include <stdio.h>
#include <string.h> int main() {
char str[] = "Hello, World!";
char *subStr = "World";
char *result = strstr(str, subStr); if (result != NULL) {
printf("找到子字符串:%s\n", result);
} else {
printf("未找到子字符串。\n");
} return 0;
}

关键字

C 语言有如下关键字:

关键字 描述 关键字 描述
auto 声明自动变量 enum 声明枚举类型
break 跳出当前循环或开关语句 extern 声明外部变量或函数
case 开关语句分支标签 float 声明单精度浮点型变量或函数返回值类型
char 声明字符型变量或函数返回值类型 for 循环语句
const 声明只读变量 goto 无条件跳转语句
continue 结束当前循环的迭代,并开始下一次迭代 if 条件语句
default 开关语句的默认分支 int 声明整型变量或函数返回值类型
do 循环语句的循环体 long 声明长整型变量或函数返回值类型
double 声明双精度浮点型变量或函数返回值类型 register 声明寄存器变量
else 条件语句中否定条件的执行体 return 从函数返回值
short 声明短整型变量或函数返回值类型 signed 声明有符号数类型
sizeof 获取某个数据类型或变量的大小 static 声明静态变量
struct 声明结构体类型 switch 开关语句
typedef 声明类型别名 union 声明联合体类型
unsigned 声明无符号数类型 void 声明无类型
volatile 说明变量可以被意外修改,应立即从内存中读取或写入而不进行优化 while 循环语句

重点说一下:static、const(已讲过)、extern。

auto

在C语言中,auto 关键字并不是必需的,因为所有在函数内部声明的变量默认都是自动变量。所以在实际编码中,很少使用 auto 关键字进行变量声明

#include <stdio.h>

int main() {
auto int x = 10; // 等于 int x = 10; printf("The value of x is: %d\n", x); // 输出:The value of x is: 10 return 0;
}

在现代的C语言标准中(C99及以上),auto 关键字的使用已经不常见,并且很少被推荐使用,因为它已经成为了默认行为

register

比如 for(int i = 0; i< 1000; i++),每次i都会自增1,如果编译器没有任何优化,有可能会导致寄存器内存之间的数据交互发生一千次。如果将 i 声明成寄存器变量(register int count;),可能就无需交互这么多次。但这也只是给编译器提供一个建议,指示它将变量存储在寄存器中。实际上,编译器可以选择忽略这个建议,根据自身的优化策略和限制来决定是否将变量存储在寄存器中。

Tip: 寄存器是不能被直接取地址。C 语言标准已经从 C99 开始将 register 关键字标记为过时(deprecated)

extern

extern - 用于声明变量或函数的外部链接性。

通俗点说,比如我在b.c中定义了一个方法,在另一个文件中想使用,无需重复编写,通过 extern 声明后可直接使用。编译时需要将多个文件一起编译成可执行文件。

定义b.c和main.c两个文件:

  • b.c 中通过 extern int number 声明 number 变量在外部已定义,不会分配内存空间
  • main.c 先声明 show() 函数已在外部定义,然后使用
  • 最后通过 gcc 将这两个文件编译成 main 可执行函数

完整代码如下:

b.c:

#include <stdio.h>
extern int number; // 声明外部变量 number 已存在。不会分配内存空间 void show() {
printf("x = %d\n", number); // 使用外部变量 number
}

main.c:

#include <stdio.h>

extern void show();  // 声明函数 show 的存在

// 全局变量
int number = 101;
int main() {
show(); // 调用 b.c 中的函数,打印外部变量 number
return 0;
}

两两个文件一起编译,运行输出 x = 101

pjl@pjl-pc:~/$ gcc main.c b.c -o main && ./main
x = 101

static

static 有3个作用:

  • static int number = 101;, 指明 number 只能在本文件中使用,其他文件即使使用了 extern 也不能访问
  • static void show(), 指明 show 只能在本文件中使用,其他文件即使使用了 extern 也不能访问
  • static 修饰局部变量,会改变变量的声明周期,直到程序结束才释放

通过三个示例一一说明。

:在 extern 示例基础上进行。

示例1:修改 main.c:

- int number = 101;
+ static int number = 101;

编译报错如下:

pjl@pjl-pc:~/$ gcc main.c b.c -o main
/usr/bin/ld: /tmp/ccEOKXoI.o: in function `show':
b.c:(.text+0xa): undefined reference to `number'
collect2: error: ld returned 1 exit status

示例2:修改 b.c:

- void show() {
+ static void show() {

编译报错:

pjl@pjl-pc:~/$ gcc main.c b.c -o main
/usr/bin/ld: /tmp/cc8XfhVS.o: in function `main':
main.c:(.text+0xe): undefined reference to `show'
collect2: error: ld returned 1 exit status

示例3:请问下面这段代码输出什么?

#include <stdio.h>
void show(); void fn1(){
int i = 1;
printf("%d\n", ++i);
} int main() {
for(int i = 0; i < 3; i++){
fn1();
}
return 0;
}

输出三个2。因为 fn1() 每次执行完,存放在栈中的变量 i 就被释放。

如果给 i 增加 static 会输出什么:

- int i = 1;
+ static int i = 1;

输出2 3 4

Tip:在C语言中,全局变量静态变量都属于静态存储类别,默认情况下会被分配到静态数据区。静态数据区在程序启动时被分配,在程序结束时释放。

练习

Tip:以下4个都是字符串相关的编程练习

练习1

题目:查找字符数组中字符的位置,例如 hello e,输出1。

实现:

#include <stdio.h>
#include <string.h> int findIndex(char array[], char target) {
int length = strlen(array);
for (int i = 0; i < length; i++) {
if (array[i] == target) {
return i;
}
}
return -1; // 字符不在数组中
} int main() {
char array[] = "hello";
char target = 'e';
int index = findIndex(array, target); if (index != -1) {
printf("字符 %c 的位置是:%d\n", target, index);
} else {
printf("字符 %c 不在数组中\n", target);
} return 0;
}

输出:字符 e 的位置是:1

练习2

题目:查找字符数组中字符的位置,例如 hello ll,输出2。

实现:

#include <stdio.h>
#include <string.h> int findIndex(char array[], char substring[]) {
char *result = strstr(array, substring);
if (result != NULL) {
return result - array;
}
return -1; // 字符串不在数组中
} int main() {
char array[] = "hello";
char substring[] = "ll";
int index = findIndex(array, substring); if (index != -1) {
printf("字符串 \"%s\" 的位置是:%d\n", substring, index);
} else {
printf("字符串 \"%s\" 不在数组中\n", substring);
} return 0;
}

输出:字符串 "ll" 的位置是:2

练习3

题目:在字符串指定位置插入字符串

实现

#include <stdio.h>
#include <string.h> void insertString(char str[], int pos, const char insert_str[]) {
int len1 = strlen(str);
int len2 = strlen(insert_str); if (pos < 0 || pos > len1)
return; // 无效的插入位置 // 创建临时数组,用于存储插入后的新字符串
char temp[len1 + len2 + 1]; // 将原字符串中插入位置之前的部分复制到临时数组
strncpy(temp, str, pos); // 将要插入的字符串复制到临时数组的合适位置
strcpy(&temp[pos], insert_str); // 追加原字符串中插入位置之后的部分
strcat(temp, &str[pos]); // 将新字符串复制回原字符串
strcpy(str, temp);
} int main() {
char original_str[100] = "Hello, world!";
int pos = 7;
char insert_str[100] = "beautiful "; insertString(original_str, pos, insert_str);
printf("%s\n", original_str); return 0;
}

输出:Hello, beautiful world!

练习4

题目:计算字符串中子串的次数,例如 "helloworldhelloworldhelloworld hello" 中 hello 出现4次

#include <stdio.h>
#include <string.h> int countSubstringOccurrences(const char* str, const char* substring) {
int count = 0;
int substring_length = strlen(substring);
const char* ptr = strstr(str, substring); while (ptr != NULL) {
count++;
ptr += substring_length;
ptr = strstr(ptr, substring);
} return count;
} int main() {
const char* str = "helloworldhelloworldhelloworld hello";
const char* substring = "hello"; int count = countSubstringOccurrences(str, substring);
// 4
printf("%d\n", count); return 0;
}

前端学习C语言 - 函数和关键字的更多相关文章

  1. iOS学习05C语言函数

    本次主要是学习和理解函数,函数树状图如下: 1.函数的声明和定义 函数定义的四要素分别为: 返回值类型 :函数的结果值类型,函数不能返回数组. 指定返回类型是void类型说明函数没有返回值. 函数名 ...

  2. iOS学习09C语言函数指针

    本次主要学习和理解函数指针 1.函数指针 void printValue(int number) { printf("number = %d\n", number); } int ...

  3. 前端学习之回调函数、call方法、apply方法

    今天学习的内容比较少,大部分时间是自己在写qq音乐和京东移动端的页面.现在说说今天学到的内容: 首先,回调函数,就是在函数内部中调用另外一个函数, 将一个函数当作参数传给另一个函数,被传的函数叫做回调 ...

  4. 菜鸟学习-C语言函数参数传递详解-结构体与数组 分类: C/C++ Nginx 2015-07-14 10:24 89人阅读 评论(0) 收藏

    C语言中结构体作为函数参数,有两种方式:传值和传址. 1.传值时结构体参数会被拷贝一份,在函数体内修改结构体参数成员的值实际上是修改调用参数的一个临时拷贝的成员的值,这不会影响到调用参数.在这种情况下 ...

  5. 【前端学习笔记】函数定义、函数调用、this

    函数定义的三种方式与特点: 1.函数声明:(特点:会被前置:重复定义函数时,最后一次定义有效.) 2.函数表达式: 3.函数实例化:(特点:只能访问本地作用域与全局作用域!!!) /* 对象实例化定义 ...

  6. C语言函数申明关键字inline

    内联inline是给编译器的优化提示,如果一个函数被编译成inline的话,那么就会把函数里面的代码直接插入到调用这个函数的地方,而不是用调用函数的形式.如果函数体代码很短的话,这样会比较有效率,因为 ...

  7. C语言函数声明什么时候可以省略,什么时候不能省?

    在学习C语言函数的时候,老师总会告诉我们函数要写声明,然后再定义.这是个稳健的做法.等我自己学习了其他高级语言以后,回头再来写C,突然就觉得函数要写声明有点麻烦.无意间发现有一次函数没写声明居然编译( ...

  8. 大前端学习笔记整理【五】关于JavaScript中的关键字——this

    写在前面 工作有那么一段时间了,但是在工作中,发现自己的理论知识还是有所欠缺.特别是在javascript上,很多东西其实自己属于知道要用这个,但是不知道为什么要这么用...这种情况很是尴尬了,所以写 ...

  9. 前端学习 第三弹: JavaScript语言的特性与发展

    前端学习 第三弹: JavaScript语言的特性与发展 javascript的缺点 1.没有命名空间,没有多文件的规范,同名函数相互覆盖 导致js的模块化很差 2.标准库很小 3.null和unde ...

  10. 【C语言学习笔记】C语言函数执行成功时,返回1和返回0,究竟哪个好?

    基本上,没有人会将大段的C语言代码全部塞入 main() 函数,更好的做法是按照复用率高,耦合性低的原则,尽可能的将代码拆分不同的功能模块,并封装成函数.C语言代码的组合千变万化,因此函数的功能可能会 ...

随机推荐

  1. 为什么 Python、Go 和 Rust 都不支持三元运算符?

    在编程时,我们经常要作条件判断,并根据条件的结果选择执行不同的语句块.在许多编程语言中,最常见的写法是三元运算符,但是,Python 并不支持三元运算符,无独有偶,两个最热门的新兴语言 Go 和 Ru ...

  2. CentOS 9 开局配置

    CentOS 9 开局配置 CentOS 9 发布有几年了,一直没有尝试使用,CentOS 9 有一些变动. 查看系统基础信息 # 查看系统基础信息 [root@chenby ~]# neofetch ...

  3. 二进制安装Kubernetes(k8s) v1.24.1 IPv4/IPv6双栈

    二进制安装Kubernetes(k8s) v1.24.1 IPv4/IPv6双栈 Kubernetes 开源不易,帮忙点个star,谢谢了 介绍 kubernetes二进制安装 后续尽可能第一时间更新 ...

  4. Semantic Kernel 知多少 | 开启面向AI编程新篇章

    引言 在ChatGPT 火热的当下, 即使没有上手亲自体验,想必也对ChatGPT的强大略有耳闻.当一些人在对ChatGPT犹犹豫豫之时,一些敏锐的企业主和开发者们已经急不可耐的开展基于ChatGPT ...

  5. 从内核源码看 slab 内存池的创建初始化流程

    在上篇文章 <细节拉满,80 张图带你一步一步推演 slab 内存池的设计与实现 >中,笔者从 slab cache 的总体架构演进角度以及 slab cache 的运行原理角度为大家勾勒 ...

  6. Nuget 多项目复合打包

    问题描述 我这里有个进程间通信组件,用于提供多应用间通信的解决方案. 进程间通信,分为客户端和服务端,俩端肯定会有些共性代码,所以加了个H3C.Channel.Core项目 因为之前还不太了解nuge ...

  7. 2023-05-02:如果一个正整数每一个数位都是 互不相同 的,我们称它是 特殊整数 。 给你一个正整数 n ,请你返回区间 [1, n] 之间特殊整数的数目。 输入:n = 20。 输出:19。

    2023-05-02:如果一个正整数每一个数位都是 互不相同 的,我们称它是 特殊整数 . 给你一个正整数 n ,请你返回区间 [1, n] 之间特殊整数的数目. 输入:n = 20. 输出:19. ...

  8. Node + Express 后台开发 —— 登录标识

    登录标识 系统通常只有登录成功后才能访问,而 http 是无状态的.倘若直接请求需要登录才可访问的接口,假如后端反复查询数据库,而且每个请求还得带上用户名和密码,这都是不很好. 作为前端,我们听过 c ...

  9. 【Linux】sed文本处理及软件管理

    软件管理 1.编译安装http2.4,实现可以正常访问 安装编译相关工具包 root@mirror-centos8-p11 ~]# yum install gcc make autoconf apr- ...

  10. pytest测试实战和练习

    开头 经过前面几章的学习,这时候要来个测试实战会比较好巩固一下学过的知识 任务要求 1.实现计算器(加法,除法)的测试用例 2.使用数据驱动完成测试用例的自动生成 3.在调用测试方法之前打印[开始计算 ...