当前位置:网站首页>【C】(笔试题)指针与数组,指针
【C】(笔试题)指针与数组,指针
2022-07-05 00:13:00 【心辛向荣】
博客主页:心辛向荣
系列专栏:【从0到1,C语言学习】
一句短话:你若盛开,蝴蝶自来!
博客说明:尽己所能,把每一篇博客写好,帮助自己熟悉所学知识,也希望自己的这些内容可以帮助到一些在学习路上的伙伴,文章中如果发现错误及不足之处,还望在评论区留言,我们一起交流进步!
前言
这篇博客的重点是对于指针以及数组的理解与运用,掌握这些题目,对于指针与数组的理解与认识会有很大的提高!
一.关于指针和数组的笔试题
- 数组名的意义:
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
- 除此之外所有的数组名都表示首元素的地址。
- strlen和sizeof的区别:
- strlen是求字符串长度的,关注的是字符串中的\0,计算的是\0之前出现的字符的个数
strlen是库函数,只针对字符串
- sizeof只关注占用内存空间的大小,不在乎内存中放的是什么
sizeof是操作符
- 正确理解题目:
对于这部分的理解,sizeof(&…)计算的是一个指针类型的大小,也就是地址,地址的大小在32位环境(x86)下是4字节,在64位环境下(x64)是8字节;但对应下面的例题,不能只是单单理解打印结果,更重要的的是理解到这个地址到底是谁的地址;同样在理解strlen求字符串长度时,也要对地址有正确的认识和理解!
1.一维数组
#include<stdio.h>
int main()
{
int a[] = {
1,2,3,4 };
printf("%d\n", sizeof(&a + 1));//4/8
//&a取出的是数组的地址
//&a对应的指针类型是int(*)[4]
//&a+1 是从数组a的地址向后跳过了一个(4个整型元素)数组的大小
//&a+1还是地址,是地址就是4/8字节
printf("%d\n", sizeof(&a[0]));//4/8
//&a[0]就是第一个元素的地址
//计算的是地址的大小
printf("%d\n", sizeof(&a[0] + 1));//4/8
//&a[0]+1是第二个元素的地址
//大小是4/8个字节
//&a[0]+1 ---> &a[1]
printf("%d\n", sizeof(a));//16
//sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小,单位是字节
printf("%d\n", sizeof(a + 0));//4
//a不是单独放在sizeof内部,也没有取地址,
//所以a就是首元素的地址,a+0还是首元素的地址
//是地址,大小就是4/8个字节
printf("%d\n", sizeof(*a));//4
//*a中的a是数组首元素的地址,*a就是对首元素的地址解引用,找到的就是首元素
//首元素类型是整形,大小是4个字节
printf("%d\n", sizeof(a + 1));
//这里的a是数组首元素的地址
//a+1是第二个元素的地址
//sizeof(a+1)就是地址的大小
printf("%d\n", sizeof(a[1]));//4
//计算的是第二个元素的大小
printf("%d\n", sizeof(&a));//4/8
//&a取出的数组的地址,数组的地址,也是个地址
printf("%d\n", sizeof(*&a));//16
//第1种理解方法
//&a----> int(*)[4]
//&a拿到的是数组名的地址,类型是 int(*)[4],是一种数组指针
//数组指针解引用找到的是数组
//*&a ---> a
//
//第2种理解方法
//&和*抵消了
//*&a ---> a
return 0;
}
运行结果:
2.字符数组
#include<stdio.h>
int main()
{
char arr[] = {
'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));//6
//sizeof(数组名),计算整个数组的大小
printf("%d\n", sizeof(arr + 0));//4/8
//arr + 0 是数组首元素的地址
printf("%d\n", sizeof(*arr));//1
//*arr就是数组的首元素,大小是1字节
//*arr --> arr[0]
//*(arr+0) --> arr[0]
printf("%d\n", sizeof(arr[1]));//1
//计算数组第二个元素的大小
printf("%d\n", sizeof(&arr));//4/8
//&arr是数组的地址,是地址就是4/8个字节
printf("%d\n", sizeof(&arr + 1));//4/8
//&arr + 1是数组后的地址
printf("%d\n", sizeof(&arr[0] + 1));//4/8
//&arr[0] + 1是第二个元素的地址
return 0;
}
运行结果:
#include<stdio.h>
#include <string.h>
int main()
{
char arr[] = {
'a','b','c','d','e','f' };
printf("%d\n", strlen(arr));//随机值
//没有'\0'作为结束标志
printf("%d\n", strlen(arr + 0));//随机值
//printf("%d\n", strlen(*arr));//--> strlen('a');-->strlen(97);//野指针
// 程序会报错
//printf("%d\n", strlen(arr[1]));//-->strlen('b')-->strlen(98);
printf("%d\n", strlen(&arr));//随机值
printf("%d\n", strlen(&arr + 1));//随机值-6
printf("%d\n", strlen(&arr[0] + 1));//随机值-1
return 0;
}
运行结果:
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "abcdef";
//[a b c d e f \0]
printf("%d\n", strlen(arr));//6
printf("%d\n", strlen(arr + 0));//6
//printf("%d\n", strlen(*arr));//err
// 野指针,程序会报错
//printf("%d\n", strlen(arr[1]));//err
printf("%d\n", strlen(&arr));//6
printf("%d\n", strlen(&arr + 1));//随机值
printf("%d\n", strlen(&arr[0] + 1));//5
return 0;
}
运行结果:
#include<stdio.h>
int main()
{
char arr[] = "abcdef";
//[a b c d e f \0]
printf("%d\n", sizeof(arr));//7
printf("%d\n", sizeof(arr + 0));//4/8
printf("%d\n", sizeof(*arr));//1
printf("%d\n", sizeof(arr[1]));//1
printf("%d\n", sizeof(&arr));//4/8
printf("%d\n", sizeof(&arr + 1));//4/8
printf("%d\n", sizeof(&arr[0] + 1));//4/8
return 0;
}
运行结果:
#include<stdio.h>
int main()
{
char* p = "abcdef";
printf("%d\n", sizeof(p));//4/8
printf("%d\n", sizeof(p + 1));//4/8
printf("%d\n", sizeof(*p));//1
printf("%d\n", sizeof(p[0]));//1
printf("%d\n", sizeof(&p));//4/8
printf("%d\n", sizeof(&p + 1));//4/8
printf("%d\n", sizeof(&p[0] + 1));//4/8
return 0;
}
运行结果:
#include<stdio.h>
#include<string.h>
int main()
{
char* p = "abcdef";
printf("%d\n", strlen(p));//6
printf("%d\n", strlen(p + 1));//5
//printf("%d\n", strlen(*p));//野指针,报错
//printf("%d\n", strlen(p[0]));//野指针,报错
printf("%d\n", strlen(&p));//随机值
printf("%d\n", strlen(&p + 1));//随机值
//由于p的地址占4/8格字节,无法确定'\0'会不会在其中出现
//所以推断不出这俩个随机值之间的关系
//这俩个随机值之间相差的值也是一个随机值
printf("%d\n", strlen(&p[0] + 1));//5
//p[0]-->*(p+0)
return 0;
}
运行结果:
3.二维数组
#include<stdio.h>
int main()
{
int a[3][4] = {
0 };
printf("%d\n", sizeof(a));//48
printf("%d\n", sizeof(a[0][0]));//4
printf("%d\n", sizeof(a[0]));//16
//a[0]是第一行这个一维数组的数组名,
//单独放在sizeof内部,a[0]表示第一个整个这个一维数组;
//sizeof(a[0])计算的就是第一行的大小
printf("%d\n", sizeof(a[0] + 1));//4/8
//a[0]并没有单独放在sizeof内部,也没取地址,a[0]就表示首元素的地址
//就是第一行这个一维数组的第一个元素的地址,
//a[0] + 1就是第一行第二个元素的地址
printf("%d\n", sizeof(*(a[0] + 1)));//4
//a[0] + 1就是第一行第二个元素的地址
//*(a[0] + 1))就是第一行第二个元素
printf("%d\n", sizeof(a + 1));//4/8
//a虽然是二维数组的地址,但是并没有单独放在sizeof内部,也没取地址
//a表示首元素的地址,二维数组的首元素是它的第一行,a就是第一行的地址
//a+1就是跳过第一行,表示第二行的地址
printf("%d\n", sizeof(*(a + 1)));//16
//*(a + 1)是对第二行地址的解引用,拿到的是第二行
//*(a+1)-->a[1]
//sizeof(*(a+1))-->sizeof(a[1])
printf("%d\n", sizeof(&a[0] + 1));//4/8
//&a[0] - 对第一行的数组名取地址,拿出的是第一行的地址
//&a[0]+1 - 得到的是第二行的地址
printf("%d\n", sizeof(*(&a[0] + 1)));//16
printf("%d\n", sizeof(*a));//16
//a表示首元素的地址,就是第一行的地址
//*a就是对第一行地址的解引用,拿到的就是第一行
printf("%d\n", sizeof(a[3]));//16
//这里并不会造成越界访问,sizeof在计算时关心的的是a[3]的类型
//并不会真的去访问a[3],计算它和a[0]是一样的
printf("%d\n", sizeof(a[0]));//16
return 0;
}
运行结果:
二.关于指针的笔试题
题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;
}
运行结果:
解析:
&a+1得到的是数组之后的地址,将这个地址强制类型转换为int * 类型,并赋值给int * 类型的prt,此时 * prt只能访问一个整形元素的空间;
*(a+1)中的a是数组首元素地址,a + 1是数组第二个元素的地址,由于prt只有一个整形空间的访问权限,prt - 1得到数组最后一个元素的地址。
题2:
#include <stdio.h>
//已知,在32位环境下(x86)
//结构体Test类型的变量大小是20个字节
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}* p = (struct Test*)0x100000;
//假设p 的值为0x100000;如下表表达式的值分别为多少?
int main()
{
printf("%p\n", p + 0x1);
//0x100000+20-->0x100014
printf("%p\n", (unsigned long)p + 0x1);
//1,048,576+1 --> 1,048,577
//0x100001
printf("%p\n", (unsigned int*)p + 0x1);
//0x100000+4-->0x100004
return 0;
}
运行结果:
解析:
p的类型为struct Test * ,p+1跳过一个结构体(20字节)空间的大小,20转化成16进制就是14;
unigned int 是一个无符号整形,进行整形运算即可;
将p由结构体指针类型强制类型转化为unigned int* 类型,此时p+1跳过4个字节
题3:
#include <stdio.h>
int main()
{
int a[4] = {
1, 2, 3, 4 };
int* ptr1 = (int*)(&a + 1);
int* ptr2 = (int*)((int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2);
return 0;
}
运行结果:
解析:
prt1是数组后的地址,类型为int * ,prt1[-1]–>*(prt-1),prt-1向前跳过四个字节;
将数组首元素地址强制类型转换为一个整形,此时 + 1进行的是整形运算;再将 + 1后的结果强制类型转换为int 类型赋值给prt2,此时prt2是a向后跳过一个字节的地址,* prt2访问4个字节的内容,
而在vs环境下,数据采用的是小端存储模式,是倒着存的,看下图理解:
%x以16进制的形式打印。
题4:
#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;
}
运行结果:
解析:
这道题有一个忽悠人的点,题中的初始化用的是( )而不是 { } ;而题目( )中的是逗号表达式;
所以数组当中的内容实际上相当于int a[3][2] = {1,2,3};
题5:
#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是二维数组首元素地址,也就是二维数组第一行的地址,其类型为int ( * ) [5],具有5个字节的访问权限;将赋值给p,p的类型为int ( * )[4],具有4个字节的访问权限;
指针 - 指针计算出的俩指针之间元素的个数,高地址 - 低地址得到的结果是整数,反之是负数;参照下图理解:
从图中可以观察到,俩个地址之间的元素个数为4,低地址 - 高地址,所以应该是 - 4;
将结果以%p和%d打印;%d打印出的结果为 - 4;
将 - 4以地址的形式打印;%p可以理解为一个无符号数,求出 - 4的补码转换为16进制即可;
原码:10000000 00000000 00000000 00000100
反码:11111111 11111111 11111111 11111011
补码:11111111 11111111 11111111 11111100
补码的十六进制:ff ff ff fc
题6:
#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;
}
运行结果:
解析:
&aa+1得到的是数组后的地址,将其强制类型转换为int * 类型,赋值给prt1,此时prt1具有4个字节的访问权限;所以prt1-1得到数组最后一个元素的地址;
aa+1(相当于&arr[1])得到二维数组第二行的地址,* *(aa+1)得到第二行(相当于 * &aa[1]–>aa[1]);此时 ** (aa + 1 )并没有加&或者放在sizeof中,所以此时它相当于第二行数组首元素地址,题目这里进行强制类型转换属于迷惑行为,有没有都一样;将其赋值给prt2,prt2-1就是第一行最后一个元素的地址了!
题7:
#include <stdio.h>
int main()
{
char* a[] = {
"work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
运行结果:
解析:
看图更直观,如下,
题8:
#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;
}
运行结果:
解析:
要注意,如:++cpp,会将cpp自身的值改变;而cpp+1不会改变cpp本身;要注意区分,防止做题时混淆,具体理解如下图:
结语:
各位小伙伴,看到这里就是缘分嘛,希望我的这些内容可以给你带来那么一丝丝帮助,可以的话三连支持一下呗!!! 感谢每一位走到这里的小伙伴,我们可以一起学习交流,一起进步!!!加油!!!
边栏推荐
- [paper reading] Tun det: a novel network for meridian ultra sound nodule detection
- URL和URI
- 如何将自己的代码作品快速存证,已更好的保护自己劳动成果
- 2022.07.03(LC_6108_解密消息)
- JS convert pseudo array to array
- Actual combat simulation │ JWT login authentication
- [Peking University] tensorflow2.0-1-opening
- Advanced template
- IT转测试岗,从迷茫到坚定我究竟付出了什么?
- go踩坑——no required module provides package : go.mod file not found in current directory or any parent
猜你喜欢
Parsing of XML
如何在外地外网电脑远程公司项目?
In the enterprise, win10 turns on BitLocker to lock the disk, how to back up the system, how to recover when the system has problems, and how to recover quickly while taking into account system securi
Using the uniapp rich text editor
Hash table, hash function, bloom filter, consistency hash
快解析内网穿透帮助企业快速实现协同办公
同事的接口文档我每次看着就头大,毛病多多。。。
图解网络:什么是网关负载均衡协议GLBP?
The waterfall flow layout demo2 (method 2) used by the uniapp wechat applet (copy and paste can be used without other processing)
How to use fast parsing to make IOT cloud platform
随机推荐
Go step on the pit - no required module provides package: go mod file not found in current directory or any parent
Netcore3.1 JSON web token Middleware
How to do the project of computer remote company in foreign Internet?
[error reporting] "typeerror: cannot read properties of undefined (reading 'split')“
uniapp微信小程序拿来即用的瀑布流布局demo2(方法二)(复制粘贴即可使用,无需做其他处理)
Hisilicon 3559 universal platform construction: YUV422 pit stepping record
go踩坑——no required module provides package : go.mod file not found in current directory or any parent
JS convert pseudo array to array
How to apply for PMP project management certification examination?
The waterfall flow layout demo2 (method 2) used by the uniapp wechat applet (copy and paste can be used without other processing)
雅思考试流程、需要具体注意些什么、怎么复习?
C语言中sizeof操作符的坑
Introduction to ACM combination counting
[论文阅读] TUN-Det: A Novel Network for Thyroid Ultrasound Nodule Detection
巩固表达式C# 案例简单变量运算
Acwing164. Accessibility Statistics (topological sorting +bitset)
初识ROS
[paper reading] cavemix: a simple data augmentation method for brain vision segmentation
IT转测试岗,从迷茫到坚定我究竟付出了什么?
How to reduce the stock account Commission and stock speculation commission? Is it safe to open an online account