重生之我在异世界学编程之C语言:深入指针篇(下)

发布于:2024-12-06 ⋅ 阅读:(39) ⋅ 点赞:(0)

大家好,这里是小编的博客频道
小编的博客:就爱学编程

很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!!

在这里插入图片描述


那接下来就让我们开始遨游在知识的海洋!

题集

(1)指针笔试题1

判断代码运行结果

#include<stdio.h>
#include<string.h>
int main() {
	char  arr[] = "abcd";
	//char arr[] = {'a', 'b', 'c', 'd', '\0'}
	printf("%d\n", sizeof(arr)); 
	printf("%d\n", sizeof(arr + 0 ));  
	printf("%d\n", sizeof (*arr));   
	printf("%d\n", sizeof(arr[1]));   
	printf("%d\n", sizeof(&arr));    
	printf("%d\n", sizeof(&arr + 1));   
	printf("%d\n", sizeof(&arr[0] + 1));   

	printf("%d\n", strlen(arr));   
	printf("%d\n", strlen(arr + 0));    
	printf("%d", strlen (*arr));     
    printf("%d", strlen(arr[1]));     
	printf("%d\n", strlen (&arr));     
	printf("%d\n", strlen (&arr + 1));    
	printf("%d\n", strlen (&arr[0] + 1));    
	
	return 0;
}

建议思考写下自己的答案再看后文进行核对与理解

答案 解析
5 sizeof()内仅有arr(数组名), arr代表整个数组,所以sizeof计算的是整个数组的大小——5(单位:字节)
4/8 除了仅有arr&arrarr代表的是整个数组,其他arr代表的都是数组首元素的地址,地址的大小就是4/8(至于4/8取决于32位机器或64位机器)
1 *arr就是数组首元素'a'(int),大小为1
1 arr[1] == *(arr + 1),就是数组的第二个元素,大小为1
4/8 &arr中的arr代表的是整个数组,所以&arr代表的是整个数组的地址,大小就是4/8
4/8 &arr + 1中的arr代表的是整个数组,所以&arr + 1代表的是整个数组后的和数组一样大小的连续元素的地址,大小就是4/8
4/8 第二个元素的地址
4 strlen(arr)arr代表的是数组首元素的地址,所以strlen()从数组首元素开始数到至’\0’,结果就为:4
4 strlen(arr+0)arr代表的是数组首元素的地址,所以strlen()从数组首元素开始数至’\0’,结果就为:4
非法访问 *arr表示数组首元素,而strlen()要的是地址,所以非法访问
非法访问 同上
4 &arr取出了整个数组的地址,就数整个数组,结果就为:4
随机 &arr + 1 就跳过了整个数组,不知道什么时候出现’\0’,也不知道任何元素的信息,所以打印的是个随机数
3 &arr[0] + 1 就跳过了首元素,结果就为:3

考察:一维数组的数组名——特殊与一般
涉及:一维数组,strlen(),sizeof()


(2)指针笔试题2

判断代码运行结果

#include<stdio.h>
#include<string.h>
int main() {
	char* p = "abcd";
	//这个代码的意思:把首元素的地址(a的地址)放到指针变量p中
	//p就相当于一般的arr(除了两种特殊情况除外)(首元素的地址)
	printf("%d\n", sizeof(p)); //地址的大小就是4/8(字节)(至于4/8取决于32位机器或64位机器)
	printf("%d\n", sizeof(p + 0 ));   //同上
	printf("%d\n", sizeof (*p));   //*p就是数组首元素'a'(char),大小为1(字节)
	printf("%d\n", sizeof(p[1]));   //p[1] == *(p + 1),就是数组的第二个元素,大小为1(字节)
	printf("%d\n", sizeof(&p));    //&p中的p代表的是数组首元素的地址,所以&p代表的是存储指针变量p的地址,大小就是4/8
	printf("%d\n", sizeof(&p + 1));   //&p + 1中的p代表的是数组首元素,所以&p + 1代表的是存储指针变量p的地址处后一位的地址,大小就是4/8
	printf("%d\n", sizeof(&p[0] + 1));   //b的地址,大小就是4/8

	printf("%d\n", strlen(p));   //strlen(p)中p代表的是数组首元素的地址,所以strlen函数从数组首元素开始数至'\0',结果就为:4
	printf("%d\n", strlen(p + 0));    //strlen(p + 0)中p代表的是数组首元素的地址,所以strlen函数从数组首元素开始数至'\0',结果就为:4
	//printf("%d", strlen (*p));     //*p表示数组首元素,而strlen函数要的是地址,所以非法访问
	//printf("%d", strlen(p[1]));     //同上
	printf("%d\n", strlen (&p));     //&p取出了数组首元素的地址的地址,不知道什么时候出现'\0',也不知道任何元素的信息,所以打印的是个随机数
	printf("%d\n", strlen (&p + 1));    //&p + 1 就跳过了整个数组,不知道什么时候出现'\0',也不知道任何元素的信息,所以打印的是个随机数
	printf("%d\n", strlen (&p[0] + 1));    //&p[0] + 1 就跳过了首元素,结果就为:3

	return 0;
}

考察:字符指针——把首元素的地址(a的地址)放到指针变量p中
涉及:字符指针,strlen()


(3)指针笔试题3

判断代码运行结果


#include<stdio.h>
struct test {
	int Num;
	char* pcname;
	short sDate;
	char cha[2];
	short sBa[4];
}* p;
//假设 * p = 0x00000000;
//已知结构体变量test的大小为20字节;
int main() {
	printf("%p\n", p + 1);
	//指针加1跳过整个指针权限的内容(步长)(解引用权限),所以这里跳过了整个结构体变量,也就是20个字节,结果用十六进制表示就是00000014
	printf("%u\n", (unsigned long)p + 1);
	//先把结构体指针变量p强转成无符号长整型变量,再加1就是让一个整型变量加1,结果就是加1,用十进制表示就是1
	printf("%p\n", (unsigned int*)p + 1);
	//先把结构体指针变量p强转成整型指针变量,再加1就是让一个整型指针变量加1,指针加1跳过整个权限的内容,所以这里跳过了整个整型变量,也就是4个字节,结果用十六进制表示就是00000004
}

考察:指针加1的意义,是跳过一个步长的地址
涉及:结构体指针,基本指针


(4)指针笔试题4

判断代码运行结果

//x86,小端
#include<stdio.h>
   int main() {
	int a[4] = { 1, 2, 3, 4 };
	int* p = (int*)(&a + 1);
	int* p1 = (int*)((int)(a + 1) + 1);   //如果我们定义一个:int* pa = a;   则a + 1就等价于*(pa + 1)
	printf("%x\n", p[-1]);  
	//1.p[-1] == *(p - 1),因为p的类型为int*,所以p-1就是向前挪动一个整型(4个字节)的长度,指向了第4个元素的第一个字节的最左端
	//2.再进行解引用,根据p的访问权限可知从当前位置向后访问一个整型(4个字节),就得到了数组的第4个元素——4
	//3.又因为%x是用来打印十六位进制数且会去掉前面的0,所以打印就是4
	printf("%x\n", * p1); 
	//1.a + 1: 
	             //低地址                                       高地址//低地址                                       高地址                                                         
	             //01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00    //01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
	   //地址设为:00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f    //00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f    
	   //指针位置:pa                                              -->              pa    
	//2.(int)(a + 1): 把 地址04 强转成一个大小等于 04 的整型数据
	//3.(int)(a + 1) + 1:一个整型数据加1,就是数值加1,此时((int)(a + 1) + 1)就得到了一个数值大小为 05 的整型数据
	//4.((int*)((int)(a + 1) + 1)):把数值大小为 05 的整型数据强转为 地址05
	//5.int* p1 = (int*)((int)(a + 1) + 1):把上面得到的 05的地址 赋给 类型为int*的 整形指针变量p1
	//6.printf("%x\n", * p1):再进行解引用,根据p的访问权限可知从当前位置向后访问一个整型(4个字节),就得到了00 00 00 03(小端存储),转换为03 00 00  00(原值)(小端存储是以字节为单位的)
	//7.又因为%x是用来打印十六位进制数且会去掉前面的0,所以打印就是3000000
	return 0;
}

考察并涉及:小端存储,%x的作用,指针运算


(5)指针笔试题5

判断代码运行结果

#include<stdio.h>
int main() {
	int arr[] = { 1, 2, 3, 4 };
	int* p = (int*)(&arr + 1);
	printf("%d\n", *(p - 1));
	//没什么好讲的
	printf("%d\n", *(arr + 1));
	//同上
	return 0;
}

考察并涉及:一维数组数组名


(6)指针笔试题6

判断代码运行结果

#include<stdio.h>
int main() {
	int arr[2][3] = { (1, 2), (3, 4), (5, 6)};
	//逗号表达式的值就是','右边的表达式的结果(但要注意:','左边的表达式也会执行,且是先执行的)
	//所以该二维数组的元素为://2 4
	                          //6 0
	                          //0 0
	int* p;
	p = arr[0];
	printf("%d\n", p[1]);
	//两种理解p[1]的方法
	//1.用数组的格式理解:p = arr[0],则p[1] = arr[0][1] = 4;
	//2.用数组的本质理解:arr[0]作为二维数组的第一行的数组元素的数组名,代表的是数组第一行的首元素的地址,即——&arr[0][0],
	                    //p[1] == *(p + 1) == *(&arr[0][0] + 1) == 4
	return 0;
}

考查并涉及:指针与二维数组的关系和逗号表达式的作用


(7)指针笔试题7

判断代码运行结果

#include<stdio.h>
int main() {
	int arr[5][5];
	//数组元素:|0 0 0 0 0 | 0 0 0 0 0 | 0 0 0 0 0 | 0 0 0 0 0 | 0 0 0 0 0 | 
	//             arr[0]      arr[1]     arr[2]      arr[3]      arr[4]
	//地址设为:|0 1 2 3 4 | 5 6 7 8 9 | a b c d e | f 0 1 2 3 | 4 5 6 7 8 |
	int(*p)[4] = (int(*)[4])arr;
	//arr作为二维数组的数组名,类型为:int(*)[5],与数组指针变量p(类型为:int(*)[4])类型基本一致,所以强转之后可以和数组名一样理解
	printf("%p,%d\n", &p[4][2] - &arr[4][2], &p[4][2] - &arr[4][2]);
	//以p为二维数组的数组名的二维数组:
	//    元素:| 0 0 0 0 | 0 0 0 0 | 0 0 0 0 | 0 0 0 0 | 0 0 0 0 | 0 0 0 0 | 0 
	//             p[0]      p[1]      p[2]      p[3]       p[4]     p[5]     p[6]
	//地址设为:| 0 1 2 3 | 4 5 6 7 | 8 9 a b | c d e f | 0 1 2 3 | 4 5 6 7 | 8
	//所以 &p[4][2] - &arr[4][2] == 12 - 16 == -4,再根据占位符的功能打印的结果为:FFFFFFFC(十六进制),-4(十进制)
	return 0;
}

考察:%p打印的是无符号十六进制的地址值
涉及:二维数组,占位符%p,数组指针。


(8)指针笔试题8

判断代码运行结果

#include<stdio.h>
int main() {
	char* arr[3] = { "hello", "world", "bite" };
	//arr[0] == &'h' , arr[1] == &'w', arr[2] == &'b'
	//即数组元素:&'h'| &'w' | &'b'
	//元素名:  arr[0]|arr[1]|arr[2]

	char** pa = arr;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

考查:字符指针在接收字符串时,接受的是字符串的首元素的地址
涉及:指针数组,占位符%s。


(9)指针笔试题9

判断代码运行结果

#include<stdio.h>
int main(){
	char* c[4] = { "hello", "world", "bite", "pengge" };
	//一级字符指针数组c:
	//       数组元素值:| &'h'| &'w'| &'b'| &'p'|
	//         元素类型:|char*|char*|char*|char*|
	//         元素意义:|指向"hello"的指针(一开始指向'h')|指向"world"的指针(一开始指向'w')|指向"bite"的指针(一开始指向'b')|指向"pengge"的指针(一开始指向'p')
	//           元素名:| c[0]| c[1]| c[2]| c[3]|
	//         地址设为:   0      1    2     3
	char** cp[4] = {c + 3, c + 2, c + 1, c};
	//二级字符指针数组cp:
	//         数组元素:|  3  |  2  |  1  |  0  |
	//           元素名:|cp[0]|cp[1]|cp[2]|cp[3]|
	//         地址设为:   a     b    c     d
	char*** cpp = cp;
	//    *cpp == &cp[0] == a
	printf("%s\n", **++cpp);
	//都只能先从cpp开始分析:
	//1.++cpp:cpp先自增1——*cpp = a + 1 == b;
	//2.**(++cpp):两次解引用——(1)*(++cpp) == b --> (2)**(++cpp) == 2;
	//3.%s的作用:从所给地址开始,一直打印字符至'\0'处,所以打印的结果为:bite
	printf("%s\n", *-- *++cpp + 3);
	//1.++cpp:cpp再自增1——*cpp = b + 1 == c;
	//2.*++cpp:解引用——*cpp == c;
	//3.-- *++cpp:c自减1——*c = 1 - 1 ==0;
	//4.*-- *++cpp:解引用——*c == 0;
	//5.*-- *++cpp + 3:一级字符指针 + 3——*0 = 'h' + 3 == 'l'(临时,不是真的加);
	//6.打印结果为:lo
	printf("%s\n", *cpp[-2] + 3);
	//1.cpp[-2]:cpp指向的值先减2再解引用——(1)cpp - 2——*cpp = c - 2 == a(临时,不是真的加);(2)*(cpp - 2)——*(cpp - 2) = a;
	//2.*cpp[-2]:再解引用——**(cpp - 2) == *a == 3;
	//3.*cpp[-2] + 3:一级字符指针 + 3——*3 = 'p' + 3 =='g'(临时,不是真的加);
	//4.打印结果为:gge
	printf("%s\n", cpp[-1][-1] + 1);
	//1.cpp[-1]:cpp指向的值先减1再解引用——(1)cpp - 1——*cpp = c - 1 == b(临时,不是真的加);(2)*(cpp - 2)——*(cpp - 2) = b;
	//2.cpp[-1][-1]:b指向的值先减1再解引用——(1)b - 1——*b = 2 - 1 == 1(临时,不是真的加);(2)*1——*1 = 'w';
	//3.cpp[-1][-1] + 1:一级字符指针 + 1——*1 = 'w' + 1 == 'o'(临时,不是真的加);
	//4.打印为:orld
	return 0;
}

考察:操作符的优先级只在操作数的相邻位置才考虑;++ 和-- 会真实改变变量存的数据
涉及:二级指针,操作符++和–,解引用。


快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞和收藏支持一下,在此非常感谢!!!


网站公告

今日签到

点亮在社区的每一天
去签到