深入理解指针(7)

一、数组和指针笔试题解析(2)

(一)二维数组

cpp 复制代码
int main()
{
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));
	//48 a是数组名,单独放在sizeof内部,代表整个数组,3*4*4
	printf("%d\n", sizeof(a[0][0]));
	//4 第一个元素,即第一行的第一个元素,为int型
	printf("%d\n", sizeof(a[0]));
	//16 a[0]本来是第一行的数组名,但是数组名单独放在sizeof内部,代表整个数组的大小,1*4*4
	printf("%d\n", sizeof(a[0] + 1));
	//4 or 8 a[0]代表的是第一行数组名,也就是&a[0][0],+1后也就代表a[0][1]的地址,是地址就是4 or 8
	printf("%d\n", sizeof(*(a[0] + 1)));
	//4 a[0] + 1是第一行第二个元素的地址,解引用完就代表第二个元素,也就是a[0][1]
	printf("%d\n", sizeof(a + 1));
	//4 or 8 a代表二维数组首元素的地址,因为二维数组首元素是第一行所代表的一维数组
	//所以a也就是第一行的地址,+跳过一行,所以a+1就是第二行的地址,是地址就是4 or 8
	printf("%d\n", sizeof(*(a + 1)));
	//16 ------1------ 对a+1解引用得到的就是第二行,1*4*4
	//------2------ *(a+1)-->a[1] ,a[1]是第二行的数组名,所以sizeof(*(a + 1))就相当于
	//sizeof(a[1]),此时a[1]代表的是第二行整个的一维数组,1*4*4
	printf("%d\n", sizeof(&a[0] + 1));
	//4 or 8 &a[0]代表取出第一行的地址,+1代表现在是第二行的地址,是地址大小就是4 or 8
	printf("%d\n", sizeof(*(&a[0] + 1)));
	//16 解引用后代表第二行,1*4*4
	printf("%d\n", sizeof(*a));
	//16 a代表首元素地址,即第一行的地址,1*4*4
	printf("%d\n", sizeof(a[3]));
	//16 a[3]无需真实存在,仅仅通过类型就能推断出长度
	//a[3]是第四行的数组名,单独放在sizeof内部,代表整个第四行的一维数组,1*4*4
	//这里如果还不理解,我们不妨看sizeof(int)这里我们仅仅给出了类型,并没有什么具体占据了
	//内存空间的数据,所以sizeof()中的内容没必要真实存在
	return 0;
}

二、指针运算笔试题解析

(一)题目1

cpp 复制代码
#define  _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);
	//整个数组的类型是int(*)[5],所以我们需要进行强制类型转换为int型
	printf("%d,%d", *(a + 1), *(ptr - 1));
	return 0;
}

(二)题目2

cpp 复制代码
//在X86(32位)环境下
//假设结构体的大小是20个字节
//程序输出的结果是啥?
struct Test
{
    int Num;
    char* pcName;
    short sDate;
    char cha[2];
    short sBa[4];
} * p = (struct Test*)0x100000;
//这里定义了一个结构体指针,用来存放0x100000这个地址
//指针+-整数

int main()
{
    printf("%p\n", p + 0x1);
    //这里是结构体指针+1,跳过的是整个结构体指针
    //又因为结构体的大小是20个字节,所以
    //0x100000+20,这里的20是10进制的20
    //所以转换为16进制的最终输出结构为
    //00100014
    printf("%p\n", (unsigned long)p + 0x1);
    //这里强制类型转换为整型,所以就是普通+1
    //当然,或许有人会注意到这里的占位符还是%p
    //所以实际上在这里VS会报警告
    printf("%p\n", (unsigned int*)p + 0x1);
    //这里强制类型转换为整形指针
    return 0;
}

(三)题目3

cpp 复制代码
int main()
{
    int a[3][2] = { (0, 1), (2, 3), (4, 5) };//初始化
    //数组a的定义是本题的理解关键
    //注意到,0,1等数字都是用小括号括起来的,说明这并不是普通的二维数组
    //(0,1)本质上是一个逗号表达式,而逗号表达式只取决于最后一个结果;
    //所以此二维数组的值为1,3,5,0,0,0
    int* p;
    p = a[0];
    //a[0]是二维数组第一行的地址
    printf("%d", p[0]);
    //p[0]=*(p+0)=*(a[0]+0)=a[0][0],所以是第一个元素的值
    return 0;
}

(四)题目4

cpp 复制代码
//假设环境是x86环境,程序输出的结果是啥?
int main()
{
    int a[5][5];
    int (*p)[4];
    //p是一个数组指针,p指向的是含有4个整型元素的数组
    p = a;
    //将二维数组第一行的地址赋给p
    //但是我们会发现,这里a的类型为int(*)[5],与p并不一样
    //所以这里其实会报警告,但是来做题是没问题的
    printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
    // &a[4][2]的值我们很容易确定,那&p[4][2]又代表什么呢?
    //首先我们要明确p=a保证了p的首元素地址就是二维数组第一个元素的地址;
    //这里的p[4][2]与*(*(p+4)+2)是等价的,然后我们看下面那个图来理解一下
    return 0;
}

(五)题目5

cpp 复制代码
int main()
{
    int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int* ptr1 = (int*)(&aa + 1);
    //&aa是取出整个二维数组的地址,+1说明跳过了整个二维数组
    int* ptr2 = (int*)(*(aa + 1));
    //这里aa代表的是第一行数组的地址,+1后代表第二行数组的地址
    //*(aa + 1)等价于aa[1]也就是第二行首元素地址
    printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));

    return 0;
}

(六)题目6

cpp 复制代码
int main()
{
    char* a[] = { "work","at","alibaba" };
    //这里是指针数组,每一个元素都是一个字符串的首地址
    //可以类比char*p="abcdef"中的p就是a的地址
    char** pa = a;
    //因为a是数组首元素的地址,即&a[0],所以pa是二级指针
    //指向&a[0]
    pa++;
    //pa++后,指向的是&a[1]
    printf("%s\n", *pa);
    //【这里一定要注意】,printf的占位符为%s的时候,逗号后面
    //跟的一定是该字符串首元素的地址!!!
    return 0;
}

(七)题目7

cpp 复制代码
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;
}

本道题是以上指针题目中,分析最繁琐,逻辑链最长的一道题。

想要搞懂这道题,我们就要画出以下的空间(地址)指示关系图。

相关推荐
张人玉7 小时前
西门子PLC地址知识点
算法·西门子plc
sheeta19987 小时前
LeetCode 每日一题笔记 日期:2025.12.17 题目:3573.买卖股票的最佳时机Ⅴ
笔记·算法·leetcode
榮十一7 小时前
10道SQL练习题及答案
数据库·sql·算法
l1t7 小时前
Javascript引擎node bun deno比较
开发语言·javascript·算法·ecmascript·bun·精确覆盖·teris
古城小栈7 小时前
Go 异步编程:无锁数据结构实现原理
java·数据结构·golang
仰泳的熊猫8 小时前
1094 The Largest Generation
数据结构·c++·算法·pat考试
LYFlied8 小时前
【每日算法】LeetCode 739. 每日温度:从暴力遍历到单调栈的优雅解决
前端·算法·leetcode·面试·职场和发展
铭哥的编程日记8 小时前
DFS + 剪枝 解决 全排列系列问题 (所有题型)
算法·深度优先·剪枝
yaoh.wang8 小时前
力扣(LeetCode) 67: 二进制求和 - 解法思路
python·程序人生·算法·leetcode·面试·职场和发展·跳槽