0基础C语言积跬步之深入理解指针(5下)

目录

1.sizeof 和 strlen 的对比

2.数组和指针笔试题分析

3.指针运算笔试题分析


三、指针运算笔试题分析

(1)题一:程序的结果是什么?

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

&a+1跳过的是整个数组,然后我们强制类型转换为int*类型,并存入指针变量ptr中,在我们打印中,用%d打印整数,*(a+1)指的是对数组第二个元素的地址进行解引用,所以会打印2;*(ptr-1),ptr指向的是a[5],ptr-1指向的就是a[4],所以会打印5;结果会打印 2,5

我们来检验一下我们分析的结果:

运行截图:

(2)题二:程序的结果是什么?

我们采用x86,三十二的环境,struct结构体所占内存空间为20个字节

cpp 复制代码
#include <stdio.h>
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;
}

p + 0x1:0x指的是十六进制,我们把0x10 00 00强制类型转化为struct Text*类型,也就是转化成了一个地址,并存入指针变量p,此时就会把0x10 00 00当成一个地址看待,0x10 00 00实际上是0x00 10 00 00,1前面的两个0会自动省略,0x1也就是0x00 00 00 01,struct Text*类型的指针+1,跳过20个字节,因为struct Text这个结构体类型设计出的大小是20个字节,20个字节换算成十六进制数字就是14,所以p+0x1就等,0x10 00 14;

(unsigned long)p + 0x1:是把p转化成一个无符号长整型的数据,整型加1,那值就是加1,所以0x10 00 00+0x1就等于0x10 00 01;

(unsigned int*)p + 0x1:把p转化成了一个无符号整型指针的类型,加1跳过一个整型的大小,也就是四个字节,转化成十六进制是4,所以(unsigned int*)p + 0x1就等于0x10 00 04

我们来检验一下我们分析的结果:

运行截图:

(3)题三:程序的结果是什么?

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

数组a中存放的是三个逗号表达式,整个逗号表达式的结果是最后一个表达式的结果,所以(0,1)的结果是1,(2,3)的结果是3,(4,5)的结果是5,所以数组a中第一行存的元素是1,3;第二行存的是5,0,第三行存的是0,0;我们创建了个指针变量p存放a[0],首行整行的地址,这个地址在数值上等同于首行第一个元素的地址,所以我们打印的是1

我们来检验一下我们分析的结果:

运行截图:

(4)题四:程序的结果是什么?

我们采用x86,三十二的环境

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

二维数组a一行有五个元素,但数组指针p指向的数组一行只能存四个元素,我们把a的首元素地址传给了p指针,此时就能通过p指针去访问a这个数组,但是我们访问的时候会发生指针偏移,也就是p[4][2]和a[4][2]指向的不是同一块空间,这个就需要我们来分析

&p[4][2]==&*(*(p+4)+2)==*(p+4)+2,p+4,因为p一行只有4个元素,p+4指向的是第五行的起始地址,从p的第一行起始地址越到第五行起始地址,也就是越过了64个字节,然后对*(p+4)进行解引用表示找到了第五行第一个元素的地址,然后再加2,再跳过两个整型元素,也就是从起始位置一共越了68个字节

而&a[4][2],指向的就是a的第五行第三个元素,从a的起始位置开始越过了88个字节,所以偏移量是88-68=20个字节,也就是4个元素,所以&p[4][2] - &a[4][2]等于-4,用%d打印时打印出来的结果就是-4,但用%p打印时,会把它理解成一个地址打印并以十六进制出来,整型占4个字节,内存中存储着32个二进制位数,程序理解成这32位数是地址,并以十六进制打印出来

然后因为-4:

  • 原码是10000000 00000000 00000000 00000100
  • 反码是111111111 111111111 111111111 111111011
  • 补码是111111111 111111111 111111111 111111100

我们将补码转换成十六进制就是FFFFFFFC,所以用&p打印会出现FFFFFFFC

我们来检验一下我们分析的结果:

运行截图:

(5)题五:程序的结果是什么?

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

*(ptr1 - 1),我们首先分析数组,这个aa二维数组中,第一行存储的是1 2 3 4 5 ,第二行存储的是6 7 8 9 10,&aa+1跳过整个二维数组,指向数组整体后面的第一个 int 地址,逻辑上等价于 aa[1][4] 的下一个位置,也就是a[1][5]的位置。然后因为&aa+1的类型是int(*)[2][5],所以我们强制类型转换为int*类型,并存入int*类型的指针里,当ptr1-1时,其实也就是减掉了一个int整型的字节,此时我们ptr1-1的地址指向aa元素中的aa[1][4]这个元素的地址位置,解引用就得到10,所以*(ptr1 - 1)会打印10

*(ptr2 - 1),aa是首行的地址,+1变成第二行的地址,对第二行的地址解引用就获得了第二行第一个元素的地址,我们强制类型转化为int*,传给指针ptr2,此时ptr2指向a[1][0],ptr2减1也就是减一个int类型的字节大小,就会指向a[0][4]地址,解引用该地址得到的数就是5,所以*(ptr2 - 1)会打印5

我们来检验一下我们分析的结果:

运行截图:

(6)题六:程序的结果是什么?

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

a指向的是这个字符指针数组的首元素的地址,并将这个地址存入了二级指针pa中,pa++,因为pa所指向的元素额的类型是char*,所以++跳过四个字节,指向了a数组的第二个元素的地址,这个地址相当于一个盒子,存的是字符串 "at" 首字符的地址,还需要对这个盒子进行解引用才能得到a[1]中首字符的地址,所以*pa就得到了a[1]的首字符地址,所以会打印a的第二个元素"at"

我们来检验一下我们分析的结果:

运行截图:

(7)题七:程序的结果是什么?

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

cp中存的是其实就是c数组中的元素倒过来存了,cpp存的就是cp的起始地址

printf("%s\n", **++cpp),**++cpp是先++后解引用,++cpp指向的就是cp的第二个元素的地址,*cpp解引用得到cp第二个元素c+2,再解引用就是对cp第二个元素c+2这个地址进行解引用,得到的是"POINT"这个元素的地址,所以会打印POINT

printf("%s\n", *-- * ++cpp + 3),上一步中cpp已经指向了cp[1],然后这里先++,cpp就会指向cp[2],cp[2]也就是c+1,然后*cpp也就是对c+1这个地址进行解引用,找到了c[1],然后--指向了c[0],表示这一整个字符串的地址,再解引用找到了c[0]的首字符地址,为char*类型,最后+3,跳过3个char类型,也就是3个字节然后指向了c[0]中的'E',最后打印出ER

printf("%s\n", *cpp[-2] + 3),cpp[-2]等于*(cpp-2),上一步中++cpp找到了cp[2],这一步-2就找到了cp[0],解引用后找到的就是c+3这个地址对应的值,也就是"FIRST"的地址,*(*(cpp-2))接着我们又解引用,找到了"FIRST"的首字符地址,最后+3跳过三个字符,指向了'S',最后用%s打印出ST

printf("%s\n", cpp[-1][-1] + 1),cpp[-1][-1]可以写成*(*(cpp-1)-1),上一步并没有改变cpp指向的位置,只是将它减2的值临时使用而已,所以cpp依然指向第二句指向的cp[2],我们这里*(cpp-1)表示找到了cp[1]的地址并解引用找到了"POINT"的地址,然后我们再*(*(cpp-1)-1):-1后解引用,-1指向了"NEW",解引用找到了"NEW"的首字符地址,最后 cpp[-1][-1] + 1,最后+1,跳过一个字符,就指向了'E'这个字符的地址,最终用%s打印,就会打印出EW

我们来检验一下我们分析的结果:

运行截图:

感谢大家的观看,佬们互三互三,关注我必回关!下章见!

相关推荐
一直不明飞行1 小时前
Java的equals(),hashCode()应该在什么时候重写
java·开发语言·jvm
盲敲代码的阿豪2 小时前
Python 入门基础教程(爬虫前置版)
开发语言·爬虫·python
basketball6162 小时前
C++ 构造函数完全指南:从入门到进阶
java·开发语言·c++
互联科技报2 小时前
2026超融合选型:Top5品牌与市场格局解读
开发语言·perl
weixin199701080162 小时前
[特殊字符] 智能数据采集:数字化转型的“数据石油勘探队”(附Python实战源码)
开发语言·python
想唱rap3 小时前
IO多路转接之poll
服务器·开发语言·数据库·c++
@杰克成3 小时前
Java学习30
java·开发语言·学习
三品吉他手会点灯3 小时前
C语言学习笔记 - 40.数据类型 - scanf函数的编程规范与非法输入处理
c语言·开发语言·笔记·学习
凯瑟琳.奥古斯特3 小时前
数据冗余与规范化的本质[数据库原理]
开发语言·数据库·职场和发展