前端学习C语言 - 初级指针
初级指针
本篇主要介绍:指针和变量的关系、指针类型、指针的运算符、空指针和野指针、指针和数组、指针和字符串、const 和指针、以及gdb 调试段错误。
基础概念
指针是一种特殊的变量。存放地址的变量就是指针。
int num = 1; 会申请4个字节的内存来存放数字1,每次访问 num 就是访问这4个字节。
访问内存中的这4个字节,不仅可以通过名称(例如 num),还可以通过地址。
Tip:& 不仅是位运算符,还是取地址操作符。例如 int* ptr = #,就是取变量 num 的地址并将其保存到指针变量 ptr 中
请看示例:
#include <stdio.h>
int main() {
int num = 10;
// num 的地址:0x7fff4dbf01d8
printf("num 的地址:%p\n", &num);
// num 的地址加1 :0x7fff4dbf01dc。
printf("num 的地址加1 :%p\n", &num + 1);
// j 存放连续内存的第一个字节地址
int *j = #
// 10。通过地址访问
printf("%d", *j);
return 0;
}
&num 和 &num + 1 相差4个字节,说明 &num 表示整数。
普通变量存放值,而指针用于存放地址。
通过 int *j = &num 将变量num的首地址给到指针 j(j的类型是 int *),最后通过地址(*j) 访问整数1。
int *j 是一个int类型的指针,还有 char、float等指针类型。指针类型必须匹配,比如将 j 的指针类型换成 char,则会警告。就像这样:
- int *j = #
+ char *j = #
运行:
/workspace/CProject-test/main.c:12:11: warning: incompatible pointer types initializing 'char *' with an expression of type 'int *' [-Wincompatible-pointer-types]
char *j = #
^ ~~~~
1 warning generated.
num 的地址:0x7ffddcfe5328
num 的地址加1 :0x7ffddcfe532c
10
Tip: 指针 j 也有地址,也就是指针的指针。现在不研究
练习
题目:请问输出什么?
#include <stdio.h>
int main() {
int num = 10;
int *p = #
printf("用指针访问数据 num :%d\n", *p);
*p = 11;
printf("用过指针修改 num 数据:%d\n", num);
return 0;
}
提示:数据可以通过变量访问,也能使用地址(指针)访问。就像通知同学去嵌入式实验室上课,或者是 303 上课。其中*p = 11; 等价于 num = 11;
输出:
用指针访问数据 num :10
用过指针修改 num 数据:11
星号的作用
指针 * 有两个主要作用(根据* 前面有无类型做区分):
- 指针类型声明
- 取值(又称解引用操作符)。例如,*ptr 表示获取指针变量 ptr 所指向内存地址上的值。
请看示例:
#include <stdio.h>
int main() {
int num = 10;
// 指针类型声明
int *p = #
// 取值
printf("%d\n", *p); // 10
// 取值
*p = 11;
printf("%d\n", num); // 11
return 0;
}
指针类型
所占字节
在32位系统上,指针通常占用4个字节;而在64位系统上,指针通常占用8个字节。请看示例:
#include <stdio.h>
int main() {
printf("char类型指针所占字节数为:%zu\n", sizeof(char*));
printf("short类型指针所占字节数为:%zu\n", sizeof(short*));
printf("int类型指针所占字节数为:%zu\n", sizeof(int*));
printf("long类型指针所占字节数为:%zu\n", sizeof(long*));
printf("float类型指针所占字节数为:%zu\n", sizeof(float*));
printf("double类型指针所占字节数为:%zu\n", sizeof(double*));
printf("long long类型指针所占字节数为:%zu\n", sizeof(long long*));
return 0;
}
输出:
char类型指针所占字节数为:8
short类型指针所占字节数为:8
int类型指针所占字节数为:8
long类型指针所占字节数为:8
float类型指针所占字节数为:8
double类型指针所占字节数为:8
long long类型指针所占字节数为:8
练习
题目:请问整数类型的指针和字符类型的指针加1分别是几个字节?
#include <stdio.h>
int main() {
int num = 10;
printf("num 的地址:%p\n", &num);
printf("num 的地址加1 :%p\n", &num + 1);
char ch = 'a';
printf("ch 的地址:%p\n", &ch);
printf("ch 的地址加1 :%p\n", &ch + 1);
return 0;
}
输出:
num 的地址:0x7fffe8244288
num 的地址加1 :0x7fffe824428c
ch 的地址:0x7fffe8244287
ch 的地址加1 :0x7fffe8244288
答案:int * 加1是4个字节;char * 加1是1个字节。&num 和 &ch 分别代表该变量的全部字节。
指针交换数据
比如这段代码是不能实现 a、b 两数交换。请看示例:
#include <stdio.h>
void swap(x, y){
int tmp = x;
x = y;
y = tmp;
}
int main() {
int a = 1;
int b = 2;
swap(a, b);
printf("a:%d\n", a);
printf("b:%d\n", b);
return 0;
}
a:1
b:2
分析:调用 swap(a, b) 这里是一个值传递,找到函数入口地址,对参数 x、y 申请空间和赋值,通过 tmp 变量完成了 x和y的交换,最后回收局部变量 x、y和tmp,释放空间。而 a,b数据没有变化。
可以通过指针来实现两数的交换。请看示例:
#include <stdio.h>
void swap(int* x, int* y){
int tmp = *x;
*x = *y;
*y = tmp;
}
int main() {
int a = 1;
int b = 2;
swap(&a, &b);
printf("a:%d\n", a);
printf("b:%d\n", b);
return 0;
}
a:2
b:1
分析:通过 swap(&a, &b) 将 a b 的地址传给 x 和 y,通过 x 和 y 指针对 a 和 b 进行交换,虽然最后会销毁swap中的局部变量,但 a 和 b的值已经完成了交换。
指针的运算符
指针和变量的关系
练习1
题目:输出什么?
#include <stdio.h>
int main() {
int a = 10, *pa = &a, *pb;
printf("%d\n", *pa);
pb = pa;
printf("%d\n", *pb);
return 0;
}
输出:10 10
分析:
int a = 10,
// pa 指向变量 a
*pa = &a,
// 定义一个整数型的指针 pb
*pb;
printf("%d\n", *pa);
// pb 也指向变量 a
pb = pa;
printf("%d\n", *pb);
return 0;
练习2
题目:输出什么?
#include <stdio.h>
int main() {
int x = 3, y = 0, *px = &x;
y = *px + 5;
printf("%d\n", y);
y= ++*px;
printf("%d\n", y);
printf("%p\n", px);
y = *px++;
printf("%p\n", px);
printf("%d\n", y);
return 0;
}
输出:
8
4
0x7ffc48b9be38
0x7ffc48b9be3c
4
分析:
y= ++*px;等效++(*px)。如果是 ++* 是不对的
类似 y = ++i,等于先执行 ++,在执行 y = i,
这里先对 (*px) 执行 ++,在返回 *px 的值
y = *px++;
先执行 y = *px,然后是 px++。px是整数类型的地址,加1就是加4个字节。
练习3
题目:输出什么?
#include <stdio.h>
int main() {
int x = 3, y = 0, *px = &x;
printf("%p\n", px);
y = (*px)++;
printf("%p\n", px);
printf("%d\n", x);
return 0;
}
输出:
0x7ffef1dc4d58
0x7ffef1dc4d58
4
分析:*px++ 表示指针加1,(*px)++ 表示值加1。
指针初始化
指针初始化有两种方法:已经存在的空间和自己申请空间。
已经存在的空间,例如:
#include <stdio.h>
#include <stdlib.h>
int main() {
int num;
int* p = #
*p = 10;
char *str = "abc";
printf("%s\n", str); // abc。把字符串的地址赋值给指针变量
return 0;
}
自己申请空间可以使用 malloc 函数。申请的是 void 类型指针,也称为通用类型指针。请看示例:
#include <stdio.h>
// malloc 需要引入 <stdlib.h>
#include <stdlib.h>
int main() {
// 申请16个字节
int* q = malloc(sizeof(int) * 4); // 在堆里申请了16个字节
// int* q = (int *)malloc(sizeof(int) * 4); // 推荐
*q = 10;
// 释放申请的16个字节
free(q);
return 0;
}
申请空间,使用完需要使用 free() 释放。
Tip:根据 C99 标准以及更高版本的标准,显式的类型转换是建议的做法,以确保类型的安全性和可读性。
空指针和野指针
下面这段代码 p 就是一个野指针,运行报错:段错误 (核心已转储):
#include <stdio.h>
int main() {
int* p;
*p = 1;
return 0;
}
这里声明一个指针 p,里面是一个随机数,例如 0x7ffe71df3f40,接着往指向的内存放1,由于这块内存不知道是否存在,即使存在也不能访问,于是报段错误。
直接手写一个地址也不可以。就像这样:
#include <stdio.h>
int main() {
// warning: incompatible integer to pointer conversion initializing 'int *' with an expression of type 'long' [-Wint-conversion]
// 这个警告是因为你正在将一个 long 类型的表达式赋值给一个 int* 类型的指针变量,导致类型不匹配。
// int* p = 0x7ffe71df3f40;
int* p = (int *)0x7ffe71df3f40;
*p = 100;
return 0;
}
// 分段错误 (核心已转储)"
Segmentation fault (core dumped)
空指针也不能使用:
int* p = NULL;
*p = 100;
// 输出:`Segmentation fault (core dumped)`
但空指针会让你可控。就像这样:
int* p = NULL;
if (p != NULL) {
printf("p is not NULL\n");
}else{
printf("p is NULL\n");
}
// 输出:p is NULL
指针和数组
指针当数组用
遍历一个数组,可以这样:
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
int length = sizeof(arr) / sizeof(arr[0]); // 计算数组的长度
// 1 2 3 4 5
for (int i = 0; i < length; ++i) {
printf("%d ", arr[i]);
}
return 0;
}
使用指针遍历数组有两种方式(效果相同)。请看示例:
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
int length = sizeof(arr) / sizeof(arr[0]); // 计算数组的长度
// 指针遍历方式1
/*
int* pArr = arr;
for (int i = 0; i < length; ++i) {
printf("%d ", *(pArr + i));
}
*/
// 指针遍历方式2
int* pArr = arr;
for (int i = 0; i < length; ++i) {
printf("%d ", pArr[i]);
}
return 0;
}
Tip:在数组一文中我们知道数组名表示首元素地址,这里*(pArr + i)会依次遍历数组或许是因为指针是int类型吧!
总结:pArr[i] 等于 *(pArr + i)。在这里[]不再是取某个索引,而是表示取值。
指针和字符数组
题目:分析 char a[] = "Hello"; 和 char *b = "World";
- 都可以用for遍历元素。例如:
#include <stdio.h>
int main() {
char a[] = "Hello";
char *b = "World";
// Iterating over 'a'
printf("Characters in 'a':\n");
for (int i = 0; a[i] != '\0'; i++) {
printf("%c\n", a[i]);
}
// Iterating over 'b'
printf("\nCharacters in 'b':\n");
for (int i = 0; b[i] != '\0'; i++) {
printf("%c\n", b[i]);
}
return 0;
}
输出:
开始运行...
Characters in 'a':
H
e
l
l
o
Characters in 'b':
W
o
r
l
d
运行结束。
- 为什么指针也可以通过索引访问特定字符?
比如char *b = "World";,可以将字符串视为字符数组,使用指针来指向该数组的首地址,指针可以通过偏移来访问特定位置的元素,包括字符串中的字符。
练习
题目:下面代码中 p1[0]、p2[0]、p3[0]的值分别是多少?
// 申请4*4个字节,每个字节地址假如是:0x100(存放1) 0x104(存放2) 0x108 0x10c
int a[] = {1,2,3,4};
int *p1 = (int*)(&a + 1);
int *p2 = (int*)((int)a + 1);
int *p3 = (int*)(a + 1);
分析:
(int*)(&a + 1)- &a 表示整个数组,加1则到下一个数组,然后将数组指针强转成整数指针,指向第5个元素,其实已经越界了。(int*)((int)a + 1)- a 表示数组首元素地址,(int)a 将地址转为整数,以前是加1个元素,现在就是加1,然后又将整数转为整数指针,乱了(就好比访问0x101 0x102 0x103 0x104)(int*)(a + 1)- a 表示数组首元素地址,加1则是第二个元素地址 0x104,不强转也可以。
结论:只有p3[0](等价于 *(p3 + 0))是一个正常的元素,也就是2.
指针和字符串
题目:用数组和指针定义字符串有什么区别?
#include <stdio.h>
int main() {
char str[] = "HelloWorld";
// HelloWorld
printf("%s\n", str);
char* s = "HelloWorld";
// HelloWorld
printf("%s\n", s);
return 0;
}
Tip: 字符串的输出都是首地址,比如这里的 str 是数组的首地址,s 指针指向的也是首地址。
分析:
char str[] = "HelloWorld"; 在栈中定义一个数组,用11个字节存储HelloWorld(还有一个 \0)。请看示例:
#include <stdio.h>
int main() {
char str[] = "HelloWorld";
str[0]++;
// IelloWorld
printf("%s\n", str);
// error: cannot increment value of type 'char[11]'
str++;
// printf("%s\n", str);
return 0;
}
数组名(str++)不可以修改,str 就是数组首元素地址,已经固定了,可认为它是常量。但数组内容可以修改。
char* s = "helloWorld"; 将 helloWorld 放在只读数据区,s 是局部变量,放在栈中,占8个字节。请看示例:
#include <stdio.h>
int main() {
char* s = "helloWorld";
s++;
// elloWorld
printf("%s\n", s);
// 报错:Segmentation fault (core dumped)
s[0]++;
return 0;
}
指针可以加加,但指针指向的内容不能修改。
str 只是个名字,不占空间,如果一定要说占多少,那就是它执行的数组占11个字节。而 s 是8个字节,指向一个只读区,占 11 个字节。
练习
题目:分析以下示例。
#include <stdio.h>
int main() {
char str[20];
str = "HelloWorld";
char* s;
s = "HelloWorld";
// HelloWorld
printf("%s\n", s);
return 0;
}
分析:
// 分配20个字节的内存,并把首地址给 str
char str[20];
// str 是只读的,不能再赋值。报错:`error: array type 'char[20]' is not assignable`
str = "HelloWorld";
// 定义一个指针 s
char* s;
// 将 HelloWorld 的首地址给 s
s = "HelloWorld";
扩展
自定义strcpy()函数
题目:实现原生字符串拷贝方法strcpy。strcpy 其用法如下:
#include <stdio.h>
#include <string.h>
int main() {
char source[] = "Hello";
char destination[10]; // 目标字符串需要足够的空间来容纳 source 字符串
strcpy(destination, source);
printf("Source string: %s\n", source);
printf("Destination string: %s\n", destination);
return 0;
}
实现:
#include <stdio.h>
char* strcpy_custom(char* destination, const char* source) {
// 字符串数组末尾有一个特殊的空字符 '\0' 来表示字符串的结束。逐个复制字符,直到遇到源字符串的结束标志 '\0'
while (*source != '\0') {
*destination = *source;
destination++;
source++;
}
*destination = '\0'; // 在目标字符串末尾添加结束标志 '\0'
return destination;
}
int main() {
// 定义两个字符数组
char source[] = "Hello";
char destination[10]; // 目标字符串需要足够的空间来容纳 source 字符串
// 数组名。表示首元素的地址,加 1 是加一个元素(比如这里1个字节)
strcpy_custom(destination, source);
printf("Source string: %s\n", source);
printf("Destination string: %s\n", destination);
return 0;
}
Tip:const char* source 中 const 的作用请看const 和指针
输出:
开始运行...
Source string: Hello
Destination string: Hello
运行结束。
将 while 替换成下面一行代码效果也相同:
char* strcpy_custom(char* destination, const char* source) {
/*
while (*source != '\0') {
*destination = *source;
destination++;
source++;
}
*destination = '\0';
*/
// 替换成
while((*destination++ = *source++) != '\0');
return destination;
}
分析:(*destination++ = *source++) != '\0':
之前的是首先判断,在赋值。`*source != '\0'`、`*destination = '\0';`,这里是先赋值
后置++会放在表达式最后,所以等于:
(*destination = *source) != '\0';
destination++;
source++;
const 和指针
首先补充下(int*)的作用。之前说到 const 定义的变量可以被修改,我们写了如下代码:
#include <stdio.h>
int main() {
const int val =5;
int *ptr= (int*)&val;
*ptr=10;
printf("val = %d\n",val);
printf("*ptr = %d\n", *ptr);
return 0;
}
其中 int *ptr= (int*)&val; 是将一个 const int 类型的变量 val 地址强制转换为 int* 类型的指针,并将指针存储在 ptr 中。这种类型转换是不安全的,因为它丢失了 val 的常量性质。
const char* source 声明一个常量指针,以下代码仅做示意:
#include <stdio.h>
int main() {
const char* source = "Hello";
char* mutableSource = "World";
printf("%c\n", source[0]);
printf("%c\n", mutableSource[0]);
// 以下操作是非法的,会导致编译错误
// source[0] = 'h'; // 不能修改字符数据
// 合法
// 尽管mutableSource是一个非常量指针,看起来可以进行修改,但修改字符串常量是不被允许的,并且这可能导致未定义行为。
mutableSource[4] = 'w'; // 可以修改字符数据
return 0;
}
运行:
开始运行...
H
W
Segmentation fault (core dumped)
运行结束。
就近原则
const 有个就近原则:
- 比如:
const int* p1 = #,const 修饰的是 *,所以 *p1 不能修改, p1 可以修改 - 比如:
int* const p2 = #,const 修饰 p2,所以 p2 不能修改,*p2 可以修改
请看示例:
#include <stdio.h>
int main() {
int num = 1;
const int* p1 = # // const 修饰的是 *,所以 *p1 不能修改, p1 可以修改
p1++;
// (*p1)++;
int* const p2 = # // const 修饰 p2,所以 p2 不能修改,*p2 可以修改
// p2++;
(*p2)++;
const int* const p3 = # // 两个都不能修改
// p3++;
// (*p3)++;
return 0;
}
gdb 调试段错误
GDB(GNU Debugger)是一款强大的调试器,用于帮助开发者查找和解决程序中的错误。通过与源代码交互,并提供诸如断点设置、变量观察、内存检查等功能,GDB允许开发者逐行执行程序并分析其运行状态。
除了上文使用的 run,还有如下操作
- run:运行程序。
- break
<line_number>:在指定行设置断点。 - break
<function_name>:在指定函数设置断点。 - continue:继续执行程序直到下一个断点或程序结束。
- next:逐过程地执行程序。
- step:逐语句地执行程序。
- print
<variable>:打印变量的值。 - backtrace:显示函数调用的堆栈跟踪信息。
- quit:退出GDB调试会话。
使用 gdb 调试段错误的过程如下:
编写代码:
pjl@pjl-pc:~/pjl$ cat demo-3.c
#include <stdio.h>
int main() {
int* p;
*p = 1;
return 0;
}
编译运行发现段错误:
pjl@pjl-pc:~/pjl$ gcc demo-3.c -o demo-3
pjl@pjl-pc:~/pjl$ ./demo-3
段错误 (核心已转储)
将代码编译为可调试的可执行文件。在gcc或g++编译时,添加"-g"选项可以生成包含调试信息的可执行文件。
// 增加 -g
pjl@pjl-pc:~/pjl$ gcc demo-3.c -o demo-3 -g
// 启动GDB并加载可执行文件
pjl@pjl-pc:~/pjl$ gdb demo-3
GNU gdb (Ubuntu 9.1-0kylin1) 9.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from demo-3...
(gdb)
输入 run(还有其他操作) 找到是第5行代码报错:
...
// run:运行程序。
(gdb) run
Starting program: /home/pjl/pjl/demo-3
Program received signal SIGSEGV, Segmentation fault.
0x0000555555555135 in main () at demo-3.c:5
5 *p = 1;
(gdb)
高级指针
提前透露:指针遇上数组
题目:以下代码输出什么?
#include <stdio.h>
int main() {
char * string[] = {"Hello", "World" };
printf("%s\n", string);
return 0;
}
分析:
我们知道定义字符串有以下两种方法:
char str[] = "HelloWorld";
char* s = "HelloWorld";
Tip: string 在 C 中不是关键字,也不是保留字,就是一个普通变量名。
[] 的优先级是非常高的,这里首先是定义一个数组(string[]),其次就是指针,合起来就是一个指针数组。
首先在只读区分配两块内存分别存放 Hello(地址比如是 0x100) 和 World(地址比如是 0x200),指针数组是16个字节,本质就是数组,只不过里面放的是指针,比如前8个字节的地址是0x1000,那么 string 就是 0x1000,因为数组名就是数组首元素地址。
所以要输出这两个字符串,可以这么写:
#include <stdio.h>
int main() {
char * string[] = {"Hello", "World" };
// Hello
printf("%s\n", string[0]);
// World
printf("%s\n", string[1]);
return 0;
}
前端学习C语言 - 初级指针的更多相关文章
- iOS学习09C语言函数指针
本次主要学习和理解函数指针 1.函数指针 void printValue(int number) { printf("number = %d\n", number); } int ...
- C语言 --- 初级指针
1.内存的访问:直接访问,间接访问. 直接访问:int a = 0;直接对a赋值.选一个内存地址,让他存20这个数. a += 10; ...
- 前端学习笔记 - Css初级篇
有话先说:我是一只菜鸟,一只都是,从前是现在也是. CSS中的会计元素与行内元素 块级元素特性:占据一整行,总是重起一行并且后面的元素也必须另起一行显示.内联元素特性:和其他内联元素显示在同一行. 可 ...
- 编程基础-c语言中指针、sizeof用法总结
1.指针 学习 C 语言的指针既简单又有趣.通过指针,可以简化一些 C 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的.所以,想要成为一名优秀的 C 程序员,学习指针是很有必要的. ...
- 前端学习 第三弹: JavaScript语言的特性与发展
前端学习 第三弹: JavaScript语言的特性与发展 javascript的缺点 1.没有命名空间,没有多文件的规范,同名函数相互覆盖 导致js的模块化很差 2.标准库很小 3.null和unde ...
- Go语言学习笔记九: 指针
Go语言学习笔记九: 指针 指针的概念是当时学C语言时了解的.Go语言的指针感觉与C语言的没啥不同. 指针定义与使用 指针变量是保存内存地址的变量.其他变量保存的是数值,而指针变量保存的是内存地址.这 ...
- 如何系统学习C 语言(中)之 指针篇
谈到指针,我们可能会想到钟表上的指针,但这里的指针不是现实生活中看得见摸得着的钟表上的指针,c 语言中的指针只存在于逻辑思维中,物理上并不存在. 同时,指针也是C 语言中最精华的部分,通过灵活地运用指 ...
- 【FE前端学习】第二阶段任务-基础
技能学习部分: 1.需要熟练掌握HTML标签以及CSS各个常用属性. 2.掌握CSS3 常用属性 3.掌握jquery的基本用法,对于JS基本逻辑语句需要熟练掌握 上文 [FE前端学习]第二阶段任务- ...
- 漫谈C语言及如何学习C语言
抄自:http://my.oschina.net/apeng/blog/137911 目录:[ - ] 为什么要学习C语言? C语言学习方法 1,参考书籍 2,动手实验环境搭建 3,网络资源 附录 一 ...
- [转贴]漫谈C语言及如何学习C语言
抄自http://my.oschina.net/apeng/blog/137911,觉得很有用,收藏它 目录:[ - ] 为什么要学习C语言? C语言学习方法 1,参考书籍 2,动手实验环境搭建 3, ...
随机推荐
- AI工具导航
.xe-comment-entry img { float: left; display: block; background: rgba(136, 136, 136, 0.15); margin-r ...
- 【Deep Learning】DDPM
DDPM 1. 大致流程 1.1 宏观流程 1.2 训练过程 1.3 推理过程 2. 对比GAN 2.1 GAN流程 2.2 相比GAN优点 训练过程更稳定,损失函数指向性更强(loss数值大小指示训 ...
- 在Kubernetes上安装Netdata的方法
介绍 Netdata可用于监视kubernetes集群并显示有关集群的信息,包括节点内存使用率.CPU.网络等,简单的说,Netdata仪表板可让您全面了解Kubernetes集群,包括在每个节点上运 ...
- [Windows]解决:windows连接远程桌面-出现身份验证错误,要求的函数不受支持( CredSSP加密数据库修正)[转载]
文由 需要在本地Windows系统电脑通过远程桌面(mstsc)另一台Windows服务器,将其内的数据拷贝过来.但却发生了这样的异常 解决方案 step1 Win+R step2 打开注册表: gp ...
- [JAVA/Maven/IDEA]解决JAR包冲突
1 前言 想必这个问题,诸多同仁都遇到过. 很不凑巧,这段时间咱也屡次撞上这问题好几次了. 因此,实在是有必要说说怎么解决好这问题了0.0 2 诊断:包冲突的异常信息特征 [类定义未发现错误] NoC ...
- 打造自己的ChatGPT:OpenAI的API接入技巧
打造自己的ChatGPT:OpenAI 的API接入技巧 2023年3月更新 OpenAI 在3月1日的时候放出了ChatGPT的接口,新的接口可以使用 GPT-3.5 模型,同时接口参数更新为了 m ...
- 性能最快的代码分析工具,Ruff 正在席卷 Python 圈!
几天前,Python 开源社区又出了一个不小的新闻:HTTPX 和 Starlette 在同一天将在用的代码分析工具(flake8.autoflake 和 isort)统一替换成了 Ruff. HTT ...
- HTTP.sys漏洞的检测和修复(附补丁包下载)
关于这个 HTTP.sys 漏洞,查了一些资料,没有一个写的比较全的,下面我来整理下. 这个漏洞主要存在Windows+IIS的环境下,任何安装了微软IIS 6.0以上的Windows Server ...
- Go For Web:一篇文章带你用 Go 搭建一个最简单的 Web 服务、了解 Golang 运行 web 的原理
前言: 本文作为解决如何通过 Golang 来编写 Web 应用这个问题的前瞻,对 Golang 中的 Web 基础部分进行一个简单的介绍.目前 Go 拥有成熟的 Http 处理包,所以我们去编写一个 ...
- 零样本文本分类应用:基于UTC的医疗意图多分类,打通数据标注-模型训练-模型调优-预测部署全流程。
零样本文本分类应用:基于UTC的医疗意图多分类,打通数据标注-模型训练-模型调优-预测部署全流程. 1.通用文本分类技术UTC介绍 本项目提供基于通用文本分类 UTC(Universal Text C ...