<C语言> 指针(上)

news2024/11/25 1:53:13

1.指针是什么?

指针(Pointer)是一种特殊的变量类型,它存储了一个内存地址。可以将指针视为存储了另一个变量的地址的变量。通过指针,可以直接访问和修改内存中的数据。

指针提供了一种间接访问内存的方式,使得在程序中可以有效地操作和管理内存资源。

  1. 指针是内存中一个最小单元的编号,也就是地址
  2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量

我们可以通过&(取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个 变量就是指针变量

#include <stdio.h>

int main() {
    int a = 10;  //在内存中开辟一块空间
    int *pa = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
                 //a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在pa变中,pa就是一个之指针变量。
    *pa = 20;    //解引用操作符(间接访问操作符)
    printf("%d\n", a);   //20
    //char * pc = &ch;

    //printf("%d\n", sizeof(pa));
    //printf("%d\n", sizeof(pc));

    //printf("%p\n",  &a);
    return 0;
}

总结:

指针变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。

那这里的问题是:

  • 一个小的单元到底是多大?
  • (1个字节) 如何编址?

经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pe2fQr6z-1688483690225)(C:\Users\TaeYeon\AppData\Roaming\Typora\typora-user-images\image-20230630220338283.png)]

对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电 平(低电压)就是(1或者0); 那么32根地址线产生的地址就会是:

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000001

11111111 11111111 11111111 11111111

这里就有2的32次方个地址。

每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB == 232/1024/1024MB==232/1024/1024/1024GB == 4GB) 4G的空间进行编址。

这里我们就明白:

  • 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
  • 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地 址。

总结:

指针变量是用来存放地址的,地址是唯一标示一个内存单元的

指针的大小在32位平台是4个字节,在64位平台是8个字节。

int main()
{
	int* pa;
	char* pc;
	float* pf;

	printf("%d\n", sizeof(pa));  //4/8
	printf("%d\n", sizeof(pc));  //4/8
	printf("%d\n", sizeof(pf));  //4/8

	return 0;
}

2.指针和指针类型

这里我们在讨论一下:指针的类型

我们都知道,变量有不同的类型,整型,浮点型等。那指针有没有类型呢? 准确的说:有的。

当有这样的代码:

int num = 10;
p = &num;

要将&num(num的地址)保存到p中,我们知道p就是一个指针变量,那它的类型是怎样的呢? 我们给指针变量相应的类型。

char  *pc = NULL;
int   *pi = NULL;
short *ps = NULL;
long  *pl = NULL;
float *pf = NULL;
double *pd = NULL;

这里可以看到,指针的定义方式是: type + *

其实:

char* 类型的指针是为了存放 char 类型变量的地址。

short* 类型的指针是为了存放 short 类型变量的地址。

int* 类型的指针是为了存放 int 类型变量的地址。

那指针类型的意义是什么?

2.1 指针±整数

可以对指针进行加法和减法运算,以在内存中移动指针的位置。这种指针与整数的运算称为指针算术。

当一个指针与一个整数相加时,指针会根据指定的整数值进行偏移。偏移量是通过将整数值乘以指针所指向类型的大小来计算的。

#include <stdio.h>
int main() {
    int a = 0x11223344;
    int *pa = &a;
    char *pc = (char *) &a;

    printf("pa=%p\n", pa);//pa=000000000061FE0C
    printf("pc=%p\n", pc);//pc=000000000061FE0C

    printf("pa+1=%p\n", pa - 1);//pa+1=000000000061FE08 地址从0C到08 -4
    printf("pc+1=%p\n", pc - 1);//pc+1=000000000061FE0B 地址从0C到0B -1

    return 0;
}

总结:指针的类型决定了指针向前或者向后走一步有多大(距离)。

2.2 指针的解引用

指针的解引用是通过指针变量来访问其所指向内存位置上存储的值。解引用操作使用星号(*)运算符来实现。

当对一个指针进行解引用时,可以通过将星号(*)放在指针变量前面来访问指针所指向内存位置上的值。

#include <stdio.h>
int main() {
    int arr[10] = {0};
    int *p = arr;//数组名就是数组首元素的地址arr->&arr[0];

    int i = 0;
    for (i = 0; i < 10; i++) {
        *(p + i) = i + 1;
    }

    for (i = 0; i < 10; i++) {
        printf("%d ", *(p + i));//1 2 3 4 5 6 7 8 9 10
    }


    for (i = 0; i < 10; i++) {
        //*(p + i) = i + 1等价于以下两行
        *p = i + 1;
        p++;
    }

    return 0;
}

总结:

指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。

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

3.野指针

野指针(Dangling Pointer)是指指向已经释放或无效的内存位置的指针。当指针指向的内存被释放或无效后,该指针仍然保留了原来的地址,但此时访问该指针可能导致未定义的行为。

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

3.1 野指针成因

1.指针未初始化

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

2.指针越界访问

#include <stdio.h>
int main() {
    int arr[10] = {0};
    int *p = arr;
    int i = 0;
    for (i = 0; i <= 11; i++) {
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        *(p++) = i;
    }
    return 0;
}

3.指针指向的空间释放

在使用动态内存分配函数(如free)释放内存后,在使用动态内存分配函数(如free)释放内存后

3.2 如何规避野指针

#include <stdio.h>
int main() {
    int a = 10;
    int *pa = &a;//明确初始化

    //NULL - 0,就是初始化指针的
    int *p = NULL;


    return 0;
}

1.指针初始化

2.小心指针越界

3.指针指向空间释放,及时置NULL

//返回局部变量指针
#include <stdio.h>
int *test() {
    int num = 100;
    return &num;
}

int main() {
    int *p = test();
    *p = 200;

    return 0;
}

在这段代码中存在一个问题,即返回了一个指向局部变量的指针 &num。这会导致在函数 test() 执行完毕后,num 变量所占用的内存空间被释放,而指向它的指针 p 仍然保留着该地址,从而形成了一个野指针。

4.避免返回局部变量的地址

#include <stdio.h>
int main() {
    int a = 10;
    int *p = NULL;
    //检查有效性
    if (p != NULL) {
        printf("%d\n", *p);
    }

    return 0;
}

5.指针使用之前检查有效性

4.指针运算

4.1 指针±整数

当指针与一个整数相加时,指针会向后偏移,移动的字节数由指针所指向类型的大小决定。例如,如果指针指向一个int类型的变量,那么加1将使指针向后移动sizeof(int)个字节。

同样地,当指针与一个整数相减时,指针会向前偏移。减法操作会将指针向前移动指定数量的元素,字节数由指针所指向类型的大小决定。

实例:

#include <stdio.h>

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    int *ptr = numbers;  // 指向数组的第一个元素

    printf("初始指针位置: %p\n", ptr);

    ptr = ptr + 2;  // 向后移动2个int大小的元素
    printf("移动后的指针位置: %p\n", ptr);

    ptr = ptr - 1;  // 向前移动1个int大小的元素
    printf("移动后的指针位置: %p\n", ptr);

    return 0;
}

输出结果:

初始指针位置: 000000000061FE00
移动后的指针位置: 000000000061FE08
移动后的指针位置: 000000000061FE04

注意:

指针的算术运算必须在同一数组中进行,或者指向同一类型的对象。否则,结果将是不确定的。

4.2 指针-指针

指针减法的结果是两个指针之间的元素个数,而不是字节数。具体来说,两个指针相减的结果将是一个整数,表示两个指针之间相隔的元素个数,其中每个元素的大小由指针所指向类型的大小决定。

示例:

#include <stdio.h>

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    int *ptr1 = &numbers[0];// 指向数组的第一个元素
    int *ptr2 = &numbers[3];// 指向数组的第四个元素

    printf("指针1的位置: %p\n", ptr1);
    printf("指针2的位置: %p\n", ptr2);

    ptrdiff_t diff = ptr2 - ptr1;// 计算指针之间的偏移量
    printf("指针1和指针2之间的偏移量: %td\n", diff);

    return 0;
}

输出结果:

指针1的位置: 0x7ffeeef0d030
指针2的位置: 0x7ffeeef0d03c
指针1和指针2之间的偏移量: 3

注意:

两个指针进行减法运算必须指向同一个数组或同一类型的对象。否则,结果将是不确定的。另外,如果两个指针之间没有直接或间接的关系,进行减法运算是无意义的。

int main() {
    //两个指针相减的前提是:指针指向的同一个数组(同一块连续的空间)
    int arr[10] = {0};
    printf("%d\n", &arr[9] - &arr[0]);
    printf("%d\n", &arr[0] - &arr[9]);

    int a = 10;
    char c = 'w';
    printf("%d\n", &a - &c); //err 报错

    return 0;
}

4.3 指针的关系运算

可以对指针进行关系运算,用于比较指针的大小关系。

  • <(小于):如果左操作数指向的地址在内存中出现在右操作数指向的地址之前,则结果为真。
  • >(大于):如果左操作数指向的地址在内存中出现在右操作数指向的地址之后,则结果为真。
  • <=(小于等于):如果左操作数指向的地址在内存中出现在右操作数指向的地址之前或相等,则结果为真。
  • >=(大于等于):如果左操作数指向的地址在内存中出现在右操作数指向的地址之后或相等,则结果为真。
  • ==(等于):如果两个操作数指向相同的地址,则结果为真。
  • !=(不等于):如果两个操作数指向不同的地址,则结果为真。

这些关系运算符通常用于比较指针之间的相对位置,而不是比较它们所指向的具体值。

实例:

for (vp = &values[N_VALUES]; vp > &values[0];) {
    *--vp = 0;
}

代码简化, 这将代码修改如下:

for (vp = &values[N_VALUES - 1]; vp >= &values[0]; vp--) {
    *vp = 0;
}

实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。

标准规定:

允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

5.指针和数组

我们看一个例子:

#include <stdio.h>
int main() {
    int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
    printf("%p\n", arr);
    printf("%p\n", &arr[0]);
    return 0;
}

输出结果:

000000000061FDF0
000000000061FDF0

可见数组名和数组首元素的地址是一样的。

结论:数组名表示的是数组首元素的地址。

那么这样写代码是可行的:

int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
int *p = arr;//p存放的是数组首元素的地址

既然可以把数组名当成地址存放到一个指针中,我们使用指针来访问一个就成为可能。

例如:

#include <stdio.h>
int main() {
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
    int *p = arr;//指针存放数组首元素的地址
    int sz = sizeof(arr) / sizeof(arr[0]);
    for (int i = 0; i < sz; i++) {
        printf("&arr[%d] = %p   <====> p+%d = %p\n", i, &arr[i], i, p + i);
    }
    return 0;
}

输出结果:

&arr[0] = 000000000061FDE0   <====> p+0 = 000000000061FDE0
&arr[1] = 000000000061FDE4   <====> p+1 = 000000000061FDE4
&arr[2] = 000000000061FDE8   <====> p+2 = 000000000061FDE8
&arr[3] = 000000000061FDEC   <====> p+3 = 000000000061FDEC
&arr[4] = 000000000061FDF0   <====> p+4 = 000000000061FDF0
&arr[5] = 000000000061FDF4   <====> p+5 = 000000000061FDF4
&arr[6] = 000000000061FDF8   <====> p+6 = 000000000061FDF8
&arr[7] = 000000000061FDFC   <====> p+7 = 000000000061FDFC
&arr[8] = 000000000061FE00   <====> p+8 = 000000000061FE00
&arr[9] = 000000000061FE04   <====> p+9 = 000000000061FE04

所以 p+i 其实计算的是数组 arr 下标为i的地址。

那我们就可以直接通过指针来访问数组。

#include<stdio.h>
int main() {
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
    int *p = arr;//指针存放数组首元素的地址
    int sz = sizeof(arr) / sizeof(arr[0]);
    int i = 0;
    for (i = 0; i < sz; i++) {
        printf("%d ", *(p + i));  //1 2 3 4 5 6 7 8 9 0 
    }
    return 0;
}

总结:

  1. 数组名作为指针:
    • 数组名可以被视为指向数组首元素的指针。
    • 数组名作为指针使用时,可以进行指针运算。
  2. 使用指针访问数组元素:
    • 可以使用指针来访问和操作数组元素。
    • 使用指针访问数组元素的常见方法是使用指针偏移运算符*[]
  3. 指针与数组的关系:
    • 数组名可以用作指针常量,不能被赋值或修改。
    • 可以创建指向数组的指针变量,并通过指针变量来操作数组元素。
  4. 指针和数组的传递:
    • 在函数中,可以通过指针参数来传递数组,从而可以在函数内部修改数组的内容。
    • 数组作为函数参数传递时,传递的实际是数组的首元素的地址。函数中的形式参数可以声明为指针类型或数组类型,两者等效。

6.二级指针

二级指针是指指向指针的指针。也可以称为指向指针的指针,它提供了对指针的间接访问。

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?这就是二级指针。

在这里插入图片描述

例如:

#include <stdio.h>
int main() {
    int a = 10;   
    int *p = &a;  //p就是指针变量,一级指针变量
    int **pp = &p;//pp就二级指针

    
    **pp = 200;    //*(*pp) = 200;  *pp表示a的地址 再来一个*表示对a地址解引用
    //*p = 20;
    printf("%d\n", a);  //200
 
    return 0;
}

多级指针

二级指针的概念可以扩展到多级指针。多级指针是指指向指针的指针的指针,以此类推。例如,三级指针是指指向二级指针的指针。

例如:

#include <stdio.h>
int main() {
    int a = 10;   
    int *p = &a;  //p就是指针变量,一级指针变量,指向一个变量,是一个变量的地址
    int **pp = &p;  //pp是二级指针,是一个一级指针的地址
    int ***ppp = &pp;  //ppp是一个三级指针,是一个二级指针的地址
    int ****pppp = &ppp;  //pppp是一个四级指针,是一个三级指针的地址
 
    return 0;
}

7.指针数组

在C语言中,指针数组是指一个数组,其元素都是指针类型的变量。指针数组可以存储指向不同类型对象的指针。

那么指针数组是指针还是数组?

是数组。是存放指针的数组。

例如:

int main() {
    //整型数组-存放整型的数组
    int arr[10];
    //字符数组-存放字符的数组
    char arr2[5];
    //指针数组-存放指针的数组
    int *arr3[5]; //存放整型指针的数组
    char *arr4[6];//存放字符指针的数组

    return 0;
}

例如:

#include <stdio.h>
int main() {
    int a = 10;
    int b = 20;
    int c = 30;
    int d = 40;
    int e = 50;

    int *arr3[5] = {&a, &b, &c, &d, &e};//存放整型指针的数组
    int i = 0;
    for (i = 0; i < 5; i++) {
        printf("%d ", *(arr3[i]));
    }

    return 0;
}

指针数组使用场景:用一个一维数组模拟二维数组

#include<stdio.h>
int main() {
    //用一维数组模拟一个二维数组
    int arr1[] = {1, 2, 3, 4, 5};
    int arr2[] = {2, 3, 4, 5, 6};
    int arr3[] = {3, 4, 5, 6, 7};
    int arr4[] = {4, 5, 6, 7, 8};

    int *arr[4] = {arr1, arr2, arr3, arr4};
    int i = 0;
    for (i = 0; i < 4; i++) {
        int j = 0;
        for (j = 0; j < 5; j++) {
            printf("%d ", *(*(arr + i) + j));
        }
        printf("\n");
    }

    //int i = 0;
    //for (i = 0; i < 4; i++)
    //{
    //	int j = 0;
    //	for (j = 0; j < 5; j++)
    //	{
    //		printf("%d ", arr[i][j]);
    //	}
    //	printf("\n");
    //}

    return 0;
}

输出结果:

1 2 3 4 5 
2 3 4 5 6 
3 4 5 6 7 
4 5 6 7 8 

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

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

相关文章

SpringCloud源码探析(七)-整合Elasticsearch

1.概述 ElasticSearch是一个基于Lucene的搜索服务器&#xff0c;提供了一个分布式多用户能力的全文搜索引擎。它是基于JAVA语言开发&#xff0c;并且是基于RESTful web接口进行查询和结果返回&#xff0c;是一款非常流行的企业级搜索引擎。Elasticsearch的核心功能包括存储数据…

Redis缓存问题与缓存更新机制

目录 ​编辑 一、缓存问题 1.1 缓存穿透 1.1.1 问题来源 1.1.2 解决方案 1.1.2.1 缓存空对象 1.1.2.2 使用布隆过滤器 1.2 缓存击穿 1.2.1 问题来源 1.2.2 解决方案 1.2.2.1 设置热点数据永远不过期 1.2.2.2 新增后台定时更新缓存线程&#xff08;逻辑不过期&#xff09; 1.2.…

详解Java Synchronized锁升级原理

✅作者简介&#xff1a;热爱Java后端开发的一名学习者&#xff0c;大家可以跟我一起讨论各种问题喔。 &#x1f34e;个人主页&#xff1a;Hhzzy99 &#x1f34a;个人信条&#xff1a;坚持就是胜利&#xff01; &#x1f49e;当前专栏&#xff1a;JAVA多线程 &#x1f96d;本文内…

蓝桥杯专题-试题版含答案-【猴子吃桃问题】【小光棍数】【九九乘法表】【谁是最好的Coder】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

(Linux) 套接字socket基础

文章目录 前言基本原理 Codeserverclient 核心函数socketbindlistenacceptrecvsendconnectclose 多线程改进END 前言 本文将以纯C语言描述&#xff0c;编译器gcc。 C/C没有标准的网络库&#xff0c;因为都需要用到各个平台的接口才行。 本文讲解Linux下最基础的socket编程&a…

uniapp 常用提示弹框整理

一. 加载提示弹框 在执行数据查询、页面数据渲染等过程中弹出提示。以页面渲染为例&#xff1a; //前端数据请求时&#xff0c;显示加载提示弹框 uni.showLoading({title: 加载中... }); // 数据从后端接口返回后&#xff0c;提示弹框关闭 uni.hideLoading();效果如下&#x…

Sui Builder House京都站|创意大赛获奖名单公布

Sui Builder House京都站于6月30日&#xff08;周五&#xff09;圆满结束&#xff0c;这是一次Sui生态系统项目演示和展示各自产品的良好机会。构建者们向大家展示了游戏、NFT、DeFi和基础设施赛道的项目&#xff0c;同时现场演讲还介绍了Sui的最新进展以及有关AI和用户体验设计…

React环境安装配置

React环境安装配置 一、前提二、React安装 一、前提 安装本地React环境需要Node.js&#xff0c;如果具有Node环境跳过即可。如果没有安装则可参考该篇文章安装Node环境&#xff0c;点击查看 二、React安装 全局安装React 首先打开命令行&#xff0c;建议以管理员身份输入命…

20230704测试STC32G实验箱9.6(STC32G12K128)开发板的虚拟串口(C语言深入了解)

20230704测试STC32G实验箱9.6&#xff08;STC32G12K128&#xff09;开发板的虚拟串口&#xff08;C语言深入了解&#xff09; 06第五集&#xff1a;C语言运算符和进制数入门上.mp4 07第五集&#xff1a;C语言运算符和进制数入门下.mp4 2023/7/4 19:00 下次 在【冲哥】录视频的时…

024、数据库管理之数据同步工具TiCDC

TiCDC TiCDCTiCDC介绍架构与原理适用场景对已有TiDB进行扩容部署TiCDC管理工具TiCDC同步任务查询所有TiCDC同步任务查询TiCDC指定的同步任务管理TiCDC同步任务动态更新任务监控TiCDC 实验数据同步完整实操缩容当前TiCDC节点 TiCDC TiCDC介绍 TiCDC 是一款通过拉取 TiKV 变更日…

leetcode 42-接雨水

解法一&#xff1a;双指针&#xff08;暴力按列计算&#xff09; 首先&#xff0c;如果按照列来计算的话&#xff0c;宽度一定是1了&#xff0c;我们再把每一列的雨水的高度求出来就可以了。 可以看出每一列雨水的高度&#xff0c;取决于该列左侧最高的柱子和右侧最高的柱子中…

VMware虚拟机的基本操作:快照和克隆

VMware虚拟机的基本操作 一、虚拟机快照二、克隆 一、虚拟机快照 “快照”是虚拟机磁盘文件&#xff08;VMDK&#xff09;在某个点及时的副本。系统崩溃或系统异常&#xff0c;你可以通过使用恢复到快照来保持磁盘文件系统和系统存储。当升级应用和服务器及给它们打补丁的时候…

一桩关于Json序列化引发的惨案(Go系统)

文章目录 前言突然崩了排查问题关于go的json库什么是反射 解决大结构体序列化的性能问题干掉大结构体减少反射使用一些好用的第三方序列化包 自定义序列化 写在最后 前言 一个风和日丽的下午&#xff0c;线上系统突然开始报警&#xff08;系统温馨提示&#xff0c;您的服务接口…

部署Git服务器

哈喽&#xff0c;大家好&#xff0c;本次为大家演示如何部署git服务器. 首先要准备gitea和phpstudy_pro phpstudy一路nest即可&#xff0c;可以帮你安装mysql和阿帕奇。 登录127.0.0.1:3000注册gitea即可。 如何在上传公钥的时候出现500的错误&#xff0c;加入这句便可解决…

SpringBoot+Vue+Element-ui实现文件下载

目录 1.后端代码部分 2.前端代码部分 3.效果展示 1.后端代码部分 GetMapping("downloadFile")public void downloadFile(RequestParam("filename") String filename,HttpServletResponse response) throws Exception {// 告知浏览器这是一个字节流&…

WebSocket connection to “wss://xxx/xxx“ failed

用了https后&#xff0c;需要用nginx作websocket地址的转发才能使用wss&#xff0c;别直接用端口访问 location /ws/ {proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "Upgrade"; proxy_pass http://localhost:10001/…

JVM源码剖析之SymbolTable和StringTable

很多读者在观看JVM相关的书籍时会看到SymbolTable和StringTable&#xff0c;书中的三言二语介绍的不是很清楚&#xff0c;并且读者的水平有限&#xff0c;导致无法理解SymbolTable和StringTable。所以特意写此篇图文并茂的文章来彻底理解SymbolTable和StringTable这两张表。 版…

Java面试Day16

1.Dubbo 是什么&#xff1f;是否了解过它的架构设计&#xff1f; Dubbo官网&#xff1a;Apache Dubbo Dubbo是一个高性能、轻量级的开源Java RPC框架&#xff0c;它提供了完整的RPC协议栈&#xff0c;包括服务发布、服务引用、负载均衡、容错、服务治理和服务监控等功能&#…

构造函数使用初始化列表+模板

构造函数使用初始化列表模板 注意对应关系&#xff1a; Stack(int size) ;template<class DataType> inline Stack<DataType>::Stack(int size) : stacksize(size), top(0) {item new DataType[stacksize];if (item ! nullptr) cout << "成功初始化栈&…

再见!Fastjson!

你为何仍用Fastjson&#xff1f; 原因可以说出5678种&#xff0c;总而言之言而总之&#xff0c;你不&#xff08;敢&#xff09;切换的原因或许只有一个&#xff1a;Fastjson的静态方法调用用着省心&#xff1b;最重要的是&#xff0c;对其它JSON库&#xff08;如Jackson/Gson…