欢迎光临
我们一直在努力

做几个指针题目巩固一下

题目1

#include <stdio.h>
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr 1));
return 0;
}

分析:本题考察数组指针运算和指针类型转换的理解。

1. 代码解析

数组 a 的定义:

int a[5] = { 1, 2, 3, 4, 5 };

  • 创建了一个包含5个整数的数组
  • 内存布局:a[0]=1, a[1]=2, a[2]=3, a[3]=4, a[4]=5

关键语句分析:

int* ptr = (int*)(&a + 1);

  • &a:获取整个数组的地址,类型是 int(*)[5](指向包含5个int的数组的指针)
  • &a + 1:指针算术运算,移动 1 * sizeof(int[5]) = 1 * (5 * sizeof(int)) = 20 字节(假设int为4字节)
  • (int*):将 int(*)[5] 类型强制转换为 int* 类型
  • 结果:ptr 指向数组 a 末尾之后的位置(即 a[5] 的地址,虽然这个元素不存在)

2. 输出语句分析

printf("%d,%d", *(a + 1), *(ptr 1));

第一个输出:*(a + 1)

  • a 是数组名,在大多数上下文中会退化为指向首元素的指针(int* 类型)
  • a + 1:指针加1,移动 1 * sizeof(int) = 4 字节
  • *(a + 1):解引用,获取 a[1] 的值
  • 结果:2

第二个输出:*(ptr – 1)

  • ptr 是 int* 类型,指向 a[5](数组末尾之后)
  • ptr – 1:指针减1,向后移动 1 * sizeof(int) = 4 字节
  • *(ptr – 1):解引用,获取 a[4] 的值
  • 结果:5

3. 内存布局示意图

地址 内容 说明
0x1000 a[0]=1 数组起始位置
0x1004 a[1]=2 *(a + 1) 指向这里
0x1008 a[2]=3
0x100C a[3]=4
0x1010 a[4]=5 *(ptr – 1) 指向这里
0x1014 (越界) ptr 指向这里 (&a + 1 的结果)

4. 关键知识点总结

  • 数组名与指针的关系:

    • a 作为数组名,在大多数表达式中退化为指向首元素的指针(&a[0])
    • &a 获取的是整个数组的地址,类型不同(int(*)[5])
  • 指针算术运算的尺度:

    • a + 1:移动 sizeof(int) 字节
    • &a + 1:移动 sizeof(int[5]) 字节
    • 指针加减整数的移动距离 = 整数 × 指针所指向类型的大小
  • 类型转换的作用:

    • (int*)(&a + 1) 将数组指针转换为普通整型指针
    • 转换后,指针算术运算的尺度变为 sizeof(int)
  • 数组越界访问:

    • ptr 指向数组末尾之后是合法的指针运算
    • ptr – 1 又回到数组范围内,访问 a[4] 是合法的
  • 5. 预期输出结果

    2,5

    题目2

    struct Test
    {
    int Num;
    char *pcName;
    short sDate;
    char cha[2];
    short sBa[4];
    }*p = (struct Test*)0x100000;
    int main()
    {
    printf("%p\\n", p + 0x1);
    printf("%p\\n", (unsigned long)p + 0x1);
    printf("%p\\n", (unsigned int*)p + 0x1);
    return 0;
    }

    分析:本题考察结构体指针运算和不同类型指针的算术运算规则。

    1. 结构体大小计算

    首先需要计算 struct Test 的大小(在32位系统下):

    • int Num:4字节
    • char *pcName:指针,4字节
    • short sDate:2字节
    • char cha[2]:2字节
    • short sBa[4]:8字节

    总大小 = 4 + 4 + 2 + 2 + 8 = 20字节

    2. 三个输出语句分析

    第一个输出:printf("%p\\n", p + 0x1);

    • p 是 struct Test* 类型指针,指向地址 0x100000
    • 指针加整数 0x1 表示移动 1 * sizeof(struct Test) 个字节
    • sizeof(struct Test) = 20 字节
    • 所以 p + 0x1 = 0x100000 + 20 = 0x100014
    • 输出:0x100014

    第二个输出:printf("%p\\n", (unsigned long)p + 0x1);

    • (unsigned long)p 将指针转换为无符号长整型
    • 整型加法是直接的数值加法
    • 0x100000 + 0x1 = 0x100001
    • 输出:0x100001

    第三个输出:printf("%p\\n", (unsigned int*)p + 0x1);

    • (unsigned int*)p 将指针转换为 unsigned int* 类型
    • sizeof(unsigned int) = 4 字节(32位系统)
    • 指针加整数 0x1 表示移动 1 * 4 = 4 字节
    • 所以 (unsigned int*)p + 0x1 = 0x100000 + 4 = 0x100004
    • 输出:0x100004

    3. 关键知识点总结

  • 指针算术运算:指针加整数 p + n 的实际移动距离是 n * sizeof(指针所指向的类型)
  • 类型转换的影响:
    • 保持指针类型时,算术运算按指针类型的大小进行
    • 转换为整型时,进行直接的数值运算
    • 转换为其他指针类型时,按新指针类型的大小进行运算
  • 结构体对齐:本题中结构体大小为20字节,没有额外的对齐填充(在32位系统下)
  • 4. 预期输出结果

    0x100014
    0x100001
    0x100004

    5. 验证方法

    可以在实际编译环境中运行验证,注意编译时可能需要指定32位模式(如 -m32 选项)以确保结构体大小与预期一致。

    题目3

    #include <stdio.h>
    int main()
    {
    int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    int *p;
    p = a[0];
    printf( "%d", p[0]);
    return 0;
    }

    分析:本题考察二维数组的初始化、逗号运算符的使用以及数组与指针的关系。

    1. 代码解析

    二维数组 a 的定义:

    int a[3][2] = { (0, 1), (2, 3), (4, 5) };

    这里有一个关键陷阱:初始化列表中使用的是圆括号 () 而不是花括号 {}。

    • 在C语言中,(0, 1) 是逗号表达式,其值为最后一个表达式的值,即 1
    • 同理,(2, 3) 的值为 3,(4, 5) 的值为 5
    • 因此,实际初始化等价于:int a[3][2] = { 1, 3, 5 };

    正确的二维数组初始化应该是:

    int a[3][2] = { {0, 1}, {2, 3}, {4, 5} };

    内存布局分析:

    • a 是一个3行2列的二维数组,共6个int元素
    • 按行优先顺序存储:a[0][0], a[0][1], a[1][0], a[1][1], a[2][0], a[2][1]
    • 由于逗号表达式,实际初始化值为:1, 3, 5, 0, 0, 0(剩余元素自动初始化为0)

    指针操作:

    int *p;
    p = a[0];

    • a[0] 是二维数组第一行的首地址,类型为 int[2],但在赋值给 int* 时会退化为指向 a[0][0] 的指针
    • p 指向 a[0][0],即数组的第一个元素

    2. 输出语句分析

    printf("%d", p[0]);

    • p[0] 等价于 *(p + 0),即访问 p 指向的第一个元素
    • 由于 p 指向 a[0][0],而 a[0][0] 被初始化为 1(来自 (0, 1) 逗号表达式的结果)
    • 因此输出:1

    3. 内存布局示意图

    地址 内容 说明
    0x1000 a[0][0]=1 (0,1)逗号表达式的结果
    0x1004 a[0][1]=3 (2,3)逗号表达式的结果
    0x1008 a[1][0]=5 (4,5)逗号表达式的结果
    0x100C a[1][1]=0 自动初始化为0
    0x1010 a[2][0]=0 自动初始化为0
    0x1014 a[2][1]=0 自动初始化为0

    p指向这里 (a[0][0])

    4. 关键知识点总结

  • 逗号表达式:

    • 格式:(表达式1, 表达式2, …, 表达式n)
    • 值:最后一个表达式的值
    • 本题中 (0, 1) 的值为 1,(2, 3) 的值为 3,(4, 5) 的值为 5
  • 二维数组初始化:

    • 正确语法:int a[3][2] = { {0, 1}, {2, 3}, {4, 5} };
    • 错误语法(本题):使用圆括号导致逗号表达式
    • 部分初始化:剩余元素自动初始化为0
  • 数组与指针关系:

    • a[0] 表示二维数组的第一行,类型为 int[2]
    • 在赋值给 int* 时,a[0] 退化为指向 a[0][0] 的指针
    • p = a[0] 等价于 p = &a[0][0]
  • 数组下标访问:

    • p[0] 等价于 *(p + 0),访问 p 指向的第一个元素
    • 对于二维数组,a[i][j] 在内存中是连续存储的
  • 5. 预期输出结果

    1

    题目4

    #include <stdio.h>
    int main()
    {
    int a[5][5];
    int(*p)[4];
    p = a;
    printf( "%p,%d\\n", &p[4][2] &a[4][2], &p[4][2] &a[4][2]);
    return 0;
    }

    分析:本题考察二维数组指针、指针类型转换以及指针算术运算的复杂应用。

    1. 代码解析

    数组定义:

    int a[5][5];

    • 定义一个5行5列的二维整型数组,共25个int元素
    • 内存中按行优先顺序连续存储

    指针定义:

    int(*p)[4];

    • p 是一个指向包含4个int的数组的指针(int(*)[4]类型)
    • 注意:p指向的数组大小是4,而a是5×5的数组

    指针赋值:

    p = a;

    • 将二维数组a的地址赋给指针p
    • 这里存在类型不匹配:a的类型是int(*)[5],而p的类型是int(*)[4]
    • 但C语言允许这种赋值(有警告),p现在指向a[0][0]

    2. 输出语句分析

    printf( "%p,%d\\n", &p[4][2] &a[4][2], &p[4][2] &a[4][2]);

    关键计算:&p[4][2] – &a[4][2]

  • &a[4][2] 的计算:

    • a[4][2] 是数组a的第5行第3列元素(0-based索引)
    • 在内存中的偏移量:4 * 5 * sizeof(int) + 2 * sizeof(int) = 22 * sizeof(int)
    • 假设int为4字节,则偏移量为88字节
  • &p[4][2] 的计算:

    • p 是int(*)[4]类型,p[4]表示第5个包含4个int的数组
    • p[4][2] 是p[4]这个数组的第3个元素
    • 在内存中的偏移量:4 * 4 * sizeof(int) + 2 * sizeof(int) = 18 * sizeof(int)
    • 假设int为4字节,则偏移量为72字节
  • 指针减法:

    • &p[4][2] – &a[4][2] 计算两个指针之间的元素个数差
    • 由于两个指针都指向int类型,相减的结果是它们之间相差的int元素个数
    • 地址差:72 – 88 = -16字节
    • 元素个数差:-16 / sizeof(int) = -4(假设int为4字节)
  • 输出格式:

    • 第一个%p:以指针格式输出地址差(-4转换为无符号指针值)
    • 第二个%d:以整数格式输出元素个数差(-4)
  • 3. 预期实验结果

    0xfffffffc,-4

    或(取决于平台和编译器):

    FFFFFFFFFFFFFFFC,-4

    注意:%p输出的具体格式可能因平台而异,但表示的地址差值是相同的。负数的指针表示通常使用补码形式。

    题目5

    #include <stdio.h>
    int main()
    {
    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));
    return 0;
    }

    分析:本题考察二维数组的指针运算、数组名退化规则以及指针类型转换的综合理解。

    1. 代码解析

    二维数组 aa 的定义:

    int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    • 创建一个2行5列的二维整型数组,共10个元素
    • 内存布局:按行优先顺序存储
      • 第0行:aa[0][0]=1, aa[0][1]=2, aa[0][2]=3, aa[0][3]=4, aa[0][4]=5
      • 第1行:aa[1][0]=6, aa[1][1]=7, aa[1][2]=8, aa[1][3]=9, aa[1][4]=10

    指针 ptr1 的定义:

    int *ptr1 = (int *)(&aa + 1);

    • &aa:获取整个二维数组的地址,类型为 int(*)[2][5](指向2×5二维数组的指针)
    • &aa + 1:指针算术运算,移动 1 * sizeof(int[2][5]) = 1 * (2×5×sizeof(int)) = 40 字节(假设int为4字节)
    • (int*):将 int(*)[2][5] 类型强制转换为 int* 类型
    • 结果:ptr1 指向数组 aa 末尾之后的位置

    指针 ptr2 的定义:

    int *ptr2 = (int *)(*(aa + 1));

    • aa 是二维数组名,在表达式中退化为指向第一行的指针,类型为 int(*)[5]
    • aa + 1:指针加1,移动 1 * sizeof(int[5]) = 1 * (5×sizeof(int)) = 20 字节
    • *(aa + 1):解引用,获取第二行的首地址,类型为 int[5],但会退化为 int*
    • (int*):显式转换为 int* 类型(实际上已经是 int*,转换是多余的但合法)
    • 结果:ptr2 指向 aa[1][0],即第二行的第一个元素

    2. 输出语句分析

    printf( "%d,%d", *(ptr1 1), *(ptr2 1));

    第一个输出:*(ptr1 – 1)

    • ptr1 是 int* 类型,指向数组 aa 末尾之后(即 aa[2][0] 的位置,虽然这个元素不存在)
    • ptr1 – 1:指针减1,向后移动 1 * sizeof(int) = 4 字节
    • *(ptr1 – 1):解引用,获取 aa[1][4] 的值
    • 计算:ptr1 指向 aa 末尾(偏移40字节),ptr1 – 1 指向 aa[1][4](偏移36字节)
    • 结果:10(aa[1][4] 的值)

    第二个输出:*(ptr2 – 1)

    • ptr2 是 int* 类型,指向 aa[1][0](第二行第一个元素)
    • ptr2 – 1:指针减1,向后移动 1 * sizeof(int) = 4 字节
    • *(ptr2 – 1):解引用,获取 aa[0][4] 的值
    • 计算:ptr2 指向 aa[1][0](偏移20字节),ptr2 – 1 指向 aa[0][4](偏移16字节)
    • 结果:5(aa[0][4] 的值)

    3. 内存布局示意图

    地址 内容 说明
    0x1000 aa[0][0]=1 数组起始位置
    0x1004 aa[0][1]=2
    0x1008 aa[0][2]=3
    0x100C aa[0][3]=4
    0x1010 aa[0][4]=5 *(ptr2 – 1) 指向这里
    0x1014 aa[1][0]=6 ptr2 指向这里 (*(aa + 1))
    0x1018 aa[1][1]=7
    0x101C aa[1][2]=8
    0x1020 aa[1][3]=9
    0x1024 aa[1][4]=10 *(ptr1 – 1) 指向这里
    0x1028 (越界) ptr1 指向这里 (&aa + 1 的结果)

    4. 关键知识点总结

  • 二维数组的地址运算:

    • &aa:获取整个二维数组的地址,类型为 int(*)[2][5]
    • aa:退化为指向第一行的指针,类型为 int(*)[5]
    • aa[0] 或 *aa:指向第一行第一个元素的指针,类型为 int*
  • 指针算术运算的尺度:

    • &aa + 1:移动 sizeof(int[2][5]) = 40 字节
    • aa + 1:移动 sizeof(int[5]) = 20 字节
    • ptr1 – 1 或 ptr2 – 1:移动 sizeof(int) = 4 字节
  • 数组名退化规则:

    • 在大多数表达式中,数组名会退化为指向其首元素的指针
    • 对于二维数组 aa,在 aa + 1 中退化为 int(*)[5]
    • 在 *(aa + 1) 中,得到的是 int[5] 类型,但会进一步退化为 int*
  • 类型转换的作用:

    • (int*)(&aa + 1) 将二维数组指针转换为普通整型指针
    • 转换后,指针算术运算的尺度从40字节变为4字节
  • 5. 预期输出结果

    10,5

    题目6

    #include <stdio.h>
    int main()
    {
    char *a[] = {"work","at","alibaba"};
    char**pa = a;
    pa++;
    printf("%s\\n", *pa);
    return 0;
    }

    分析:本题考察指针数组、二级指针的声明与操作,以及指针算术运算在字符指针数组中的应用。

    1. 代码解析

    指针数组 a 的定义:

    char *a[] = {"work","at","alibaba"};

    • 定义了一个包含3个元素的指针数组 a
    • 每个元素都是 char* 类型,指向一个字符串常量
    • 内存布局:
      • a[0] 指向字符串 "work" 的首字符
      • a[1] 指向字符串 "at" 的首字符
      • a[2] 指向字符串 "alibaba" 的首字符
    • 数组 a 本身在栈上,包含3个指针(每个指针大小通常为4或8字节,取决于系统)

    二级指针 pa 的定义:

    char**pa = a;

    • pa 是一个指向 char* 的指针(即二级指针)
    • a 作为数组名,在大多数表达式中退化为指向其首元素的指针
    • 首元素 a[0] 是 char* 类型,所以 &a[0] 是 char** 类型
    • 因此 pa 指向 a[0],即指向指针数组的第一个元素

    指针运算:

    pa++;

    • pa 是 char** 类型,指向 char* 类型的指针
    • pa++ 使 pa 向前移动 sizeof(char*) 个字节
    • 在32位系统中,sizeof(char*) = 4 字节
    • 在64位系统中,sizeof(char*) = 8 字节
    • 执行后,pa 指向 a[1](指针数组的第二个元素)

    2. 输出语句分析

    printf("%s\\n", *pa);

    表达式解析:

    • pa 现在指向 a[1](执行 pa++ 后)
    • *pa 解引用一次,得到 a[1] 的值,即指向字符串 "at" 的 char* 指针
    • printf 使用 %s 格式说明符,会从 *pa 指向的地址开始打印字符,直到遇到空字符 '\\0'

    内存访问过程:

  • pa 指向 a[1](指针数组的第二个元素)
  • *pa 获取 a[1] 的值,即字符串 "at" 的地址
  • printf 从该地址开始打印字符:'a', 't', '\\0'
  • 结果: 输出字符串 "at"

    3. 内存布局示意图

    栈内存(指针数组 a):
    地址 内容 指向的字符串常量(只读区)
    0x1000 a[0]=0x2000 → "work\\0"
    0x1008 a[1]=0x2005 → "at\\0" ← pa 指向这里(执行 pa++ 后)
    0x1010 a[2]=0x2008 → "alibaba\\0"

    只读数据区:
    0x2000 'w','o','r','k','\\0' // "work"
    0x2005 'a','t','\\0' // "at"
    0x2008 'a','l','i','b','a','b','a','\\0' // "alibaba"

    指针关系:
    初始:pa → a[0] (0x1000)
    pa++ 后:pa → a[1] (0x1008)
    *pa = a[1] = 0x2005 → "at"

    4. 关键知识点总结

  • 指针数组:

    • char *a[] 声明一个指针数组,每个元素都是 char* 类型
    • 常用于存储多个字符串,每个字符串可以有不同的长度
    • 数组元素在内存中连续存储,但指向的字符串不一定连续
  • 二级指针:

    • char** pa 声明一个指向 char* 的指针
    • 可以指向指针数组的首元素,用于遍历指针数组
    • 对二级指针解引用一次得到一级指针,解引用两次得到字符
  • 数组名退化规则:

    • 在大多数表达式中,数组名会退化为指向其首元素的指针
    • a 退化为 &a[0],类型为 char**
    • 因此可以直接赋值给 char** pa
  • 指针算术运算:

    • 指针加减整数的移动距离 = 整数 × 指针所指向类型的大小
    • pa++ 移动 sizeof(char*) 字节,指向下一个指针元素
    • 与普通数组指针运算类似,但这里操作的是指针数组
  • 字符串常量:

    • 字符串常量如 "work"、"at" 存储在只读数据区
    • 指针数组元素存储的是这些字符串常量的地址
    • 通过指针可以访问但不能修改字符串常量内容
  • 5. 预期输出结果

    at

    题目7

    #include <stdio.h>
    int main()
    {
    char *c[] = {"ENTER","NEW","POINT","FIRST"};
    char**cp[] = {c+3,c+2,c+1,c};
    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;
    }

    分析:本题考察多级指针、指针数组的复杂运算,以及前置自增/自减运算符与指针解引用的优先级组合。

    1. 代码解析

    指针数组 c 的定义:

    char *c[] = {"ENTER","NEW","POINT","FIRST"};

    • 定义了一个包含4个元素的指针数组 c
    • 每个元素都是 char* 类型,指向一个字符串常量
    • 内存布局:
      • c[0] 指向字符串 "ENTER" 的首字符
      • c[1] 指向字符串 "NEW" 的首字符
      • c[2] 指向字符串 "POINT" 的首字符
      • c[3] 指向字符串 "FIRST" 的首字符

    指针数组 cp 的定义:

    char**cp[] = {c+3,c+2,c+1,c};

    • 定义了一个包含4个元素的指针数组 cp,每个元素是 char** 类型(二级指针)
    • 初始化:
      • 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 的定义:

    char***cpp = cp;

    • cpp 是一个三级指针,指向 cp 数组的首元素
    • cp 作为数组名退化为指向其首元素的指针,类型为 char***
    • 初始时 cpp 指向 cp[0]

    2. 输出语句分析

    第一个输出:printf("%s\\n", **++cpp);

    • ++cpp:前置自增,cpp 先自增,指向 cp[1]
    • *++cpp:解引用一次,得到 cp[1] 的值,即 c + 2(指向 c[2] 的指针)
    • **++cpp:解引用两次,得到 *(c + 2),即 c[2] 的值,指向字符串 "POINT"
    • 输出:POINT

    第二个输出:printf("%s\\n", *–*++cpp+3);

    此时 cpp 指向 cp[1]。

    • ++cpp:前置自增,cpp 先自增,指向 cp[2]
    • *++cpp:解引用一次,得到 cp[2] 的值,即 c + 1(指向 c[1] 的指针)
    • –*++cpp:对 c + 1 进行前置自减,得到 c(指向 c[0] 的指针)
      • 注意:– 作用于 cp[2] 这个元素本身,即 cp[2] 的值从 c + 1 变为 c
    • *–*++cpp:解引用,得到 *c,即 c[0] 的值,指向字符串 "ENTER"
    • *–*++cpp+3:指针偏移3个字符,从 "ENTER" 的第3个字符开始(0-based索引)
      • "ENTER" 的字符:'E'(0), 'N'(1), 'T'(2), 'E'(3), 'R'(4)
      • 从索引3开始:"ER"
    • 输出:ER

    第三个输出:printf("%s\\n", *cpp[-2]+3);

    此时 cpp 指向 cp[2]。

    • cpp[-2]:等价于 *(cpp – 2),即 cp[0] 的值,也就是 c + 3
    • *cpp[-2]:解引用,得到 *(c + 3),即 c[3] 的值,指向字符串 "FIRST"
    • *cpp[-2]+3:指针偏移3个字符,从 "FIRST" 的第3个字符开始
      • "FIRST" 的字符:'F'(0), 'I'(1), 'R'(2), 'S'(3), 'T'(4)
      • 从索引3开始:"ST"
    • 输出:ST

    第四个输出:printf("%s\\n", cpp[-1][-1]+1);

    此时 cpp 指向 cp[2]。

    • cpp[-1]:等价于 *(cpp – 1),即 cp[1] 的值
      • 注意:在第二个输出中,cp[2] 被修改为 c,但 cp[1] 未被修改,仍为 c + 2
    • cpp[-1][-1]:等价于 *(*(cpp – 1) – 1),即 *(cp[1] – 1)
      • cp[1] 是 c + 2,cp[1] – 1 是 c + 1
      • *(c + 1) 即 c[1],指向字符串 "NEW"
    • cpp[-1][-1]+1:指针偏移1个字符,从 "NEW" 的第1个字符开始
      • "NEW" 的字符:'N'(0), 'E'(1), 'W'(2)
      • 从索引1开始:"EW"
    • 输出:EW

    3. 内存布局与指针变化示意图

    初始状态:

    指针数组 c(存储字符串地址):
    c[0] → "ENTER"
    c[1] → "NEW"
    c[2] → "POINT"
    c[3] → "FIRST"

    指针数组 cp(存储指向 c 元素的指针):
    cp[0] = c + 3 → 指向 c[3]
    cp[1] = c + 2 → 指向 c[2]
    cp[2] = c + 1 → 指向 c[1]
    cp[3] = c → 指向 c[0]

    三级指针 cpp:
    cpp → cp[0](初始状态)

    执行过程中的指针变化:

    第一个 printf 前:++cpp → cpp → cp[1]
    第二个 printf 前:++cpp → cpp → cp[2],且 cp[2] 被修改为 c(原为 c+1)
    第三个 printf 时:cpp 仍指向 cp[2](cpp[-2] 访问 cp[0])
    第四个 printf 时:cpp 仍指向 cp[2](cpp[-1] 访问 cp[1])

    4. 关键知识点总结

  • 多级指针:

    • char *c[]:指针数组,元素为 char*
    • char **cp[]:指针数组,元素为 char**
    • char ***cpp:三级指针,指向 char**
  • 前置自增/自减运算符:

    • ++cpp:先自增再使用,改变 cpp 本身的指向
    • –*++cpp:先自增 cpp,再解引用,再对解引用的值自减
    • 自增/自减会永久改变指针变量的值
  • 指针数组的初始化:

    • cp 数组的元素初始化为指向 c 中不同元素的指针
    • c + 3 指向 c[3],c + 2 指向 c[2],以此类推
  • 数组下标与指针运算的等价性:

    • cpp[-2] 等价于 *(cpp – 2)
    • cpp[-1][-1] 等价于 *(*(cpp – 1) – 1)
    • 负下标在指针运算中是合法的,只要结果指向有效的内存区域
  • 运算符优先级:

    • 前置 ++/– 优先级高于 *(解引用)
    • [](下标)优先级高于 +(加法)
    • *cpp[-2]+3 等价于 (*(cpp[-2])) + 3
  • 副作用与顺序:

    • 每个 printf 语句中的 ++cpp 会永久改变 cpp 的指向
    • 后续的 printf 基于前一个 printf 执行后的 cpp 状态
    • 本题中 cp[2] 的值在第二个 printf 中被修改为 c
  • 5. 预期输出结果

    POINT
    ER
    ST
    EW

    赞(0)
    未经允许不得转载:171主机测评 » 做几个指针题目巩固一下
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址