《C 语言 sizeof 与 strlen 深度对比:原理、差异与实战陷阱》
目录
一. sizeof 和 strlen 的对比
1.1 sizeof
1.2 strlen
1.3 对比表格
二. 数组和指针笔试题解析
2.1 一维数组
2.2 字符数组
2.2.1 代码练习一
2.2.2 代码练习二
2.2.3 代码练习三
2.2.4 代码练习四
2.2.5 代码练习五
2.2.6 代码练习六
2.3 二维数组
三. 指针运算笔试题解析
3.1 试题练习一
3.2 试题练习二
3.3 试题练习三
3.4 试题练习四
3.5 试题练习五
3.6 试题练习六
3.7 试题练习七
一. sizeof 和 strlen 的对比
1.1 sizeof
sizeof计算变量所占内存空间的大小,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。
sizeof 只关注占用内存空间的大小,不在乎内存中存放什么数据;并且sizeof中表达式不计算
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int a = 10;printf("sizeof(a): %d\n", sizeof(a));printf("sizeof(a+3.14): %d\n", sizeof(a+3.14));printf("sizeof(int): %d\n", sizeof(int));return 0;
}
通过运行该代码可以得到
其中a是int类型 占4个字节
a+3.14 由于a算术提升到了double类型 因此占8个字节
1.2 strlen
strlen是C语言库函数,功能是求字符串长度
- 计算字符串的实际长度(不包含’\0’)
- 必须接收以’\0’结尾的有效字符串指针
strlen("hello"); // 返回5
- 遇到第一个’\0’停止计数
strlen("hel\0lo"); // 返回3
练习strlen函数 请自己思考一下 下面函数会打印那些值
#include <stdio.h>
#include<string.h>
int main()
{char arr1[3] = { 'a', 'b', 'c' };char arr2[] = "abc";printf("%d\n", strlen(arr1));printf("%d\n", strlen(arr2));printf("%d\n", sizeof(arr1));printf("%d\n", sizeof(arr2));return 0;
}
运行结果如下
其中由于arr1并没有 ' \0 ' 结尾 因此打印随机值 35
1.3 对比表格
特性 | sizeof 运算符 | strlen 函数 |
---|---|---|
类型 | 编译时运算符(非函数) | 运行时函数(来自 <string.h> ) |
作用对象 | 变量、类型或表达式 | 仅适用于以 \0 结尾的字符串 |
计算时机 | 编译时确定 | 运行时计算 |
返回值 | 对象/类型占用的内存字节数(含 \0 ) | 字符串长度(不含 \0 ) |
参数示例 | sizeof(int) , sizeof(arr) | strlen("hello") |
对指针的行为 | 返回指针本身的大小(通常4/8字节) | 计算指针指向的字符串长度 |
对数组的行为 | 返回整个数组的字节大小 | 将数组退化为指针后计算长度 |
对字符串字面量 | 包含 \0 的总大小(如 "abc" 返回4) | 不包含 \0 的长度(如 "abc" 返回3) |
时间复杂度 | O(1)(编译时完成) | O(n)(需遍历字符串到 \0 ) |
二. 数组和指针笔试题解析
首先我们先再次明确数组名的意义:
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组所占内存的大小。
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
- 除此之外所有的数组名都表示首元素的地址。
2.1 一维数组
首先请思考一下 下面这段代码将会打印什么
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{int a[] = { 1,2,3,4 };printf("%zu\n", sizeof(a));printf("%zu\n", sizeof(a + 0));printf("%zu\n", sizeof(*a));printf("%zu\n", sizeof(a + 1));printf("%zu\n", sizeof(a[1]));printf("%zu\n", sizeof(&a));printf("%zu\n", sizeof(*&a));printf("%zu\n", sizeof(&a + 1));printf("%zu\n", sizeof(&a[0]));printf("%zu\n", sizeof(&a[0] + 1));return 0;
}
思考完了请验证你的答案是否正确
典型输出(64位系统):
16 // 整个数组大小
8 // 指针大小
4 // int大小
8 // 指针大小
4 // int大小
8 // 指针大小
16 // 整个数组大小
8 // 指针大小
8 // 指针大小
8 // 指针大小
注释讲解如下
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{int a[] = { 1,2,3,4 }; // 定义一个包含4个int元素的数组printf("%zu\n", sizeof(a)); // 1. 整个数组的大小:4个int × 4字节/int = 16字节printf("%zu\n", sizeof(a + 0)); // 2. 数组名a在表达式中退化为指针,a+0是指向第一个元素的指针,指针大小通常为4或8字节printf("%zu\n", sizeof(*a)); // 3. 解引用数组名得到第一个元素,int类型大小为4字节printf("%zu\n", sizeof(a + 1)); // 4. 数组名a退化为指针,a+1是指向第二个元素的指针,指针大小printf("%zu\n", sizeof(a[1])); // 5. 第二个元素的大小,int类型为4字节printf("%zu\n", sizeof(&a)); // 6. &a是"指向整个数组的指针",但仍然是指针,指针大小printf("%zu\n", sizeof(*&a)); // 7. 解引用数组指针得到整个数组,大小为16字节printf("%zu\n", sizeof(&a + 1)); // 8. 指向数组后面位置的指针,指针大小printf("%zu\n", sizeof(&a[0])); // 9. 指向第一个元素的指针,指针大小printf("%zu\n", sizeof(&a[0] + 1)); // 10. 指向第二个元素的指针,指针大小return 0;
}
关键点说明:
- 数组名在大多数情况下会退化为指针,但在
sizeof(a)
和&a
操作中不会退化。- 指针的大小取决于系统架构(32位系统通常4字节,64位系统通常8字节)。
&a
是"指向整个数组的指针",类型是int(*)[4]
,但它的值仍然是数组的起始地址。*&a
等价于a
,所以sizeof(*&a)
就是整个数组的大小。- 数组元素的地址运算(如
a+1
、&a[0]+1
)得到的是指针,不是数组。
2.2 字符数组
2.2.1 代码练习一
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{char arr[] = { 'a','b','c','d','e','f' };printf("%zu\n", sizeof(arr));printf("%zu\n", sizeof(arr + 0));printf("%zu\n", sizeof(*arr));printf("%zu\n", sizeof(arr[1]));printf("%zu\n", sizeof(&arr));printf("%zu\n", sizeof(&arr + 1));printf("%zu\n", sizeof(&arr[0] + 1));
}
典型输出(64位系统):
6 // 整个数组大小
8 // 指针大小
1 // char大小
1 // char大小
8 // 指针大小
8 // 指针大小
8 // 指针大小
注释解析如下
#include<stdio.h>
int main()
{char arr[] = { 'a','b','c','d','e','f' }; // 定义一个包含6个char元素的字符数组printf("%zu\n", sizeof(arr)); // 1. 整个数组的大小:6个char × 1字节/char = 6字节printf("%zu\n", sizeof(arr + 0)); // 2. 数组名arr在表达式中退化为指针,arr+0是指向第一个元素的指针,指针大小通常为4或8字节printf("%zu\n", sizeof(*arr)); // 3. 解引用数组名得到第一个元素,char类型大小为1字节printf("%zu\n", sizeof(arr[1])); // 4. 第二个元素的大小,char类型为1字节printf("%zu\n", sizeof(&arr)); // 5. &arr是"指向整个数组的指针",但仍然是指针,指针大小printf("%zu\n", sizeof(&arr + 1)); // 6. 指向数组后面位置的指针,指针大小printf("%zu\n", sizeof(&arr[0] + 1)); // 7. 指向第二个元素的指针,指针大小return 0;
}
关键点说明:
- 数组名在大多数情况下会退化为指针,但在
sizeof(arr)
和&arr
操作中不会退化。- 指针的大小取决于系统架构(32位系统通常4字节,64位系统通常8字节)。
&arr
是"指向整个数组的指针",类型是char(*)[6]
,但它的值仍然是数组的起始地址。- 字符数组
arr
的大小是6字节,因为每个char
类型占用1字节。- 数组元素的地址运算(如
arr+0
、&arr[0]+1
)得到的是指针,不是数组。
2.2.2 代码练习二
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main()
{char arr[] = { 'a','b','c','d','e','f' };printf("%zu\n", strlen(arr));printf("%zu\n", strlen(arr + 0));printf("%zu\n", strlen(&arr));printf("%zu\n", strlen(&arr + 1));printf("%zu\n", strlen(&arr[0] + 1));
}
典型输出(64位系统):
36//随机值
36//随机值
36//随机值
30//随机值-6
35//随机值-1
注释解析如下:
int main()
{// 定义并初始化一个字符数组,注意没有包含字符串终止符'\0'char arr[] = { 'a','b','c','d','e','f' };// 1. 计算从数组首元素开始的"字符串"长度printf("%zu\n", strlen(arr));// arr作为数组名,在表达式中退化为指向首元素的指针// strlen会从'a'开始向后查找'\0',但数组中没有'\0'// 会继续访问数组后面的内存,直到偶然遇到'\0'// 结果是不可预测的随机值,且可能导致未定义行为// 2. 计算从数组首元素开始的"字符串"长度printf("%zu\n", strlen(arr + 0));// arr + 0 仍然指向数组首元素// 情况与第一个printf完全相同// 结果也是随机值,且可能与第一个printf相同// 3. 计算从数组地址开始的"字符串"长度printf("%zu\n", strlen(&arr));// &arr 是"指向整个数组的指针"(类型为 char(*)[6])// 但它的值与arr相同,都是数组的起始地址// 情况与前两个printf相同,结果也是随机值// 4. 计算从数组末尾后一个位置开始的"字符串"长度printf("%zu\n", strlen(&arr + 1));// &arr + 1 跳过整个数组(6个字节),指向数组之后的内存位置// strlen从这个新位置开始查找'\0'// 结果也是随机值,但通常比前几个结果小6(因为跳过了6个字符)// 注意这仍然是未定义行为// 5. 计算从数组第二个元素开始的"字符串"长度printf("%zu\n", strlen(&arr[0] + 1));// &arr[0] + 1 指向数组的第二个元素'b'// 从这个位置开始查找'\0'// 结果也是随机值,但通常比第一个结果小1// 同样属于未定义行为return 0;
}
关键点总结:
字符串终止符的重要性:
strlen
依赖\0
来确定字符串结束位置- 这个数组没有以
\0
结尾,导致strlen
会越界访问指针运算:
arr
和arr+0
都指向首元素&arr
是整个数组的指针(类型不同但值相同)&arr + 1
跳过整个数组&arr[0] + 1
指向第二个元素
2.2.3 代码练习三
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{char arr[] = "abcdef";printf("%zu\n", sizeof(arr));printf("%zu\n", sizeof(arr + 0));printf("%zu\n", sizeof(*arr));printf("%zu\n", sizeof(arr[1]));printf("%zu\n", sizeof(&arr));printf("%zu\n", sizeof(&arr + 1));printf("%zu\n", sizeof(&arr[0] + 1));
}
典型输出(64位系统):
7 // 整个数组大小(包括'\0')
8 // 指针大小
1 // char大小
1 // char大小
8 // 指针大小
8 // 指针大小
8 // 指针大小
注释解析如下:
#include<stdio.h>
int main()
{char arr[] = "abcdef"; // 定义一个字符串数组,包含6个字符和1个'\0',共7个元素printf("%zu\n", sizeof(arr)); // 1. 整个数组的大小:7个char × 1字节/char = 7字节// 注意:字符串字面量"abcdef"会自动添加'\0',所以数组大小是7不是6printf("%zu\n", sizeof(arr + 0)); // 2. arr在表达式中退化为指针,arr+0是指向第一个元素的指针// 指针大小通常为4(32位)或8(64位)字节printf("%zu\n", sizeof(*arr)); // 3. 解引用数组名得到第一个元素'a',char类型大小为1字节printf("%zu\n", sizeof(arr[1])); // 4. 第二个元素'b'的大小,char类型为1字节printf("%zu\n", sizeof(&arr)); // 5. &arr是"指向整个数组的指针",指针大小printf("%zu\n", sizeof(&arr + 1)); // 6. 指向数组后面位置的指针,指针大小printf("%zu\n", sizeof(&arr[0] + 1)); // 7. 指向第二个元素的指针,指针大小return 0;
}
关键点说明:
字符串数组的特殊性:
char arr[] = "abcdef"
会创建一个包含7个元素的数组:‘a’,‘b’,‘c’,‘d’,‘e’,‘f’,‘\0’- 所以
sizeof(arr)
是7而不是6数组名退化为指针:
- 在大多数表达式中,数组名会退化为指向其首元素的指针
- 但在
sizeof(arr)
和&arr
操作中不会退化指针大小:
- 32位系统:通常4字节
- 64位系统:通常8字节
- 示例输出假设是64位系统
指针类型差异:
arr+0
和&arr[0]+1
是char*
类型&arr
和&arr+1
是char(*)[7]
类型(指向整个数组的指针)- 但所有指针的
sizeof
结果相同
2.2.4 代码练习四
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main()
{char arr[] = "abcdef";printf("%zu\n", strlen(arr));printf("%zu\n", strlen(arr + 0));printf("%zu\n", strlen(&arr));printf("%zu\n", strlen(&arr + 1));printf("%zu\n", strlen(&arr[0] + 1));
}
典型输出(64位系统):
6
6
6
26
5
注释解析如下:
int main()
{// 定义并初始化一个字符串数组,包含6个字符和1个'\0'终止符char arr[] = "abcdef"; // 等价于 {'a','b','c','d','e','f','\0'}// 1. 计算字符串长度printf("%zu\n", strlen(arr)); // 输出6// arr作为数组名,在表达式中退化为指向首元素的指针// strlen从'a'开始向后查找'\0',在'f'后找到'\0'// 计算结果是6个字符// 2. 计算从首元素开始的字符串长度printf("%zu\n", strlen(arr + 0)); // 输出6// arr + 0 仍然指向数组首元素// 情况与第一个printf完全相同// 结果也是6// 3. 计算从数组地址开始的字符串长度printf("%zu\n", strlen(&arr)); // 输出6// &arr 是"指向整个数组的指针"(类型为 char(*)[7])// 但它的值与arr相同,都是数组的起始地址// 情况与前两个printf相同,结果也是6// 4. 计算从数组末尾后一个位置开始的字符串长度printf("%zu\n", strlen(&arr + 1)); // 未定义行为// &arr + 1 跳过整个数组(7个字节),指向数组之后的内存位置// strlen从这个新位置开始查找'\0'// 这是未定义行为,可能返回随机值或导致程序崩溃// 5. 计算从数组第二个元素开始的字符串长度printf("%zu\n", strlen(&arr[0] + 1)); // 输出5// &arr[0] + 1 指向数组的第二个元素'b'// 从这个位置开始查找'\0',会找到'c','d','e','f','\0'// 结果是5个字符return 0;
}
关键点总结:
字符串终止符:
- 字符串"abcdef"实际上包含7个字节:6个字符+1个’\0’
strlen
计算的是’\0’前的字符数量指针运算:
arr
和arr+0
都指向首元素&arr
是整个数组的指针(类型不同但值相同)&arr + 1
跳过整个数组(7个字节)&arr[0] + 1
指向第二个元素安全与未定义行为:
- 前4个
printf
都是安全的,因为都在数组范围内查找’\0’strlen(&arr + 1)
是未定义行为,因为它访问了数组外的内存结果分析:
- 前三个输出都是6(完整字符串长度)
- 第五个输出是5(从第二个字符开始计算)
- 第四个输出不可预测
2.2.5 代码练习五
#include<stdio.h>
int main()
{char* p = "abcdef";printf("%zu\n", sizeof(p));printf("%zu\n", sizeof(p + 1));printf("%zu\n", sizeof(*p));printf("%zu\n", sizeof(p[0]));printf("%zu\n", sizeof(&p));printf("%zu\n", sizeof(&p + 1));printf("%zu\n", sizeof(&p[0] + 1));
}
典型输出(64位系统):
8 // 指针大小
8 // 指针大小
1 // char大小
1 // char大小
8 // 指针大小
8 // 指针大小
8 // 指针大小
注释解析如下:
#include<stdio.h>
int main()
{char* p = "abcdef"; // 定义一个字符指针p,指向字符串常量"abcdef"printf("%zu\n", sizeof(p)); // 1. 指针p本身的大小:在32位系统为4字节,64位系统为8字节printf("%zu\n", sizeof(p + 1)); // 2. 指针运算结果的大小:p+1是指向第二个字符的指针,大小与p相同printf("%zu\n", sizeof(*p)); // 3. 解引用指针得到字符'a'的大小:char类型为1字节printf("%zu\n", sizeof(p[0])); // 4. 等同于*(p+0),即第一个字符'a'的大小:1字节printf("%zu\n", sizeof(&p)); // 5. 取指针p的地址,得到指向指针的指针:大小仍为指针大小printf("%zu\n", sizeof(&p + 1)); // 6. 指针p的地址加1,指向下一个指针位置:大小仍为指针大小printf("%zu\n", sizeof(&p[0] + 1)); // 7. 等同于&(*(p+0))+1,即指向第二个字符的指针:大小仍为指针大小return 0;
}
关键点说明:
指针大小:所有指针的大小相同,取决于系统架构(32位系统4字节,64位系统8字节)
字符串常量:
"abcdef"
是字符串常量,存储在只读内存区域,以’\0’结尾指针运算:
p + 1
:指向字符串的第二个字符’b’&p + 1
:指向内存中下一个指针位置&p[0] + 1
:等同于p + 1
,指向第二个字符解引用操作:
*p
和p[0]
都得到第一个字符’a’,大小为1字节
2.2.6 代码练习六
#include<stdio.h>
#include<string.h>
int main()
{char* p = "abcdef";printf("%zu\n", strlen(p));printf("%zu\n", strlen(p + 1));printf("%zu\n", strlen(&p));printf("%zu\n", strlen(&p + 1));printf("%zu\n", strlen(&p[0] + 1));return 0;
}
运行结果如下:
6 // strlen(p)
5 // strlen(p+1)
随机值或崩溃 // strlen(&p)
随机值或崩溃 // strlen(&p+1)
5 // strlen(&p[0]+1)
注释解析如下
#include<stdio.h>
#include<string.h> int main()
{// 定义一个字符指针p,指向字符串常量"abcdef"// 字符串常量会自动以'\0'结尾char* p = "abcdef";// 1. 计算字符串p的长度printf("%zu\n", strlen(p));// p指向字符串"abcdef"的首地址// strlen从'a'开始计算,直到遇到'\0'为止// 结果是6(a b c d e f 共6个字符)// 2. 计算从p+1位置开始的字符串长度printf("%zu\n", strlen(p + 1));// p+1指向字符串的第二个字符'b'// strlen从'b'开始计算,直到'\0'// 结果是5(b c d e f 共5个字符)// 3. 计算从指针p的地址开始的"字符串"长度printf("%zu\n", strlen(&p));// &p是指针变量p本身的地址,不是字符串地址// strlen会将指针地址当作字符数组起始地址// 这是未定义行为,结果不可预测// 可能返回随机值或导致程序崩溃// 4. 计算从&p+1位置开始的"字符串"长度printf("%zu\n", strlen(&p + 1));// &p+1是指向p变量之后的内存位置// 同样不是有效的字符串地址// 未定义行为,结果不可预测// 5. 计算从第二个字符地址开始的字符串长度printf("%zu\n", strlen(&p[0] + 1));// &p[0]是字符串首字符'a'的地址// &p[0]+1是第二个字符'b'的地址// 等同于p+1,结果是5return 0;
}
关键点总结:
字符串常量:
"abcdef"
是字符串常量,自动以\0
结尾strlen
可以正确计算其长度指针操作:
p
指向字符串首地址p+1
指向第二个字符&p
是指针变量的地址,不是字符串地址安全与危险操作:
- 安全操作:
p
和p+1
等指向字符串内部的指针- 危险操作:
&p
和&p+1
不是字符串地址,会导致未定义行为
2.3 二维数组
#include<stdio.h>
int main()
{int a[3][4] = { 0 };printf("%zu\n", sizeof(a));printf("%zu\n", sizeof(a[0][0]));printf("%zu\n", sizeof(a[0]));printf("%zu\n", sizeof(a[0] + 1));printf("%zu\n", sizeof(*(a[0] + 1)));printf("%zu\n", sizeof(a + 1));printf("%zu\n", sizeof(*(a + 1)));printf("%zu\n", sizeof(&a[0] + 1));printf("%zu\n", sizeof(*(&a[0] + 1)));printf("%zu\n", sizeof(*a));printf("%zu\n", sizeof(a[3]));
}
典型输出(64位系统):
48 // 整个数组
4 // 单个元素
16 // 一行
8 // 指针
4 // 元素
8 // 指针
16 // 一行
8 // 指针
16 // 一行
16 // 一行
16 // 一行(编译时计算)
注释解析如下:
#include<stdio.h>
int main()
{int a[3][4] = { 0 }; // 定义一个3行4列的二维整型数组printf("%zu\n", sizeof(a)); // 1. 整个数组的大小:3×4×4字节 = 48字节printf("%zu\n", sizeof(a[0][0]));// 2. 单个元素的大小:int类型 = 4字节printf("%zu\n", sizeof(a[0])); // 3. 第一行的大小:4个int = 16字节// a[0]是第一行的数组名,不会退化为指针printf("%zu\n", sizeof(a[0] + 1));// 4. 第一行第二个元素的地址:指针大小 = 8字节// a[0]退化为指针,+1指向下一个元素printf("%zu\n", sizeof(*(a[0] + 1)));// 5. 第一行第二个元素的大小:int = 4字节printf("%zu\n", sizeof(a + 1)); // 6. 第二行的地址:指针大小 = 8字节// a退化为指向第一行的指针,+1指向第二行printf("%zu\n", sizeof(*(a + 1)));// 7. 第二行的大小:4个int = 16字节// *(a+1)等价于a[1]printf("%zu\n", sizeof(&a[0] + 1));// 8. 第二行的地址:指针大小 = 8字节// &a[0]是第一行的地址,+1指向第二行printf("%zu\n", sizeof(*(&a[0] + 1)));// 9. 第二行的大小:4个int = 16字节// *(&a[0]+1)等价于a[1]printf("%zu\n", sizeof(*a)); // 10. 第一行的大小:4个int = 16字节// *a等价于a[0]printf("%zu\n", sizeof(a[3])); // 11. 第四行的大小:4个int = 16字节// 虽然a只有3行,但sizeof是编译时操作// 不会实际访问a[3]return 0;
}
关键点说明:
二维数组的内存布局:
int a[3][4]
在内存中是按行连续存储的12个int。数组名退化规则:
a
在大多数情况下退化为指向第一行的指针(类型为int(*)[4]
)a[0]
在大多数情况下退化为指向第一个元素的指针(类型为int*
)sizeof的特殊性:
- 在
sizeof(a)
和sizeof(a[0])
中,数组名不会退化为指针sizeof
是编译时操作,不会实际计算表达式指针运算:
a + 1
和&a[0] + 1
都指向第二行a[0] + 1
指向第一行的第二个元素
三. 指针运算笔试题解析
注: 以下试题请先思考
3.1 试题练习一
#include <stdio.h>
int main()
{int a[5] = { 1, 2, 3, 4, 5 };int* ptr = (int*)(&a + 1); // 关键点1printf("%d,%d", *(a + 1), *(ptr - 1)); // 关键点2return 0;
}
关键点解析:
&a + 1
:
a
是一个int数组,&a
的类型是int (*)[5]
(指向包含5个int的数组的指针)- 当对数组指针进行
+1
运算时,会跳过整个数组的大小(5*sizeof(int))- 所以
&a + 1
指向数组末尾之后的位置指针转换:
(int*)(&a + 1)
将这个数组指针强制转换为普通的int指针- 现在
ptr
是一个指向int的指针,指向数组末尾之后的位置输出表达式:
*(a + 1)
:
a
在表达式中退化为指向首元素的指针(int*)a + 1
指向第二个元素(值为2)- 所以
*(a + 1)
输出2
*(ptr - 1)
:
ptr
指向数组末尾之后的位置ptr - 1
回退一个int的大小,指向最后一个元素5- 所以
*(ptr - 1)
输出5
3.2 试题练习二
#include<stdio.h>
struct Test
{int Num;char* pcName;short sDate;char cha[2];short sBa[4];
}*p = (struct Test*)0x100000; // 初始化结构体指针p指向地址0x100000int main()
{printf("%p\n", p + 0x1); // 关键点1printf("%p\n", (unsigned long)p + 0x1); // 关键点2printf("%p\n", (unsigned int*)p + 0x1); // 关键点3return 0;
}
关键点解析:
结构体大小计算:
struct Test
的大小取决于内存对齐(假设在32位系统上):
int Num
:4字节char* pcName
:4字节(指针)short sDate
:2字节char cha[2]
:2字节short sBa[4]
:8字节(4个short)- 总大小:4 + 4 + 2 + 2 + 8 = 20字节(假设编译器没有额外填充)
p + 0x1
:
p
是struct Test*
类型,指针算术以结构体大小为步长p + 1
会跳过整个结构体的大小(20字节)- 所以
p + 0x1
=0x100000
+0x14
(20的十六进制)=0x100014
(unsigned long)p + 0x1
:
- 将指针
p
强制转换为unsigned long
,变成普通整数- 整数加法直接加1:
0x100000
+0x1
=0x100001
(unsigned int*)p + 0x1
:
- 将
p
强制转换为unsigned int*
(指向4字节int的指针)- 指针算术以
unsigned int
大小(4字节)为步长(unsigned int*)p + 1
=0x100000
+0x4
=0x100004
3.3 试题练习三
#include <stdio.h>
int main()
{int a[3][2] = { (0, 1), (2, 3), (4, 5) }; // 关键点1:注意这里用的是逗号表达式int* p;p = a[0]; // 关键点2:获取第一行的首地址printf("%d", p[0]); // 关键点3:输出p[0]return 0;
}
关键点解析:
数组初始化:
- 表面上看是初始化一个3行2列的二维数组
- 但实际上使用的是逗号表达式而不是常规的花括号初始化
- 在C语言中,
(x, y)
是逗号表达式,其值为最后一个表达式的值- 所以实际初始化值为:
(0, 1)
→ 1(2, 3)
→ 3(4, 5)
→ 5- 由于只提供了3个值,但数组需要6个值(3×2),剩余元素会被初始化为0
- 实际数组内容为:
a[0][0] = 1 a[0][1] = 3 a[1][0] = 5 a[1][1] = 0 (自动补0) a[2][0] = 0 (自动补0) a[2][1] = 0 (自动补0)
指针赋值:
a[0]
表示二维数组的第一行(即{1, 0}
)p = a[0]
将p指向第一行的第一个元素- 输出
p[0]
:
p[0]
等价于*(p+0)
,即第一个元素的值- 根据上面的初始化,
a[0][0]
的值为1
3.4 试题练习四
#include <stdio.h>
int main()
{int a[5][5]; // 定义一个5x5的二维数组int(*p)[4]; // 定义一个指向包含4个int的数组的指针p = a; // 关键点1:将a强制转换为指向4元素数组的指针printf("%p,%d\n", &p[4][2] - &a[4][2], // 关键点2:指针相减&p[4][2] - &a[4][2]); // 关键点3:结果转换为整数return 0;
}
关键点解析:
指针类型转换:
a
是int[5][5]
类型,&a[0]
是int(*)[5]
(指向5个int的指针)p
是int(*)[4]
(指向4个int的指针)p = (int(*)[4])a
将5列的数组指针强制转换为4列的指针指针运算:
p[4]
相当于*(p + 4)
,会跳过4 * sizeof(int[4])
= 16个inta[4][2]
是第4行第2列元素(从0开始计数)地址计算:
&p[4][2]
=a + 4*4 + 2
=a + 18
(int单位)&a[4][2]
=a + 4*5 + 2
=a + 22
(int单位)&p[4][2] - &a[4][2]
=(a + 18) - (a + 22)
=-4
输出格式:
%p
会以指针形式输出-4(即0xFFFFFFFC
,补码表示)%d
直接输出-4
总结:
- 指针类型转换导致指针算术的步长不同(4 vs 5)
- 指针相减的结果是元素间隔数(-4表示p[4][2]在a[4][2]前面4个int位置)
- 这个例子展示了指针类型对算术运算的影响
3.5 试题练习五
#include <stdio.h>
int main()
{int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // 初始化2行5列的二维数组int* ptr1 = (int*)(&aa + 1); // 关键点1int* ptr2 = (int*)(*(aa + 1)); // 关键点2printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); // 关键点3return 0;
}
关键点解析:
数组初始化:
aa
是一个2行5列的二维数组,内存布局如下:aa[0][0] = 1 aa[0][1] = 2 aa[0][2] = 3 aa[0][3] = 4 aa[0][4] = 5 aa[1][0] = 6 aa[1][1] = 7 aa[1][2] = 8 aa[1][3] = 9 aa[1][4] = 10
ptr1
的赋值:
&aa
是"指向整个二维数组的指针",类型是int (*)[2][5]
&aa + 1
会跳过整个数组(2×5=10个int),指向数组末尾之后的位置(int*)
强制转换为int指针,所以ptr1
指向数组末尾之后的位置
ptr2
的赋值:
aa + 1
:aa
会退化为指向第一行的指针(类型int (*)[5]
)aa + 1
指向第二行(即{6,7,8,9,10}
)*(aa + 1)
解引用得到第二行(类型int [5]
,会退化为指向第一个元素的指针)(int*)
强制转换是多余的,但结果不变输出表达式:
*(ptr1 - 1)
:
ptr1
指向数组末尾之后ptr1 - 1
回退一个int,指向最后一个元素aa[1][4]
(值为10)*(ptr2 - 1)
:
ptr2
指向第二行第一个元素aa[1][0]
(值为6)ptr2 - 1
回退一个int,指向aa[0][4]
(值为5)
总结:
&aa + 1
跳过整个二维数组aa + 1
跳过一行(5个int)- 指针运算时要注意指针的类型和步长
- 输出结果是最后一个元素
10
和第一行最后一个元素5
3.6 试题练习六
#include <stdio.h>
int main()
{char* a[] = { "work","at","alibaba" }; // 关键点1:指针数组char** pa = a; // 关键点2:二级指针指向数组首元素pa++; // 关键点3:指针移动printf("%s\n", *pa); // 关键点4:解引用输出return 0;
}
关键点解析:
指针数组
char* a[]
:
a
是一个数组,包含3个char*
类型的指针元素- 每个指针指向一个字符串常量:
a[0]
指向"work"a[1]
指向"at"a[2]
指向"alibaba"二级指针
char** pa = a
:
a
在表达式中退化为指向首元素的指针(即char**
类型)pa
指向a[0]
,也就是指向"work"的指针指针运算
pa++
:
pa
是char**
类型,pa++
会使指针移动一个char*
的大小(通常4或8字节)- 移动后
pa
指向a[1]
,即指向"at"的指针解引用输出
*pa
:
*pa
获取pa
当前指向的值,即a[1]
(指向"at"的指针)printf
使用%s
格式打印该指针指向的字符串
总结:
- 指针数组
a
存储的是指向字符串常量的指针- 二级指针
pa
初始指向数组的第一个元素pa++
使指针移动到数组的第二个元素*pa
解引用得到指向"at"的指针,%s
打印出字符串"at"这个例子展示了指针数组和二级指针的配合使用,以及指针运算如何遍历指针数组。
3.7 试题练习七
#include <stdio.h>
int main()
{// 初始化字符串指针数组char* c[] = { "ENTER","NEW","POINT","FIRST" };// 初始化指向c数组元素的指针数组char** cp[] = { c + 3, c + 2, c + 1, c };// 三级指针指向cp数组char*** cpp = cp;// 第一次输出printf("%s\n", **++cpp);// 第二次输出printf("%s\n", *-- * ++cpp + 3);// 第三次输出printf("%s\n", *cpp[-2] + 3);// 第四次输出printf("%s\n", cpp[-1][-1] + 1);return 0;
}
初始内存布局:
-
字符串数组:
c[0] -> "ENTER" c[1] -> "NEW" c[2] -> "POINT" c[3] -> "FIRST"
-
指针数组cp:
cp[0] = c + 3 -> &c[3] -> "FIRST" cp[1] = c + 2 -> &c[2] -> "POINT" cp[2] = c + 1 -> &c[1] -> "NEW" cp[3] = c -> &c[0] -> "ENTER"
-
三级指针:
cpp -> cp
逐步解析:
第一次输出:
**++cpp
++cpp
:cpp先自增,现在指向cp[1]
*cpp
:解引用得到cp[1] -> c + 2
**cpp
:再次解引用得到c[2] -> “POINT”输出:
POINT
第二次输出:
*-- * ++cpp + 3
++cpp
:cpp自增,现在指向cp[2]
*cpp
:解引用得到cp[2] -> c + 1
-- *cpp
:c + 1减1变为c + 0
*-- *cpp
:解引用得到c[0] -> “ENTER”
+3
:指针运算,指向"ENTER"的第3个字符’E’之后输出:
ER
(从第3个字符开始)第三次输出:
*cpp[-2] + 3
cpp[-2]
:相当于*(cpp - 2),即cp[0] -> c + 3
*cpp[-2]
:解引用得到c[3] -> “FIRST”
+3
:指针运算,指向"FIRST"的第3个字符’R’之后输出:
ST
(从第3个字符开始)第四次输出:
cpp[-1][-1] + 1
cpp[-1]
:相当于*(cpp - 1),即cp[1] -> c + 2
cpp[-1][-1]
:相当于*(*(cpp - 1) - 1) -> *(c + 2 - 1) -> c[1] -> “NEW”
+1
:指针运算,指向"NEW"的第1个字符’N’之后输出:
EW
(从第1个字符开始)
关键点总结:
多级指针的解引用需要从外向内逐步分析
指针运算会改变指针的指向位置
数组下标访问和指针运算可以互相转换(如
cpp[-1]
等价于*(cpp - 1)
)字符串指针加上偏移量会从指定位置开始输出
这个例子展示了C语言中复杂指针操作的强大能力,但也显示了这类代码容易造成混淆的特点。在实际开发中,建议使用更清晰的表达方式。
本篇内容到此结束 如果对你有所帮助 希望能一键三连 谢谢
往期回顾:
《初探指针世界:揭开内存管理与编程优化的第一篇章》-----指针一
《C 语言指针进阶:const 修饰、断言机制与传址调用深度解析》----指针二
《C 语言指针高级指南:字符、数组、函数指针的进阶攻略》----指针三
《从回调函数到 qsort:C 语言指针高级应用全攻略》----指针四
相关文章:
《C 语言 sizeof 与 strlen 深度对比:原理、差异与实战陷阱》
目录 一. sizeof 和 strlen 的对比 1.1 sizeof 1.2 strlen 1.3 对比表格 二. 数组和指针笔试题解析 2.1 一维数组 2.2 字符数组 2.2.1 代码练习一 2.2.2 代码练习二 2.2.3 代码练习三 2.2.4 代码练习四 2.2.5 代码练习五 2.2.6 代码练习六 2.3 二维数组 …...
C++ 初阶 | 类和对象易错知识点(上)
目录 0.引言 1.访问限定符 2.域 3.类的实例化和声明 4.this指针 5.构造函数(自动执行) 6.拷贝构造 7.运算符重载 8.日期类的实现 9.总结 0.引言 今天,小邓儿和大家分享一下,C在类和对象中的易错知识点🤭&am…...
USB转TTL
USB转TTL模块是实现计算机USB接口与TTL电平串口设备(如单片机、嵌入式系统)通信的核心组件,其原理涉及协议转换和电平适配两大关键技术 一、核心功能与应用场景 功能:将计算机的USB信号(高速差分信号、USB协议&#…...
汽车生产中的测试台连接 – EtherCAT 转CANopen高效的网关通信
使用 EtherCAT 和 CANopen协议,实现对汽车零部件的高效生产线末端测试 某电动机、电桥和变速箱制造商之一,正在其生产线上使用ETHERCAT转canopen网关WL-ECAT-COP的解决方案。集成到测试线中的下线测试必须映射众多待测设备的测试应用。该制造商已指定 Et…...
汽车充电过程中--各个电压的关系(DeepSeek)
在电动汽车的充电过程中,电池的充电机制涉及多个电压参数的协调控制,以下从原理到实际应用逐步分析: 1. 充电基础原理 电动汽车电池(通常为锂离子电池组)的充电本质是通过外部电源向电池注入电能,使锂离子…...
基于HTML的Word风格编辑器实现:从零打造功能完备的富文本编辑器
引言 在Web开发中,实现一个功能完备的富文本编辑器是一个常见需求。本文将基于HTML5和JavaScript,结合第三方库,打造一个具有Word风格界面的富文本编辑器,支持格式设置、图片插入、表格创建、文件导入导出等核心功能。 完整代码…...
亚远景-汽车软件开发的“升级之路”:ASPICE各等级说明
ASPICE(Automotive SPICE)将汽车软件开发过程的成熟度划分为六个等级,从0级到5级,每个等级代表了组织在软件开发过程中的不同能力水平。以下是各等级的详细说明: 等级0:不完整(Incomplete&#…...
Unity Display 1 No cameras rendering
一个相机不能同时输出到屏幕和RenderTexture。 Output Texture,要么是 None (屏幕),要么是RenderTexture。 如果此时相机已经输出到RenderTexture,场景中又没有别的相机在渲染,屏幕将变黑并显示No cam…...
Python Selenium 使用指南
Selenium 是一个用于自动化 Web 浏览器交互的强大工具,常用于网页测试、数据抓取和自动化任务。以下是 Python 中 Selenium 的详细使用说明。 安装 Selenium 首先需要安装 Selenium 库和浏览器驱动: pip install selenium 然后下载对应浏览器的驱动&…...
Cribl 对数据源进行过滤-01
先说一个项目中实际的例子: Cribl 利用filter expression 来过滤 data, 举个例子: source1: sourcerouter=A, source 2: sourcerouter=B, 这个时候,可以要把他们合并起来: sourcerouter=A || sourcerouter=B 来进行过滤想要的数据。 最后可以使用一个pipeline 来对数据进行…...
python 通过 pymysql 获取 select count(*) xxx 的数量
在使用 pymysql 库来获取 SELECT COUNT(*) 语句的结果时,你可以通过以下步骤实现: 安装 pymysql:如果你还没有安装 pymysql,可以通过 pip 安装它。 pip install pymysql连接到数据库:使用 pymysql.connect() 方法连接…...
定时任务延迟任务
二者的区别: 定时任务:有固定周期的,有明确的触发时间。 延迟任务:没有固定的开始时间,它常常是由一个事件触发的,而在这个事件触发之后的一段时间内触发另一个事件,任务可以立即执行࿰…...
【动手学深度学习】1.1~1.2 机器学习及其关键组件
目录 一、引言1.1. 日常生活中的机器学习1.2. 机器学习中的关键组件1)数据2)模型3)目标函数4)优化算法 一、引言 1.1. 日常生活中的机器学习 应用场景: 以智能语音助手(如Siri、Alexa)的唤醒…...
LLaVA-MoD:基于MoE结构和蒸馏训练方法,训练轻量化多模态大模型!!
摘要:我们介绍了LLaVA-MoD,这是一个旨在高效训练小型多模态语言模型(s-MLLM)的创新框架,通过从大规模多模态语言模型(l-MLLM)中提取知识来实现。我们的方法解决了多模态语言模型(MLL…...
YOLOv8 的双 Backbone 架构:解锁目标检测新性能
一、开篇:为何踏上双 Backbone 探索之路 在目标检测的领域中,YOLOv8 凭借其高效与精准脱颖而出,成为众多开发者和研究者的得力工具。然而,传统的单 Backbone 架构,尽管已经在诸多场景中表现出色,但仍存在一…...
SSRF(服务器端请求伪造)基本原理靶场实现
1、漏洞原理 攻击者通过构造恶意请求,诱使服务器向内部系统或第三方服务发起非预期的网络请求。其核心在于 服务器信任了不可信的用户输入,并基于该输入发起网络操作。 2、攻击场景与利用方式 1. 基础利用 攻击类型示例Payload目标读取本地文件file://…...
自动化测试脚本点击运行后,打开Chrome很久??
亲爱的小伙伴们大家好。 小编最近刚换了电脑,这几天做自动化测试发现打开Chrome浏览器需要等待好长时间,起初还以为代码有问题,或者Chromedriver与Chrome不匹配造成的,但排查后发现并不是!! 在driver.py中…...
Oracle中如何解决FREE BUFFER WAITS
基于性能上的考虑,服务器进程在扫描LRU主列的同时,会将脏块移至LRU-W列,如果发现没有足够可用(可替换)的BUFFER CACHE,进程并不会无止尽地扫描整条LRU主列,而是在扫描到某个阀值(该阀…...
OpenHarmony开源鸿蒙兼容性测试常见问题解答分享
OpenHarmony 兼容性测评主要是验证合作伙伴的设备和业务应用满足 OpenHarmony 开源兼容性定义的技术要求,确保运行在 OpenHarmony 上的设备和业务应用能稳定、正常运行,同时使用 OpenHarmony 的设备和业务应用有一致性的接口和业务体验。 一、兼容性测评…...
Android trace presentFence屏幕显示的帧
Android trace presentFence屏幕显示的帧 presentFence :当帧成功显示到屏幕时,present fence就会signal。 FrameMissed/GpuFrameMissed/HwcFrameMissed表示上一次合成的结果,当SurfaceFlinger合成后显示到屏幕上,present fence就…...
【520特辑】情人节脑影像绘图
祝大家520快乐! 永远爱自己! 1.Brain Net基于节点画爱心 clear all; clc;t linspace(0, 2*pi, 30); x 16*sin(t).^3; y 13*cos(t)-5*cos(2*t)-2*cos(3*t)-cos(4*t); z zeros(size(t));[X,Y] meshgrid(linspace(-10,10,5), linspace(-10,10,5)); X …...
Linux服务器配置深度学习环境(Pytorch+Anaconda极简版)
前言: 最近做横向需要使用实验室服务器跑模型,之前用师兄的账号登录服务器跑yolo,3张3090一轮14秒,我本地一张4080laptop要40秒,效率还是快很多,(这么算一张4080桌面版居然算力能比肩3090&#…...
如何理解大模型的幻觉输出及RAG技术的应用与实战案例
导读:大语言模型(LLM)在当今技术领域中扮演着越来越重要的角色,但其“幻觉输出”问题却成为实际应用中的痛点。本文将带你深入剖析这一现象的定义、表现形式及成因,并探讨如何通过RAG(检索增强生成…...
std::vector<>.emplace_back
emplace_back() 详解:C 就地构造的效率革命 emplace_back() 是 C11 引入的容器成员函数,用于在容器尾部就地构造(而非拷贝或移动)元素。这一特性显著提升了复杂对象的插入效率,尤其适用于构造代价较高的类型。 一、核…...
卷积神经网络(CNN)学习率调整完全指南:从理论到PyTorch实践
引言 学习率是训练卷积神经网络(CNN)最重要的超参数之一,合理调整学习率可以显著提高模型性能、加速收敛并避免训练失败。本文将全面解析CNN学习率调整的技术与方法,涵盖基础概念、常用策略、PyTorch实现以及实用技巧。 一、学习率为什么如此重要&…...
KLEC--基于知识学习的演化计算算法
KLEC–基于知识学习的演化计算算法 title: Knowledge Learning for Evolutionary Computation author: Yi Jiang, Zhi-Hui Zhan, Kay Chen Tan, Jun Zhang. journal: IEEE TRANSACTIONS ON EVOLUTIONARY COMPUTATION (TEVC) DOIÿ…...
Git查看指定作者提交命令
要查看 Git 仓库中某个作者的提交记录,可以使用 git log 命令结合 --author 选项。以下是具体用法和示例: 基础命令 git log --author"作者名"作用:列出指定作者的所有提交记录。示例:查找作者名为 John Doe 的提交&am…...
隐形安全感
凌晨两点,手机突然震动。合作三年的化工原料供应商发来紧急消息:“一批次环氧树脂需要连夜从南京调往广州,但合作多年的物流公司临时爽约,能帮忙想想办法吗?” 我盯着屏幕愣了几秒。这类危险品运输从来不是简单的“拉…...
代码随想录算法训练营 Day52 图论Ⅲ 岛屿问题Ⅱ 面积 孤岛 水流 造岛
图论 题目 101. 孤岛的总面积 计算孤岛总面积,一个想法是将相邻的陆地的位置置为 0,最后计算孤岛面积中最小的一个 #include <iostream> #include <vector> #include <queue>using namespace std;int sum 0; int dir[4][2] {0,-1…...
实用 Git 学习工具推荐:Learn Git Branching
https://learngitbranching.js.org/?localezh_CN 网站概述 Learn Git Branching 是一个面向开发者的交互式 Git 学习平台,专为那些希望通过实践掌握 Git 高级用法的用户设计。网站采用游戏化的界面和渐进式挑战,让复杂的 Git 概念变得直观易懂。无论是…...
物流项目第三期(统一网关、工厂模式运用)
前两期: 物流项目第一期(登录业务)-CSDN博客 物流项目第二期(用户端登录与双token三验证)-CSDN博客 为什么要有网关? 通过前面的课程我们已经完成了四个端的登录,但是我们并没有对登录后的请…...
安卓settings单双屏显示
Settings/src/com/android/settings/homepage/SettingsHomepageActivity.java的onCreate方法中,创建布局之前会通过ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this);去获取是否使用嵌入式显示。 mIsEmbeddingActivityEnabled ActivityEmbeddingUtils.is…...
SpringCloud+Vue实现大文件分片下载(支持开始、暂停、继续、取消)
1. 实现效果 http://localhost:8089/#/demo 所有代码已提交至 https://github.com/SJshenjian/cloud.git与 https://github.com/SJshenjian/cloud-web.git中,欢迎star 2. 后端核心代码 FeignClient(value "download", contextId "download"…...
RK3576 Android 14.0 SDK开发指南(第一集)
RK3576 Android 14.0 SDK代码编译 SDK下载到本地后大概70多个G 下载后要做个校验 解压后内核源码 kernel代码路径说明 Android14支持6.1 版本的kernel,kernel源码在工程中kernel-6.1目录下 Lunch项说明 一键编译命令 ./build.sh -UKAupSoc RK3576 SDK默认没有开…...
【C/C++】现代C++线程池:从入门到生产级实现
文章目录 现代C线程池:从入门到生产级实现🧠 What Is a Thread Pool?🧩 Why Use a Thread Pool?🔰 Part 1: Basic Thread Pool (Beginner)🔧 Minimal Working Code:✅ Usage: 🧑🔬 Part 2: …...
后期:daplink
问题描述: 问题一:总工程,USB插入能识别到dap,但有个黄色的感叹号!现在连感叹号都没有了。 重点跟踪了枚举的几个函数,差异点就是有个工厂描述符没有枚举到。 问题二: 下载后,目标板,没有自动复位。 问题三:未移植daplink的时候,虚拟串口是收发正常,貌似没有映射到…...
Android SharedPreferences:从零到一的全面解析与实战指南
简介 SharedPreferences是Android平台提供的一种轻量级键值对存储方案,虽然看似简单,但在实际开发中却蕴含着丰富的技术细节和最佳实践。本文将从基本概念出发,深入分析其源码实现原理,探讨企业级开发中的应用技巧,并提供完整的代码示例,帮助开发者全面掌握这一重要数据…...
推扫式高光谱相机VIX-N230重磅发布——开启精准成像新时代
随着各行业对高光谱成像技术需求的持续增长,市场对于高分辨率、高灵敏度以及快速成像的高光谱相机的需求愈发迫切。中达瑞和凭借多年的行业经验和技术积累,敏锐捕捉到这一市场趋势,正式推出全新一代推扫式可见光近红外高光谱相机——VIX-N230…...
实现rpc通信机制(待定)
一、概述 (1)rpc(remote procedure call, 远程接口调用),就像在本地调用函数一样,是应用组成服务内部分布式的基础功能。应用场景是在内网中的计算,比如:(a) 为上传的一张图片加水印、…...
STM32--串口函数
USART_GetFlagStatus() 用于获取某个串口的寄存器中的某个标志位的状态,和中断无关。 USART_ClearFlag() 用于清楚串口寄存器中的标志位的状态,和中断无关。 USART_ITConfig() 使能或禁用指定的 USART 中断。 USART_ITConfig(USART1, USART_IT_TXE, …...
Linux跨网络通信中IP与MAC的作用
是的,MAC地址和IP地址分别属于OSI模型的不同层次,并在数据封装过程中被添加到不同的位置: 1. MAC地址(数据链路层) 作用层级:数据链路层(第二层)。封装位置:添加到数据链…...
Facebook隐私保护的成与败:一场对用户信任的考验
引言 在这个信息爆炸的时代,Facebook作为全球最大的社交网络平台之一,其隐私保护政策和实践一直是公众关注的焦点。随着数据泄露事件的频发,Facebook在隐私保护方面的成与败,不仅关系到其自身的声誉,更是对用户信任的…...
面试算法刷题3(核心+acm)
102. 二叉树的层序遍历 递归法 核心代码模式 不断递归根节点,根据深度来判断加在哪一层上。 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(in…...
[Java] idea的调试介绍
1. 什么是调试? 调试就是帮助我们去寻找代码中的bug,优化代码的过程。调试对于程序员来说非常重要,能帮助我们更容易去找到代码中的错误,去修复。 2. idea集成开发环境的调试 下面我就介绍下idea集成开发环境下的调试ÿ…...
winrm ‘Protocol‘ object has no attribute ‘run_ps‘
在使用 winrm 库时遇到错误 ‘Protocol’ object has no attribute ‘run_ps’ 通常意味着你正在尝试使用一个不存在的属性或方法。在 Python 中使用 winrm 库时,正确的调用方式应该使用 client 对象来运行 PowerShell 命令,而不是直接在 Protocol 对象上…...
WHAT - CSS 中的 min-width
文章目录 基本语法常见取值使用场景举例min-width: 0为什么 min-width: 0 重要?场景演示提示 注意事项 在 WHAT - CSS 中的 width 中我们已经详细介绍过 width。那为什么 CSS 还要提供一个 min-width? 阅读本文前可先阅读 MDN - min-width。 min-width…...
测试W5500的第2步_使用ioLibrary库创建TCP客户端
ioLibrary库下载地址:文件下载地址:https://gitee.com/wiznet-hk/STM32F10x_W5500_Examples 源文件下载地址:https://gitee.com/wiznet-hk 没有注册的,只能复制粘贴了。 本文介绍了如何初始化STM32的硬件资源,配置W5500的网络参数ÿ…...
深度学习中的正则化方法与卷积神经网络基础
笔记 1 正则化方法 1.1 什么是正则化 防止模型过拟合(训练集效果好, 测试集效果差), 提高模型泛化能力 一种防止过拟合, 提高模型泛化能力的策略 L1正则: 需要通过手动写代码实现 L2正则: SGD(weight_decay) dropout BN 1.2 Dropout正则化 让神经元以p概率随机死亡, 每…...
pg_dump
以下是 PostgreSQL 中 pg_dump 命令的 核心参数 及 使用示例 的详细说明: 一、核心参数分类及说明 pg_dump 主要用于备份单个数据库,支持多种格式和灵活的控制选项。以下是其关键参数分类: 1. 连接参数 参数说明-h, --hostHOST数据库服务器…...
css使用clip-path属性切割显示可见内容
1. 需求 想要实现一个渐变的箭头Dom,不想使用svg、canvas去画,可以考虑使用css的clip-path属性切割显示内容。 2. 实现 <div class"arrow">箭头 </div>.arrow{width: 200px;height: 60px;background-image: linear-gradient(45…...