C语言(08)——关于指针(逐渐清晰版)

为了更好地理解本篇文章的知识内容,读者可以将以下文章作为补充知识进行阅读 :
C语言————原码 补码 反码 (超绝详细解释)-CSDN博客

C语言————二、八、十、十六进制的相互转换-CSDN博客

C语言————斐波那契数列的理解和运用-CSDN博客

目录

1. 内存和指针(地址)

1.1 内存的介绍

1.1.1内存的划分

1.2 指针和地址

2. 指针变量和地址

2.1 取地址操作符&

2.2 指针变量

2.2.1 指针变量的定义

2.2.2 指针变量的类型

2.2.3 指针变量的大小 

2.3 解引用操作符*

​2.4 指针变量类型的意义

3. 指针计算

3.1 指针+-整数

3.2 指针-指针

3.3 指针的关系运算

4.野指针

4.1 野指针的成因和解决方法

4.1.1 指针未初始化

4.1.2 指针越界访问

4.1.3 指针指向的空间释放

5. void指针和assert断言

5.1 void指针

5.2 assert断言

6. 指针的应用

6.1 strlen的模拟实现

递归实现方法

迭代法 

6.2 传值调用和传址调用

7. 指针和数组

7.1 数组名的理解

7.2 指针访问数组

7.3 数组传参

7.4 数组指针


1. 内存和指针(地址)

1.1 内存的介绍

在计算机中,有各种各样的数据,他们的存储需要在内存中划分空间,计算机中的内存空间大小是有限的。如果把数据比作水,内存就是用以承载水的容器,而我们知道在生活中容器的大小都是有限的。因此我们可以 更好地理解内存之于数据的意义。

1.1.1内存的划分

一个整型变量a= 10存储在程序中需要占据4个字节的内存空间大小,而数据的单位是多种多样的,那我们在内存中应该按照何种单位进行空间划分呢?

为了内存空间的高效管理,内存被划分为一个个的内存单元,而每个内存单元的大小为1字节。

其中,一个bit位可以存储一个二进制的0或1,一个字节可以存储8个bit位,即一个内存单元能存储8个bit位。

在内存中存储的数据需要通过CPU的处理,那么CPU又是如何读取这些数据的呢?

1.2 指针和地址

我们打个比方,当我们在入住一个酒店时,服务员会给我们对应的 房号和房卡,这样我们就能快速找到对应的房间。CPU和内存中的数据传输也是同样的道理,他们之间通过很多的地址总线进行连接,每根线只有两态,表示0或1(联想到二进制),那么通过地址总线不同的脉冲组合形成的这样一个二进制数字,就是对应数据的地址编码,即地址。

​在C语言中,我们将这样的地址起名为指针。

所以我们可以理解为:

内存单元的编号 == 地址 == 指针

2. 指针变量和地址

2.1 取地址操作符&

我们在学习scanf函数时知道,scanf函数除格式化字符串以外的参数代表的都是地址。

当我们在创建变量的时候,他会向内存申请空间,我们想知道他具体的地址编号时就需要用到操作符&,示例如下:

 如图创建的整型变量a,通过查看内存,我们知道他的地址即指针为0x00000099588FFB14-0x00000099588FFB17(x64环境下),共四个字节;但如果我们对a的地址进行打印的话(x86环境下,更加便于查看),结果又是怎样的呢?

我们会发现,他只打印了一个地址编号,这是因为一个数据进行存储时,他的内存空间都是连续的,打印的往往是最低的那个地址编号,进而根据数据的内存大小,从低往高访问对应数据。

经过多次尝试我们会发现,每一次变量的地址都是在发生变化的,这是因为在每次运行程序时,操作系统的内存分配情况存在差异,所以分配给变量的具体内存地址是不同的。

2.2 指针变量

2.2.1 指针变量的定义

那么通过取地址操作符&得到的地址我们又该将他存储在哪呢?为了方便提取这些指针的数据,C语言中用指针变量作为他的容器。如:

#include <stdio.h>
int main()
{int a = 10;int * pa = &a;//取出a的地址并存储到指针变量pa中return 0;

此时的pa就是一个指针变量,而他的类型为int *; 

指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址

 在C语言中,地址就是指针,指针就是地址。

2.2.2 指针变量的类型

由上我们知道的一种指针变量类型为int *,我们应该怎么去理解他呢?

我们单独看int * pa = &a这段语句可以知道,a为整型变量,pa存储的是a的地址。由此知道:

int 代表pa存储的指针所指向的数据a的类型(整型),* 表明pa为指针变量。

a和pa分别都在内存中划分了属于他们自己的空间。

那么字符类型的变量a,他的地址又该放在上面类型的指针变量中呢?

我们可以进一步推导如下:

int main()
{char a = '2';char* pc = &a;//字符指针pc,类型为char *return 0;
}
2.2.3 指针变量的大小 

在介绍内存中,我们知道地址的编号是由地址总线输出的信号转换得到的,32位机器假设有32根地址总线,他们产生的二进制序列作为一个地址,那么一个地址就是32个bit位,需要4个字节的存储空间,指针变量的大小就是 4个字节。

同理64位机器,假设有64根地址线,一个地址就是64个二进制位组成的二进制序列,存储起来就需要 8个字节的空间,指针变量的大小就是8个字节。

我们可以输出如下的代码进行测试:

#include <stdio.h>
//指针变量的⼤⼩取决于地址的⼤⼩
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
int main()
{printf("%zd\n", sizeof(char *));printf("%zd\n", sizeof(short *));printf("%zd\n", sizeof(int *));printf("%zd\n", sizeof(double *));return 0;
}

在x86环境下,即32位操作系统

在x64环境下,即64位操作系统 

结论:

1. 32位平台下地址是32个bit位,指针变量大小是4个字节

2. 64位平台下地址是64个bit位,指针变量大小是8个字节

注:指针变量的大小和类型是无关的,同样指针类型的变量,在相同平台下,大小都是相同的

2.3 解引用操作符*

那么对于指针变量,他们应该如何使用呢?这里我们将介绍一个关键的操作符——解引用操作符*

 他相当于是一把钥匙,指针变量是对应的地址,指针变量指向的数据相当于被存储在对应的地址,但我们无法直接操作他,因此需要通过钥匙打开这道壁垒,这样我们在不直接使用数据变量时,也能对数据进行相应的操作。示例如下:

#include <stdio.h>
int main()
{int a = 100;int* pa = &a;*pa = 0;//找到变量a,并通过*打开操作他的权限return 0;
}

 我们会发现,通过解引用,*pa就相当于变量a,我们能够对他进行重新赋值

​2.4 指针变量类型的意义

由2.1中我们知道,指针变量会存储数据空间中最小的地址编号,整型指针变量解引用时,他会向上访问四个字节的内存空间,我们思考一下,如果我们使用字符指针变量对&a进行访问,能得到正确的数据么?

int *整型指针下,我们打印读取的整型变量数据是正确的(十六进制11223344转为十进制为287454020‬)

 当我们使用char *字符指针对整型变量n进行读取时,我们发现,他仅读取了n内存空间中的一个内存单元,数据为十六进制的44,转为十进制为68。

在这里我们可以发现,不同的指针变量所访问的内存空间大小也是不一样的,因此学习指针变量的类型也是十分关键的。

注:在进行地址存储时,指针变量的类型应该和地址的类型相对应,在代码中我们可以看到(char*)&n,由于&n的类型为int *,我们用char *的指针变量接收他,两者类型不同,为了使指针变量pc能够顺利存储n的地址,我们需要对&n进行强制转换,如果不进行强制转换,编译器会发出警告。(编译器会进行隐式转换类型

结论:指针的类型决定了对指针解引⽤的时候有多大的权限(⼀次能操作几个字节)。

如: char* 的指针解引用就只能访问⼀个字节,而 int* 的指针的解引用就能访问四个字节。

3. 指针计算

3.1 指针+-整数

我们观察如下代码的运行结果:

我们可以发现,整型指针变量+1,他的地址跳过了4个字节;字符指针变量+1,他的地址跳过了1个字节。

指针+1,就是跳过1个指针指向的元素。指针可以+1,那也可以-1。

跳过一个指针的空间大小就取决于指针的类型

3.2 指针-指针

此运算的前提条件

  • 参与减法运算的两个指针必须指向同一数组中的元素(或数组最后一个元素的下一个位置)
  • 两个指针必须指向相同类型的数据(即指针变量类型一致)

运算结果

结果是一个ptrdiff_t类型(在<stddef.h>中定义的有符号整数类型),表示两个指针所指向元素之间的元素个数差,而不是字节数差。

 我们观察如下代码:

int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = arr;int* p1 = &arr[9];printf("%d\n", (int)(p1 - p));//p1 - p为ptrdiff_t类型,%d无法读取,需要强制转换return 0;
}

 指针求差他们的结果就是两个指针之间内存的元素个数,如下图,两个箭头之间的元素有1,2,3,4,5,6,7,8,9。共9个整型元素,故输出9。

3.3 指针的关系运算

我们知道内存的地址编码是从低到高依次排布的,因为指针是可以用来比较大小的。

常见的关系运算符包括:== 、!= 、< 、> 、<= 、>= 。

这些关系符构建的指针关系运算可以作为语句的判断条件

指针的关系运算常用于数组遍历内存区间判断。

int arr[5] = {1,2,3,4,5};
int *p;// 遍历数组:当p未超过数组最后一个元素时继续循环
for (p = &arr[0]; p < &arr[5]; p++) {printf("%d ", *p);
}

4.野指针

概念: 野指针就是指针指向的位置是不可知的(随机的不正确的没有明确限制的
当一个程序存在野指针时,野指针的行为具有不确定性
  1. 可能立即触发程序崩溃(如段错误)
  2. 可能暂时正常运行,但在后续操作中引发错误
  3. 可能修改无关内存区域,导致数据损坏或程序逻辑错误
  4. 可能触发安全漏洞,被用于缓冲区溢出等攻击

4.1 野指针的成因和解决方法

4.1.1 指针未初始化
#include <stdio.h>
int main()
{ int *p;//局部变量指针未初始化,默认为随机值*p = 20;return 0;
}

局部变量p未进行初始化,此时的p就是野指针。 

解决方法

如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL。NULL相当于给指针变量了一个固定的地方,不再“野”。
NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址会报错。
4.1.2 指针越界访问
#include <stdio.h>
int main()
{int arr[10] = {0};int *p = &arr[0];int i = 0;for(i=0; i<=11; i++){//当指针指向的范围超出数组arr的范围时,p就是野指针*(p++) = i;}return 0;
}

当指针指向的范围超出数组arr的范围时,p就是野指针 。

⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问。
4.1.3 指针指向的空间释放
#include <stdio.h>
int* test()
{int n = 100;return &n;
}
int main()
{int*p = test();
printf("%d\n", *p);return 0;
}

当程序运行函数test()结束后,由于n为局部变量,他在内存中所占的空间就会被销毁,导致指针p无法指向具体的变量,成为了野指针。

解决方法

1. 指针变量不再使⽤时,及时置NULL,指针使⽤之前检查有效性,置NULL的指针不指向任何有效内存

2. 如造成野指针的第3个例⼦,不要返回局部变量的地址。

———————————————————————————————————————————

持续更新中

5. void指针和assert断言

5.1 void指针

在指针类型中有一种类型为void *空类型指针(void在英文中的意思是空的),也可以理解为无类型的指针或者叫做泛型指针

void*空类型指针难道是任何类型的数据地址都不能存放的指针么?恰恰相反,他可以接收任意类型地址。

void*空类型指针也有他的局限性:

1. 不能直接进行解引用;

2. 不能直接进行指针运算。

原因:空类型指针无法确定访问的内存字节数目。

void* 空类型指针的核心作用就是作为“通用地址容器”,必须通过类型转换才能进行解引用或者指针运算。

5.2 assert断言

在C语言中,assert()是一个用于调试的宏(不是函数),主要作用是在程序运行时检查某个条件是否为真。

基本用法

1. 在使用assert()时,需要包含标准库头文件<assert.h>;

2. 他的语法形式为: assert(表达式);

3. 运行时判断“表达式”是否为真(非0),若为真,assert()不做任何操作,程序继续运行;若为假,assert()会打印错误信息(包含文件名行号表达式),并调用abort()函数终止程序。

如下所示:

abort()函数是用于异常终止程序的标准库函数,他的作用是在程序发生严重错误时强制结束运行,不执行正常的清理操作:

#include <stdlib.h>
//使用前需包含头文件
void abort(void);//此为函数原型,无参数,无返回值
作用:立即终止当前程序,并返回一个非零状态码给操作系统,表示程序异常退出//可以联想main()函数中的return 0;0 表示程序正常结束

 如果我们给上述报错代码加上 #define NDEBUG ,就可以关闭assert()宏对表达式的判断。

#define NDEBUG//解除断言
#include <assert.h>
int main()
{int a = 10;int* p = &a;p = NULL;assert(p != NULL);return 0;
}

 assert()他的作用和if语句十分相近,他们的对比如下:

assert()的缺点是,因为引入了额外的检查,增加了程序的运行时间。 

在Release版本中,程序会将assert()优化掉,从而不影响用户使用程序时的效率。

6. 指针的应用

6.1 strlen的模拟实现

我们可以思考试着解决这样一个题目:

模拟实现库函数strlen

:strlen是用来测量字符串长度的一个标准库函数,他的头文件是<string.h>。他的工作原理是从字符串的起始地址开始遍历,当遇到 '\0' 时就会停止,他计算的就是第一个字符到 '\0' 之间的字符个数(不包括\0)。

递归实现方法
int mystrlen(char* str)//构建自有函数mystrlen
{int count;if (*str == '\0'){count = 0;//趋近条件return count;}else {count = 1 + mystrlen(str + 1);//递归法return count;}
}
int main()
{char str[] = "sadas";printf("%d", mystrlen(str));return 0;
}
迭代法 
int mystrlen_2(char* str)
{int count = 0;while (*str)//当*str == '\0'时,判断为0,循环结束{count++;str++;}return count;
}
int main()
{char str[] = "sadas";printf("%d", mystrlen_2(str));return 0;
}

在学习字符函数后,我们会继续更新第三种方法 

6.2 传值调用和传址调用

指针最常见的使用,就是利用地址解引用,对相关数据进行一个修改,但是我们知道更直接的一种方法就是变量赋值,那什么情况下非指针不可呢?

我们观察下面的代码:

#include <stdio.h>
void Swap1(int x, int y)//内部交换x,y
{int tmp = x;x = y;y = tmp;
}
int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);printf("交换前:a=%d b=%d\n", a, b);
Swap1(a, b);printf("交换后:a=%d b=%d\n", a, b);return 0;
}

在自建的函数中,我们对x和y的值进行了交换,那么他会影响main函数中变量a和b的值么? 

我们发现,a和b的值并没有发生交换,这是为什么呢?

我们尝试对这段代码进行调试:

实参a和b形参x和y他们的地址是不一样的,指针指向数据存储的区域,那我们能够知道,x和y的数值交换,并不影响实参a和b的值,因为他们的地址不同。swap1函数实际上,只是把实参a和b的数值传递给了形参x和y,这种就叫做传值调用

结论:实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实

那我们要怎么通过函数实现a和b的数值交换呢?我们前面提到了指针,那是不是我们可以把a和b的指针传递过去呢?基于这个想法,我们对代码进行修改:

#include <stdio.h>
void Swap2(int*px, int*py)//接收整型指针
{int tmp = 0;tmp = *px;*px = *py;*py = tmp;
}
int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);printf("交换前:a=%d b=%d\n", a, b);Swap2(&a, &b);printf("交换后:a=%d b=%d\n", a, b);return 0;
}

成功实现了a和b的数值交换,我们使用指针作为参数传递的方式,叫做传址调用

传址调用,可以让函数和主调函数之间建立真正的联系,在函数内部可以修改主调函数中的变量;所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采用传值调用。如果函数内部要修改主调函数中的变量的值,就需要传址调用

7. 指针和数组

7.1 数组名的理解
7.2 指针访问数组
7.3 数组传参
7.4 数组指针

持续更新中

打怪升级中.........................................................................................................................................

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/91184.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/91184.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode 1616.分割两个字符串得到回文串

给你两个字符串 a 和 b &#xff0c;它们长度相同。请你选择一个下标&#xff0c;将两个字符串都在 相同的下标 分割开。由 a 可以得到两个字符串&#xff1a; aprefix 和 asuffix &#xff0c;满足 a aprefix asuffix &#xff0c;同理&#xff0c;由 b 可以得到两个字符串 …

算法【1】

网址&#xff1a;主站 工具补充 1. sort 函数的使用规则 作用&#xff1a;对容器元素进行排序&#xff0c;默认升序。语法&#xff1a;sort(起始迭代器, 结束迭代器, 比较规则) 前两个参数是排序范围&#xff1a;[begin, end)&#xff08;包含begin&#xff0c;不包含end&am…

信创国产Linux操作系统汇总:从桌面到服务器,百花齐放

在数字化浪潮席卷全球的今天&#xff0c;操作系统作为信息产业的基石&#xff0c;其战略地位日益凸显。曾经由国外巨头垄断的格局正悄然改变——中国本土Linux操作系统历经多年沉淀&#xff0c;已形成了百花齐放的局面。无论是日常办公、专业开发&#xff0c;还是关键行业应用&…

claudia for claude code

一.安装所有必需的依赖项 1.安装 Git for Windows 步骤: 访问 Git 的官方网站 git-scm.com。 下载适用于 Windows 的最新版本安装程序。 运行安装程序。在安装向导的各个步骤中&#xff0c;建议保留所有默认设置&#xff0c;这些设置对于本指南的后续操作已经足够。 验证…

企业内外网文件安全传输解决方案

企业内外网文件安全传输解决方案 基于零信任架构的智能中转系统设计 一、业务背景与挑战分析 1.1 企业网络安全现状 在数字化转型浪潮下&#xff0c;企业面临着前所未有的安全挑战。传统的"城墙式"网络防护已无法满足现代企业灵活协作的需求。根据《2024年中国企业…

《HCIA-Datacom 认证》希赛三色笔记:详解 VLAN 间通信的 3 种实现方式

标记说明:&#xffed;掌握内容 &#xffed;次重点 &#xffed;理解内容 在局域网部署中&#xff0c;VLAN 技术通过隔离广播域提升了网络安全性和稳定性&#xff0c;但不同 VLAN 间的通信需求又成了新的难题。比如财务部门的电脑&#xff08;VLAN 10&#xff09;需要访问服务…

Windows 10 系统下的编程字体安装与配置(VSCode)教程

Windows 10 系统下的编程字体安装与配置教程 常见的优秀编程字体 开发者社区中有许多备受推崇的编程字体&#xff0c;它们都致力于提升代码的可读性和舒适度。以下是一些常见的选择&#xff1a; Fira Code: 以其丰富的编程连字&#xff08;ligatures&#xff09;而闻名&…

ITIL 4 高速IT:解耦架构——构建快速迭代的技术基座

一、为什么要解耦&#xff1a;从“架构”谈到“速度”1.高速IT的真正瓶颈&#xff1a;不是能力&#xff0c;而是架构在我们深入学习ITIL 4 高速IT的时候&#xff0c;大家可能都会有个疑问&#xff1a;为什么有些组织在数字化转型过程中推得动&#xff0c;有些却始终难以突破&am…

网络协议——MPLS(多协议标签转发)

一&#xff0c;基本概述1. mpls基本概念MPLS位于二三层之间&#xff0c;可以向所有网络层提供服务。通过在数据链路层和网络层之间增加额外的MPLS头部&#xff0c;基于MPLS头部实现数据快速转发。2. 控制平面和转发平面控制平面&#xff1a;负责产生和维护路由信息以及标签信息…

影刀RPA_初级课程_玩转影刀自动化_EXCEL操作自动化

声明&#xff1a;相关内容来自影刀学院&#xff0c;本文章为自用笔记&#xff0c;切勿商用&#xff01;&#xff08;若有侵权&#xff0c;请联络删除&#xff09; 1. 数据的表达 1.1 列表 1.1 获取一段字符&#xff08;字符串列表的截取 —— 前开后闭&#xff09; 1.2 获取长…

当贝纯净版_海信ip811n海思mv320处理器安卓4.42及9.0主板优盘免拆刷机固件及教程

海信IP811N安卓4.4.2及安卓9.0主板免拆升级教程 下载固件之前&#xff0c;请拆机确认下主板处理器是否为 海思hi3798mv320处理器&#xff0c;拆机将主板上 位于中心位置的CPU芯片上的黑色贴纸取下 然后查看芯片第二行是否有V32字样&#xff0c;如下图 然后进入机顶盒设置&a…

三、平衡桥电路

一、电路结构 由于平衡桥后要连接双T型桥逆变电路并联&#xff0c;这里采用平衡桥电路来稳定母线和中线的电压平衡&#xff0c;使正母线电压BUS和负母线电压BUS-相对于中线的电压大小相等&#xff0c;极性相反&#xff0c;如50VBUS&#xff0c;-50BUS-。 平衡桥电路由两个电容…

Java-85 深入浅出 MySQL InnoDB 存储结构:Buffer Pool、写缓冲与日志机制全解

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; AI炼丹日志-30-新发布【1T 万亿】参数量大模型&#xff01;Kim…

Linux救援模式之应用篇

挂载并访问文件系统1. 首先识别分区 fdisk -l # 查看所有磁盘和分区 lsblk # 以树状结构查看块设备 blkid # 查看分区的UUID和文件系统类型2. 创建挂载点并挂载分区 mkdir /mnt/rescue # 创建挂载点# 挂载根分区(根据你实际的根分区设备) mount /dev/…

【学习路线】游戏开发大师之路:从编程基础到独立游戏制作

前言 游戏开发是一个充满创意和技术挑战的领域&#xff0c;它融合了编程、美术、音效、设计等多个学科。随着游戏产业的蓬勃发展&#xff0c;游戏开发已成为最具吸引力的技术职业之一。本文将为您提供一条从零基础到游戏开发大师的完整学习路线&#xff0c;涵盖编程基础、游戏引…

宇树 G1 部署(九)——遥操作控制脚本 teleop_hand_and_arm.py 分析与测试部署

首先&#xff0c;我使用的是 v1.0 版本&#xff0c;宇树最近发力了更新的很快&#xff1a;xr_teleoperate-1.0 teleop_hand_and_arm.py 支持通过 XR 设备&#xff08;比如手势或手柄&#xff09;来控制实际机器人动作&#xff0c;也支持在虚拟仿真中运行。可以根据需要&#x…

第十一天:不定方程求解

每日一道C题&#xff1a;不定方程求解 问题&#xff1a;给定正整数a&#xff0c;b&#xff0c;c。求不定方程 axbyc 关于未知数x和y的所有非负整数解组数。 要求&#xff1a;输入一行&#xff0c;包含三个正整数a&#xff0c;b&#xff0c;c&#xff0c;两个整数之间用单个空格…

ElasticStack技术栈概述及Elasticsearch8.2.2集群部署并更换JDK版本为openjdk-17

ElasticStack 一、引言 在当今数据驱动的时代&#xff0c;如何高效地收集、处理和分析日志及其他类型的数据&#xff0c;已成为企业构建可观测性和运维能力的重要课题。Elastic Stack&#xff08;早期称为 ELK Stack&#xff09;是一套由 Elastic 公司推出的开源技术栈&#xf…

Doris中文检索效果调优

一、问题描述 原来的日志系统使用的是ES作为底层存储&#xff0c;后来因为数据量大了之后&#xff0c;出现了写入存在阻塞和查询效率变低的问题。后来决定切换到Doris数据库。 Doris的优势根据公开资料来看&#xff0c;它在写入性能、查询效率和存储成本上&#xff0c;都优于…

CDN怎么加速跟防御网站攻击呢?

**CDN&#xff08;内容分发网络&#xff09;**通过分布式架构和智能路由技术&#xff0c;不仅可以加速网站内容访问&#xff0c;还能有效防御多种网络攻击&#xff08;如DDoS、SQL注入等&#xff09;。以下是 CDN 如何实现加速和防御的详细解析&#xff1a;1. CDN 如何加速网站…