文章目录
- 前言
- 一、pandas是什么?
-
- 1. sizeof 和 strlen 对比
- 二、 数组和指针试题解析
-
- 1.一维数组
- 2.字符数组
- 三、指针运算试题解析
-
- 1.数组指针偏移
- 2. 结构体指针运算(X86)
- 3.逗号表达式陷阱
- 4.数组指针类型不匹配(X86)
- 5.二维数组地址偏移
- 6.指针数组
- 7.三级指针与字符串
- 关键总结
前言
指针学习的内容非常复杂,这里通过sizeof和strlen为引子,解析部分指针相关试题,主要涉及指针定位的偏移,多级指针变量解析
一、pandas是什么?
1. sizeof 和 strlen 对比
本质 | 编译时运算符 | 库函数(需 <string.h>) |
功能 | 计算操作数占内存大小(字节) | 统计 \\0 前的字符个数 |
关注内容 | 内存空间大小(不关心数据内容) | 依赖 \\0 结束符(无 \\0 会越界访问) |
操作数类型 | 变量、数据类型、数组、指针等 | 必须是字符串地址(const char*) |
示例 | sizeof(int) = 4(32位系统) | strlen("abc") = 3 |
关键区别:
- 数组名在 sizeof 中的意义:
- sizeof(数组名):计算整个数组大小(如 int a[4] → 16 字节)。
- &数组名:取整个数组的地址(类型为数组指针)。
- 其他情况(如 a+0):数组名退化为首元素地址。
二、 数组和指针试题解析
1.一维数组
int a[] = {1,2,3,4};
printf("%d\\n", sizeof(a)); // 16 → 整个数组大小 (4个int)
printf("%d\\n", sizeof(a+0)); // 4/8 → 首元素地址(指针大小)
printf("%d\\n", sizeof(*a)); // 4 → a[0] (int类型)
printf("%d\\n", sizeof(&a)); // 4/8 → 数组指针(地址大小)
printf("%d\\n", sizeof(&a[0])); // 4/8 → 元素地址
2.字符数组
代码1(无 \\0 初始化):
char arr[] = {'a','b','c','d','e','f'};
printf("%d\\n", sizeof(arr)); // 6 → 整个数组大小
printf("%d\\n", strlen(arr)); // 随机值 → 无 `\\0` 导致越界查找
代码2(字符串初始化):
char arr[] = "abcdef"; // 隐含 '\\0'
printf("%d\\n", sizeof(arr)); // 7 → 包含 '\\0'
printf("%d\\n", strlen(arr)); // 6 → '\\0' 前字符数
代码3(指针指向字符串):
char *p = "abcdef";
printf("%d\\n", sizeof(p)); // 4/8 → 指针大小
printf("%d\\n", strlen(p)); // 6 → 字符串长度
三、指针运算试题解析
1.数组指针偏移
int a[5] = {1,2,3,4,5};
int *ptr = (int *)(&a + 1); // &a+1 跳过整个数组
printf("%d,%d", *(a+1), *(ptr–1)); // 输出:2,5
图解:
a: [1][2][3][4][5] | 越界位置
↑ ↑ ↑
a a+1 ptr → ptr-1指向5
2. 结构体指针运算(X86)
struct Test *p = (struct Test*)0x100000;
printf("%p\\n", p + 0x1); // 0x100014 → 跳过20字节结构体
printf("%p\\n", (unsigned long)p + 0x1); // 0x100001 → 数值+1
printf("%p\\n", (unsigned int*)p + 0x1); // 0x100004 → 跳过4字节(int)
3.逗号表达式陷阱
int a[3][2] = { (0,1), (2,3), (4,5) }; // 逗号表达式→{1,3,5}
int *p = a[0];
printf("%d", p[0]); // 输出:1
实际初始化:
a[0][0]=1, a[0][1]=3, a[1][0]=5, 其余为0
4.数组指针类型不匹配(X86)
int a[5][5];
int(*p)[4] = a; // p的步长=4个int(但a每行5个int)
printf("%p,%d\\n", &p[4][2] – &a[4][2], &p[4][2] – &a[4][2]);
// 输出:0xfffffffc, -4(地址差为-4个int)
图解:
p[4][2] → 指向第18个元素(a[3][3])
a[4][2] → 指向第22个元素(下标4*5+2=22)
差值 = 18 – 22 = -4
5.二维数组地址偏移
int aa[2][5] = {1,2,3,4,5,6,7,8,9,10};
int *ptr1 = (int *)(&aa + 1); // 跳过整个二维数组
int *ptr2 = (int *)(*(aa + 1)); // 指向第二行首元素
printf("%d,%d", *(ptr1–1), *(ptr2–1)); // 输出:10,5
6.指针数组
char *a[] = {"work","at","alibaba"};
char **pa = a;
pa++; // 指向a[1]("at")
printf("%s\\n", *pa); // 输出:"at"
7.三级指针与字符串
char *c[] = {"ENTER","NEW","POINT","FIRST"};
char **cp[] = {c+3, c+2, c+1, c}; // cp[0]=c+3, cp[1]=c+2, …
char ***cpp = cp;
.
// 修正后的表达式解析:
printf("%s\\n", **++cpp); // "POINT"(cpp指向cp[1]→c[2])
printf("%s\\n", *—*++cpp + 3); // "ER"(cpp指向cp[2],修改后指向c[0]+3)
printf("%s\\n", *cpp[–2] + 3); // "ST"(cp[0]=c+3→"FIRST"+3)
printf("%s\\n", cpp[–1][–1]+1); // "EW"(c[1]="NEW"+1)
关键总结
sizeof vs strlen:
- sizeof 是编译时运算符,strlen 是运行时函数。
- 数组名在 sizeof 中代表整个数组,其他情况退化为指针。
指针运算核心:
- &a+1:跳过整个数组。
- 指针加减整数以指向类型大小为步长。
- 多级指针需逐层解引用(如 ***cpp)。
易错点:
- 逗号表达式在初始化中的陷阱((0,1) → 1)。
- 字符数组未以 \\0 结尾时 strlen 会越界。
- 数组指针类型不匹配导致地址偏移错误。
附:所有题目答案已验证,符合C标准行为(X86环境)。
评论前必须登录!
注册