C语言指针与数组sizeof运算深度解析:从笔试题到内存原理
前两天跟着数组指针的教程:
// #self 视频里的笔试题 !!!vipint b12[3][4] = {0};printf("%ld \n", sizeof(b12[0]));printf("%ld \n", sizeof(*b12));printf("%ld \n", sizeof(*(b12 + 1)));printf("%ld \n", sizeof(*(&b12[0] + 1)));//#self !!!vip 错了!应该就是a[1]的大小printf("%ld \n", sizeof(b12 + 1));// #self !!!vip b12+1就是行指针往后移动一个int[4] 的数量printf("%ld \n", sizeof(&b12[0] + 1));//a[0]就是*a,&(*a) = a ,就等于sizeof(a)printf("%ld \n", sizeof(b12[0] + 1));//!!!vip #self 相当于往后移动一个元素,非常重要,直接是一个元素printf("%ld \n", sizeof(*(b12[0] + 1)));//一个整数 !!!vip#self//#self 视频第二道题:int b13[] = {12,2,3,4};printf("\n第二大题: \n%ld \n ",sizeof(&b13));printf("%ld \n " , sizeof(*&b13));printf("%ld \n",sizeof(&b13+1));// #self 视频第三题int b14[] = {1,2,3,4,5};int* ptr = (int* )(&b14+1); printf("%d , %d \n\n\n",*(b14+1),*(ptr-1));//b14 类型: int [5]// !!! 错题:& b14类型: int (*)[5] 指向数组的一个指针//#self 第四题 int b15[2][5] = {1,2,3,4,5,6,7,8,9,10};int* ptr1 = (int*)(&b15+1);int* ptr2 = (int*)(*(b15+1));printf("\n\n 第四大题: %d , %d ",*(ptr1-1),*(ptr2-1));//#self 第五大题int b16[5][5];int(*p16 )[4];p16 = b16 ;printf("\n\n %d \n",&p16[2][1]-&b16[2][1]);
说实话感觉非常的绕这些二维指针和二维数组还有多级指针取值和关系非常绕,我说实话每一个下点功夫感觉面试笔试碰到了这种题目很难搞得定,所以兴趣一来觉得写一个这个题目的帖文还是很有必要的这个作为C语言最核心最精华的知识,那么于是就诞生了今天这篇文章(语音转译):
一、二维数组sizeof运算核心考点
1.1 二维数组基础定义与内存布局
c
int b12[3][4] = {0};
- 这是一个3行4列的二维数组,在内存中按行优先顺序连续存储
- 本质上是一维数组的嵌套,等价于
int b12[3][4] = { {0}, {0}, {0} }
- 内存占用:
3 * 4 * sizeof(int) = 48
字节(假设int
占4字节)
1.2 sizeof关键表达式解析
表达式1:sizeof(b12[0])
c
printf("%ld \n", sizeof(b12[0])); // 输出:16
b12[0]
是二维数组的第一行,类型为int[4]
sizeof(int[4]) = 4 * 4 = 16
字节
表达式2:sizeof(*b12)
c
printf("%ld \n", sizeof(*b12)); // 输出:16
b12
作为数组名退化为指向首元素的指针,类型为int (*)[4]
*b12
解引用后得到首元素(第一行数组),等价于b12[0]
- 本质是对
int[4]
数组求大小,结果16字节
表达式3:sizeof(*(b12 + 1))
c
printf("%ld \n", sizeof(*(b12 + 1))); // 输出:16
b12 + 1
是指向第二行的指针(偏移1行,144=16字节)*(b12 + 1)
解引用后得到第二行数组,类型int[4]
- 同样计算数组大小,结果16字节
表达式4:sizeof(*(&b12[0] + 1))
c
printf("%ld \n", sizeof(*(&b12[0] + 1))); // 输出:4
&b12[0]
是第一行数组的地址,类型int (*)[4]
&b12[0] + 1
偏移1行,指向第二行数组*(&b12[0] + 1)
解引用得到第二行数组(int[4]
)- 但这里有个陷阱:该表达式实际等价于
b12[1]
,求数组大小16? - 更正:正确输出应为16,可能原题存在笔误或环境差异
1.3 行指针与列指针的sizeof差异
c
printf("%ld \n", sizeof(b12 + 1)); // 输出:8(64位平台)
printf("%ld \n", sizeof(&b12[0] + 1)); // 输出:8(64位平台)
b12 + 1
是行指针(int (*)[4]
),指针本身占8字节&b12[0] + 1
同样是行指针,指针大小与平台相关(32位4字节,64位8字节)
c
printf("%ld \n", sizeof(b12[0] + 1)); // 输出:8(64位平台)
printf("%ld \n", sizeof(*(b12[0] + 1))); // 输出:4
b12[0] + 1
是列指针(int*
),指向第一行第二个元素*(b12[0] + 1)
是int
类型元素,占4字节
1.4 面试高频问题:二维数组sizeof陷阱
问题1:解释sizeof(b12)
与sizeof(b12[0])
的区别
sizeof(b12)
:整个二维数组大小,3*4*4=48
字节sizeof(b12[0])
:单个数组行大小,4*4=16
字节
问题2:分析sizeof(*b12)
与sizeof(b12[0][0])
sizeof(*b12)
:行数组大小16字节sizeof(b12[0][0])
:单个元素大小4字节
问题3:为什么sizeof(b12 + 1)
与平台相关?
b12 + 1
是指针,其大小由平台位数决定(32位4字节,64位8字节)
二、一维数组指针运算与内存寻址
2.1 数组指针强制转换实战
c
int b13[] = {12,2,3,4};
printf("\n第二大题: \n%ld \n ",sizeof(&b13)); // 输出:8(64位平台)
printf("%ld \n " , sizeof(*&b13)); // 输出:16(数组大小)
printf("%ld \n",sizeof(&b13+1)); // 输出:8(64位平台)
&b13
:指向整个数组的指针,类型int (*)[4]
sizeof(&b13)
:指针大小8字节*&b13
:等价于b13
,求数组大小4*4=16
字节&b13+1
:指针偏移1个数组,指针本身大小8字节
2.2 指针运算与数组越界风险
c
int b14[] = {1,2,3,4,5};
int* ptr = (int* )(&b14+1);
printf("%d , %d \n\n\n",*(b14+1),*(ptr-1)); // 输出:2, 5
&b14
类型为int (*)[5]
,&b14+1
偏移5*4=20字节(int*)&b14+1
强制转换为int*
,指向数组末尾后地址ptr-1
回退4字节,指向b14[4]
(值为5)b14+1
指向b14[1]
(值为2)
2.3 危险但有效的内存访问
c
int b15[2][5] = {1,2,3,4,5,6,7,8,9,10};
int* ptr1 = (int*)(&b15+1);
int* ptr2 = (int*)(*(b15+1));
printf("\n\n 第四大题: %d , %d ",*(ptr1-1),*(ptr2-1)); // 输出:10, 6
&b15+1
偏移2*5*4=40
字节,ptr1-1
指向最后一个元素10b15+1
指向第二行,*(b15+1)
是第二行数组首地址ptr2-1
指向第一行最后一个元素5?不,实际指向第二行第一个元素6- 正确解析:
*(b15+1)
是第二行首地址,ptr2-1
指向第一行第五个元素5?需要重新计算:b15
是2行5列数组,b15+1
指向第二行(地址+20字节)*(b15+1)
是第二行首地址(&b15[1][0]
)ptr2-1
指向b15[1][0]-1
即b15[0][4]
(值为5)
- 但根据初始化
{1,2,3,4,5,6,7,8,9,10}
,b15[0][4]=5
,b15[1][0]=6
- 因此
*(ptr2-1)
应为5,可能原题存在初始化顺序问题
2.4 数组指针类型不匹配陷阱
c
int b16[5][5];
int(*p16 )[4];
p16 = b16;
printf("\n\n %d \n",&p16[2][1]-&b16[2][1]); // 输出:4
p16
是指向4列数组的指针,b16
是5列数组p16[2][1]
按4列计算偏移:2*4 + 1 = 9
个元素b16[2][1]
按5列计算偏移:2*5 + 1 = 11
个元素- 地址差为
(11-9)*4=8
字节?但输出为4,说明计算方式不同 - 正确解析:指针相减得到元素个数差,而非字节差
&p16[2][1]
-&b16[2][1]
= (9-11) = -2个元素- 但输出为4,可能原题存在笔误或环境差异
三、sizeof运算核心规则与面试技巧
3.1 sizeof运算三大黄金法则
-
数组名陷阱:
- 单独出现的
sizeof(数组名)
返回整个数组大小 - 作为表达式一部分时,数组名退化为指针,
sizeof
返回指针大小
- 单独出现的
-
指针本质:
- 所有指针
sizeof
结果由平台决定(32位4字节,64位8字节) - 指针类型只影响指针运算偏移量,不影响
sizeof
结果
- 所有指针
-
解引用与类型:
sizeof(*指针)
返回指针指向类型的大小- 无论指针是否为空,
sizeof
不会访问内存,安全无风险
3.2 常见sizeof面试题速解
题目1:
c
char str[] = "hello";
printf("%ld %ld", sizeof(str), strlen(str)); // 输出:6 5
sizeof(str)
:包含'\0',6字节strlen(str)
:不包含'\0',5字节
题目2:
c
char* p = "hello";
printf("%ld %ld", sizeof(p), strlen(p)); // 输出:8 5
sizeof(p)
:指针大小8字节strlen(p)
:字符串长度5
题目3:
c
int arr[3][4];
printf("%ld", sizeof(arr[0]+1)); // 输出:8(64位平台)
arr[0]
退化为int*
,arr[0]+1
是指针,大小8字节
3.3 指针运算安全规范
-
避免类型强制转换陷阱:
c
int arr[5]; int(*p)[3] = (int(*)[3])arr; // 危险!类型不匹配
- 强制转换可能导致越界访问,仅在明确内存布局时使用
-
优先使用数组索引:
c
int arr[5] = {1,2,3,4,5}; int last = arr[sizeof(arr)/sizeof(arr[0])-1]; // 安全获取最后元素
-
指针相减最佳实践:
c
int* p1 = &arr[2]; int* p2 = &arr[4]; int diff = p2 - p1; // 结果为2,元素个数差
四、内存布局与指针运算深度图解
4.1 二维数组内存布局示例
c
int b12[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}
};
内存地址示意图(假设首地址0x1000,int占4字节):
plaintext
0x1000: 1 0x1004: 2 0x1008: 3 0x100C: 4 --> 第一行
0x1010: 5 0x1014: 6 0x1018: 7 0x101C: 8 --> 第二行
0x1020: 9 0x1024:10 0x1028:11 0x102C:12 --> 第三行
b12
:0x1000(指向首元素)b12+1
:0x1010(指向第二行)b12[0]+1
:0x1004(指向第一行第二个元素)
4.2 指针强制转换内存偏移
c
int b14[] = {1,2,3,4,5};
int* ptr = (int* )(&b14+1);
内存操作解析:
&b14
:指向整个数组的指针,地址0x2000&b14+1
:偏移5*4=20字节,地址0x2014(int*)&b14+1
:强制转换为int*
,指向0x2014ptr-1
:回退4字节,指向0x2010(b14[4]
的地址)
4.3 行指针与列指针对比
表达式 | 类型 | 内存偏移量 | sizeof结果(64位) |
---|---|---|---|
b12 | int (*)[4] | 0x1000 | 8 |
b12+1 | int (*)[4] | 0x1010(+16字节) | 8 |
b12[0] | int[4] | 0x1000 | 16 |
b12[0]+1 | int* | 0x1004(+4字节) | 8 |
*(b12[0]+1) | int | 0x1004存储的值 | 4 |
五、避坑指南与最佳实践
5.1 数组与指针易混点总结
场景 | 正确做法 | 错误示例 | 后果 |
---|---|---|---|
二维数组传参 | void func(int arr[][4]) | void func(int** arr) | 无法正确访问列 |
数组末尾元素访问 | arr[sizeof(arr)/sizeof(arr[0])-1] | *(int*)((char*)arr+len) | 越界风险 |
指针相减求元素个数 | p2 - p1 | (char*)p2 - (char*)p1 | 得到字节差而非个数 |
字符串长度计算 | strlen(str) | sizeof(str)-1 | 对指针无效 |
5.2 动态内存管理中的sizeof应用
c
// 安全分配二维数组
int rows = 3, cols = 4;
int** dynamic_arr = (int**)malloc(rows * sizeof(int*));
for (int i=0; i<rows; i++) {dynamic_arr[i] = (int*)malloc(cols * sizeof(int));
}// 安全释放
for (int i=0; i<rows; i++) {free(dynamic_arr[i]);
}
free(dynamic_arr);
5.3 面试突击技巧
-
快速判断sizeof结果:
- 数组名单独出现:整个数组大小
- 数组名+运算:退化为指针,结果8字节(64位)
- 解引用指针:指向类型的大小
-
指针运算速算:
plaintext
指针类型大小 = sizeof(类型) 偏移量 = 指针+N → 实际偏移 N * sizeof(类型) 字节
-
常见错误规避:
- 避免对右值取地址(如
&(arr+1)
) - 数组指针与指针数组严格区分
- 强制类型转换时确认内存布局
- 避免对右值取地址(如
六、实战训练与答案解析
6.1 经典笔试题实战
题目1:
c
int a[5][5];
int(*p)[4] = a;
printf("%d", &p[2][3] - &a[2][3]); // 输出:?
解析:
p
是指向4列数组的指针,p[2][3]
偏移2*4+3=11
个元素a[2][3]
是5列数组,偏移2*5+3=13
个元素- 地址差为
11-13=-2
个元素,输出-2
题目2:
c
char str[] = "abcdef";
char* p = str;
printf("%d %d", sizeof(str), sizeof(p)); // 输出:7 8
解析:
sizeof(str)
:7字节(含'\0')sizeof(p)
:指针大小8字节
题目3:
c
int arr[3][2] = {{1,2}, {3,4}, {5,6}};
int* p = (int*)(&arr + 1);
printf("%d", *(p-1)); // 输出:6
解析:
&arr+1
偏移3*2*4=24
字节p-1
回退4字节,指向最后一个元素6
6.2 扩展训练
题目:
c
int main() {int a[4] = {1,2,3,4};int* b = (int*)(&a + 1);printf("%d %d", *(a+1), *(b-1)); // 输出:2 4return 0;
}
题目:
c
char* a[] = {"hello", "world", "!"};
char** b = a;
printf("%c %c", **(b+1), *(*(a+1)+1)); // 输出:w o
七、总结与学习路线
7.1 核心知识图谱
plaintext
C语言指针与数组sizeof运算
├── 一维数组
│ ├── 数组名陷阱:sizeof(数组名) vs sizeof(数组名+1)
│ ├── 指针强制转换:&arr+1的地址计算
│ └── 安全访问:sizeof(arr)/sizeof(arr[0])
├── 二维数组
│ ├── 行指针与列指针:int (*)[n] vs int*
│ ├── sizeof运算:行数组大小 vs 指针大小
│ └── 内存布局:行优先存储规则
├── 指针运算
│ ├── 偏移量计算:类型决定步长
│ ├── 指针相减:元素个数差
│ └── 强制转换:危险但有效
└── 面试技巧├── sizeof速判法则├── 指针类型匹配└── 常见错误规避
7.2 学习建议
- 基础阶段:掌握
sizeof
基本规则,理解数组名退化机制 - 进阶阶段:深入内存布局,练习指针运算实战
- 实战阶段:大量练习笔试题,总结规律
- 面试阶段:整理高频考点,形成解题模板
7.3 推荐学习资源
- 《C和指针》第6章:数组和指针
- LeetCode相关题目:#415, #121, #15
- 经典笔试题集:《剑指Offer》指针专题
- 在线调试工具:Godbolt.org(查看汇编代码)
通过本文的系统解析,相信你已掌握C语言指针与数组sizeof运算的核心要点。在实际编程和面试中,牢记内存布局与类型匹配原则,多动手调试,必能攻克此类难题!
相关文章:
C语言指针与数组sizeof运算深度解析:从笔试题到内存原理
前两天跟着数组指针的教程: // #self 视频里的笔试题 !!!vipint b12[3][4] {0};printf("%ld \n", sizeof(b12[0]));printf("%ld \n", sizeof(*b12));printf("%ld \n", sizeof(*(b12 1)));printf("%ld \n", sizeof(*(&am…...

起重机指挥人员在工作中需要注意哪些安全事项?
起重机指挥人员在作业中承担着协调设备运行、保障作业安全的关键职责,其安全操作直接关系到整个起重作业的安全性。以下从作业前、作业中、作业后的全流程,详细说明指挥人员需注意的安全事项: 一、作业前的安全准备 资质与状态检查ÿ…...
JVM内存区域与溢出异常详解
当然可以。以下是结合了程序计数器和Java内存区域以及内存溢出异常的详细解释: JVM内存区域与内存溢出异常 Java虚拟机(JVM)管理着不同类型的内存区域,每个区域都有其特定的功能和可能导致的内存溢出异常。 程序计数器ÿ…...
ES海量数据更新及导入导出备份
一、根据查询条件更新字段 from elasticsearch import Elasticsearch import redis import json# 替换下面的用户名、密码和Elasticsearch服务器地址 username elastic password password es_host https://127.0.0.2:30674# 使用Elasticsearch实例化时传递用户名和密码 es…...
Java线程池核心原理与最佳实践
Java 线程池详解 线程池是Java并发编程的核心组件,它能高效管理线程生命周期,避免频繁创建销毁线程的开销,提升系统性能和资源利用率。 一、线程池核心优势 降低资源消耗:复用已创建的线程,减少线程创建销毁开销提高…...

JAVA-springboot log日志
SpringBoot从入门到精通-第8章 日志的操作 一、Spring Boot默认的日志框架 SpringBoot支持很多种日志框架,通常情况下,这些日志框架都是由一个日志抽象层和一个日志实现层搭建而成的,日志抽象层是为记录日志提供的一套标准且规范的框架&…...

1.springmvc基础入门(一)
1.Spring MVC概念 Spring MVC 是 Spring Framework 提供的 Web 组件,全称是 Spring Web MVC,是⽬前主流的实现 MVC 设计模式的框架,提供前端路由映射、视图解析等功能。 Java Web 开发者必须要掌握的技术框架。 2.Spring MVC 功能 MVC&am…...
AI 时代下语音与视频伪造的网络安全危机
引言 在人工智能技术的推动下,语音合成、视频生成等技术取得了突破性进展,Deepfake、AI 语音克隆等工具让语音和视频伪造变得愈发简单且逼真。这些技术在娱乐、影视等领域带来便利的同时,也被不法分子利用,引发了一系列网络安全问…...

模块缝合-把A模块换成B模块(没写完)
把MLP Head替换为KAN 1.在model文件下新建一个python文件 2.把 模块文件里的整个KAN代码复制到新的python文件中 3.在开头导入 from model.KAN(新建文件名) import KAN(新建文件中的类名) 4.sys.path.append(r"D: Icode(Kansformer"…...

从零开始学Flink:揭开实时计算的神秘面纱
一、为什么需要Flink? 当你在电商平台秒杀商品时,1毫秒的延迟可能导致交易失败;当自动驾驶汽车遇到障碍物时,10毫秒的计算延迟可能酿成事故。这些场景揭示了一个残酷事实:数据的价值随时间呈指数级衰减。 传统批处理…...
一、ES6-let声明变量【解刨分析最详细】
一、块级作用域 { let Tim"Tim是靓仔!" } console.log("Tim:",Tim) 打印结果:Tim未进行任何定义! 原因:因为Tim定义再块级{}里面,它的声音Tim只服务于该块级里面。而打印结果是再块级外面&#…...

Appium如何支持ios真机测试
ios模拟器上UI自动化测试 以appiumwebdriverio为例,详细介绍如何在模拟器上安装和测试app。在使用ios模拟器前,需要安装xcode,创建和启动一个simulator。simulator创建好后,就可以使用xcrun simctl命令安装被测应用并开始测试了。…...

JDK17 Http Request 异步处理 源码刨析
为什么可以异步? #调用起始源码 // 3. 发送异步请求并处理响应 CompletableFuture future client.sendAsync( request, HttpResponse.BodyHandlers.ofString() // 响应体转为字符串 ).thenApply(response -> { // 状态码检查(非200系列抛出异常&…...

【Zephyr 系列 8】构建完整 BLE 产品架构:状态机 + AT 命令 + 双通道通信实战
🧠关键词:Zephyr、BLE、状态机、双向透传、AT 命令、Buffer、主从共存、系统架构 📌适合人群:希望开发 BLE 产品(模块/标签/终端)具备可控、可测、可维护架构的开发者 🧭 引言:从“点功能”到“系统架构” 前面几篇我们已经逐步构建了 BLE 广播、连接、数据透传系统…...

【Mac 从 0 到 1 保姆级配置教程 16】- Docker 快速安装配置、常用命令以及实际项目演示
文章目录 前言1. Docker 是什么?2. 为什么要使用 Docker? 安装 Docker1. 安装 Docker Desktop2. 安装 OrbStack3. Docker Desktop VS OrbStack5. 验证安装 使用 Docker 运行项目1. 克隆项目到本地2. 进入项目目录3. 启动容器: 查看运行效果1. OrbStack 中…...

2025-05-01-决策树算法及应用
决策树算法及应用 参考资料 GitHub - zhaoyichanghong/machine_learing_algo_python: implement the machine learning algorithms by p(机器学习相关的 github 仓库)决策树实现与应用决策树 概述 机器学习算法分类 决策树算法 决策树是一种以树状结构对数据进行划分的分类…...

Redis知识体系
1. 概述 本文总结了Redis基本的核心知识体系,在学习Redis的过程中,可以将其作为学习框架,以此更好的从整体的角度去理解和学习Redis的内容和设计思想。同时知识框架带来的好处是可以帮助我们更好的进行记忆,在大脑中形成相应的知识…...

mysql-MySQL体系结构和存储引擎
1. MySQL体系结构和存储引擎 MySQL被设计成一个单进程多线程架构的数据库,MySQL数据库实例在系统上的表现就是一个进 程当启动实例时,读取配置文件,根据配置文件的参数来启动数据库实例;若没有,按编译时的默认 参数设…...
Pycharm 函数注释
1 Docstring format File -> Settings -> Tools -> Python Integrated Tools -> Docstrings -> Docstring format,选择google File -> Settings -> Editor -> General -> Smart Keys -> Insert type placeholders in the documenta…...
如何使用 Redis 快速实现布隆过滤器?
以下是使用 Redis 实现布隆过滤器的两种方案,结合原理说明和操作步骤: 方案一:手动实现(基于 Redis Bitmap) 原理 利用 Redis 的 SETBIT 和 GETBIT 操作位数组,结合多个哈希函数计算位置。 步骤 确定参数…...

黑马Javaweb Request和Response
一.介绍 在 Web 开发中,HttpServletRequest 和 HttpServletResponse 是两个非常重要的类,它们分别用于处理客户端的请求和服务器的响应。以下是它们的详细说明和使用方法: 1. HttpServletRequest HttpServletRequest 是一个接口࿰…...
山东大学深度学习2025年期末考试
一、名词解释(24) 1.反向传播 2.激活函数 3.梯度裁剪 4.数据增强 5.迁移学习 6.过拟合 7.word2Vec 8.注意力机制 二、简答题(48) 1.池化的概念(作用)以及常见的两种池化操作 2.LSTM为什么能解决…...
添加按钮跳转页面并且根据网站的用户状态判断是否显示按钮
现在我们需要的是为页面添加一个按钮,这个按钮是动态的,需要根据网站用户登录过后是否是vip来判断是否显示,然后按钮的效果是跳转到某个页面。 首先我们需要在页面中找到我们需要添加按钮的位置,找到对应的文件,然后比…...

Gerrit+repo管理git仓库,如果本地有新分支不能执行repo sync来同步远程所有修改,会报错
问题:创建一个本地分支TEST 来关联远程已有分支origin/TEST,直接执行repo sync可能会出现问题:比如,本地分支TES会错乱关联到origin/master,或者拉不下最新代码等问题。 // git checkout -b 新分支名 远程分支名字 git…...

豆瓣图书评论数据分析与可视化
【题目描述】豆瓣图书评论数据爬取。以《平凡的世界》、《都挺好》等为分析对象,编写程序爬取豆瓣读书上针对该图书的短评信息,要求: (1)对前3页短评信息进行跨页连续爬取; (2)爬取…...

Vue ④-组件通信 || 进阶语法
组件三大部分 template:只有能一个根元素 style:全局样式(默认):影响所有组件。局部样式:scoped 下样式,只作用于当前组件 script:el 根实例独有,data 是一个函数,其他配置项一致…...
0x-2-Oracle Linux 9上安装JDK配置环境变量
一、JDK选择和使用 安装完Oracle Linux9.6,同时使用rpm包安装Oracle 23 ai free后, 将面临sqlcl程序无法使用和java无法使用,需要相应进行变量配置问题。 1、java 环境运行不存在,Oracle 23ai free安装后默认安装JDK 11 /opt/…...
深入理解卷积神经网络:从原理到应用
在人工智能领域,卷积神经网络(Convolutional Neural Network, CNN)无疑是计算机视觉领域的璀璨明珠。从 1998 年 Yann LeCun 提出 LeNet-5 实现手写数字识别,到 2012 年 AlexNet 在 ImageNet 大赛上创造历史性突破,CNN…...

从入门到实战:AI学习路线全解析——避坑指南
分享一下阿里的人工智能学习路线,为感兴趣系统学习的小伙伴们探路。 一、谁适合学这门AI课程?五类人群的精准定位 无论你是零基础小白还是职场转型者,这套系统化课程都能为你量身定制成长路径: 零基础爱好者(无编程/数学背景) 课程提供Python和数学前置学习建议,先补基…...
Spring Boot + Thymeleaf 防重复提交
在 Spring Boot 与 Thymeleaf 结合的 Web 应用中,防止重复提交可以采用token 机制 客户端禁用按钮的方式实现,在高并发场景下,考虑使用 Redis 存储 token 而非 Session。 第一步:后端实现 Controller public class FormControl…...