8.指针笔试题-进阶

1.一维数组

int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(*&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]));
printf("%d\n",sizeof(&a[0]+1));
  • 分析上述代码的打印结果

image-20240327135431438

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

    // sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小,单位是字节
	printf("%d\n", sizeof(a));//16
    
	printf("%d\n", sizeof(a + 0));//4
	// a不是单独放在sizeof内部,也没有取地址,所以a就是首元素的地址,a+0还是首元素的地址
	// 只要a表示的是地址,大小就是4/8个字节(32位就是4个字节,64位就是8个字节)
    
	printf("%d\n", sizeof(*a));//4
    // a是数组首元素的地址,即&a[0];而 *a <==> *(a+0) <==> *(&a[0]) <==> a[0]
   	// *a中的a是数组首元素的地址,*a就是对首元素的地址解引用,找到的就是首元素
	// 首元素的大小就是4个字节(存储的元素是int类型,int是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
    // sizeof(*&a) 表示的是整个数组的大小
	// &a----> int(*)[4]
	// &a拿到的是数组名的地址,类型是 int(*)[4],是一种数组指针
	// 数组指针解引用找到的是数组
	// *&a ---> a
	//
	// 2.
	// &和*抵消了
	// *&a ---> a
	
    
    
    printf("%d\n", sizeof(&a + 1));//4/8
	// &a取出的是数组的地址
	// &a-->  int(*)[4]
	// &a+1 是从数组a的地址向后跳过了一个数组大小的字节(整个数组)
	// &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]
    
	return 0;
}

2.字符数组

  • 在做题之前,先给大家一些小tips
  • 首先了解strlen()函数

strlen是求字符串长度的,关注的是字符串中的\0,计算的是\0之前出现的字符的个数
strlen是库函数,只针对字符串
sizeof只关注占用内存空间的大小,不在乎内存中放的是什么
sizeof是操作符
32位机器,指针变量是4byte; 64位机器,指针变量是8byte

2.1 char arr[] = {‘a’,‘b’,‘c’,‘d’,‘e’,‘f’};

char arr[] = {'a','b','c','d','e','f'};
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\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

image-20240327140407795

  • 解析如下
char arr[] = {'a','b','c','d','e','f'};

printf("%d\n", sizeof(arr)); // 6
// sizeof(数组名), 计算的是整个数组的大小

printf("%d\n", sizeof(arr+0)); // 4/8; 
// arr为首元素的地址,arr+0也是首元素的地址

printf("%d\n", sizeof(*arr));// 1
// arr <==> &a[0]
// *arr <==> *(&a[0]) <==> a[0] ==> 'a'

printf("%d\n", sizeof(arr[1]));// 1
// arr[1] <==> 'b'

printf("%d\n", sizeof(&arr));// 4/8
// &arr就是整个数组的地址,也就是一个地址

printf("%d\n", sizeof(&arr+1));// 4/8
// &arr+1 就是跳过整个数组之后的地址

printf("%d\n", sizeof(&arr[0]+1));// 4/8
// &arr[0]是首元素的地址,即a的地址; &arr[0]+1 是首元素地址跳过一个元素之后的地址,即b的地址


========================================================================================// strlen

printf("%d\n", strlen(arr));// 随机值
// arr即首元素的地址;
// strlen(arr) 是求首元素地址之后字符串的长度,直到'\0'中止
// (a,b,c,d,e,f  还有其他的字符等,直到遇到'\0'中止)

printf("%d\n", strlen(arr+0));// 随机值
// arr+0也是首元素的地址

printf("%d\n", strlen(*arr));
// *arr 即'a';
// strlen(*arr) ==> strlen('a') ==> strlen(97) ;所以为野指针,是错误的

printf("%d\n", strlen(arr[1]));
// arr[1] 即'b'; 同理也为野指针

printf("%d\n", strlen(&arr));/ / 随机值
// &arr 是数组的地址,数组的地址也在首元素的地址处

printf("%d\n", strlen(&arr+1)); // 随机值 
// &arr+1 跳过整个数组之后的地址

printf("%d\n", strlen(&arr[0]+1));// 随机值 
// &arr[0]+1 第一个元素的地址跳过一个元素之后的地址

2.2 char arr[] = " abcdef ";

char arr[] = "abcdef";
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\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));
  • 以上代码的解析如下
char arr[] = "abcdef";[a b c d e f \0]

printf("%d\n", sizeof(arr)); // 7
// sizeof(arr) 计算的是整个数组的大小; 字符串"abcdef",其实是7个字符,还有一个隐藏的\0

printf("%d\n", sizeof(arr+0));// 4/8
// arr+0 为首元素的地址

printf("%d\n", sizeof(*arr));// 1
// *arr <==> 'a'

printf("%d\n", sizeof(arr[1]));// 1
// arr[1] <==> 'b'

printf("%d\n", sizeof(&arr));// 4/8
// &arr 数组的地址

printf("%d\n", sizeof(&arr+1));// 4/8
// &arr+1 跳过整个数组之后的地址

printf("%d\n", sizeof(&arr[0]+1));// 4/8
// &arr[0]+1 首元素地址跳过一个元素之后的地址


========================================================================================// strlen
    
    
printf("%d\n", strlen(arr));// 6
// 首元素地址之后的字符串个数,以'\0'结束

printf("%d\n", strlen(arr+0));// 6
// 首元素地址之后的字符串个数,以'\0'结束

printf("%d\n", strlen(*arr));
// *arr <==> 'a'; 所以为野指针
printf("%d\n", strlen(arr[1]));
// arr[1] <==> 'b'; 野指针

printf("%d\n", strlen(&arr));// 6
// &arr 数组的地址,数组的地址也是从首元素开始

printf("%d\n", strlen(&arr+1));// 随机值
// &arr+1 跳过整个数组的地址,求这个地址之后字符的个数,遇到'\0'结束

printf("%d\n", strlen(&arr[0]+1));// 5
// &arr[0]+1 即'b'的地址;

2.3 char *p = “abcdef”;("abcdef"储存在常量区)

char *p = "abcdef";
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(p+1));
printf("%d\n", sizeof(*p));
printf("%d\n", sizeof(p[0]));
printf("%d\n", sizeof(&p));
printf("%d\n", sizeof(&p+1));
printf("%d\n", sizeof(&p[0]+1));


printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
printf("%d\n", strlen(*p));
printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));
  • 以上代码的分析如下
char *p = "abcdef";
// p里面存放的是首元素a的地址

printf("%d\n", sizeof(p));// 4/8
// p也就是 char*类型指针变量的大小

printf("%d\n", sizeof(p+1));// 4/8
// p是首元素a的地址;  p+1为跳过一个元素之后的地址;  *(p+1) <==> 'b'

printf("%d\n", sizeof(*p));// 1
// *p <==> *(p+0) <==> 'a'

printf("%d\n", sizeof(p[0]));// 1
//p[0] <==> *(p+0) <==> 'a'

printf("%d\n", sizeof(&p));// 4/8
// &p是指针变量的地址

printf("%d\n", sizeof(&p+1));// 4/8
// &p+1 即:跳过4字节之后的地址,即跳过元素p之后的地址; 

printf("%d\n", sizeof(&p[0]+1));// 4/8
// &p[0]+1 即首元素的地址跳过一个元素之后的地址


========================================================================================// strlen
    
    
printf("%d\n", strlen(p));// 6
// p也就是首元素的地址(计算的是p指向的字符串中的字符个数,到\0终止)

printf("%d\n", strlen(p+1));// 5
// p+1 首元素地址跳过一个元素之后的地址,即'b'的地址
// 因为p是char*类型,所以 p+1 就是跳过一个字节

printf("%d\n", strlen(*p));err(野指针)
// *p <==> 'a'; 
printf("%d\n", strlen(p[0]));err(野指针)
// p[0] <==> 'a'; 
    
printf("%d\n", strlen(&p));// 随机值
// &p 指的是指针变量p的地址,而不是字符串首元素的地址p
// &p 即p的地址之后的字符长度

printf("%d\n", strlen(&p+1));// 随机值
// &p+1 即:跳过4字节之后的地址,即跳过元素p之后的地址;

printf("%d\n", strlen(&p[0]+1));// 5
// &p[0]+1 即'b'的地址;

3.二维数组

int a[3][4] = {0};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a[0][0]));
printf("%d\n",sizeof(a[0]));
printf("%d\n",sizeof(a[0]+1));
printf("%d\n",sizeof(*(a[0]+1)));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(*(a+1)));
printf("%d\n",sizeof(&a[0]+1));
printf("%d\n",sizeof(*(&a[0]+1)));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a[3]));

image-20240327142909941

  • 以上代码的分析如下
int main()
{
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a)); // 48
   // sizeof(数组名), 表示的是整个数组的大小(整个数组一共有12个元素,每个元素为4个字节,因此共48字节)
    
	printf("%d\n", sizeof(a[0][0])); // 4
    // a[0][0] 表示的是第一行第一个元素
    
	printf("%d\n", sizeof(a[0]));// 16
    // a[0] ==> *(a+0);    
    // a是首行元素的地址(二维数组的数组名就是首行的地址),
    // *(a+0)也是首行的地址,也是首行首元素的地址,两个地址是相等的
	// a[0]如果单独使用,那么是整个第一行的大小,如sizeof(a[0])
    // 不单独使用就是 计算的就是首行首元素的地址的大小
   
    // &a[0],也就是第一行数组地址(数组的地址的大小就是4字节)
    // 通过如下代码进行验证
	// printf("%d\n", sizeof(&a[0]));   // 没有单独使用a[0],打印结果为4
    // printf("%d\n", sizeof(a[0]));    // 打印结果为16
    // printf("%d\n", sizeof(*(a+0)));  // 打印结果为16
    // printf("%d\n", sizeof(a[0]+0));  // 没有单独使用a[0],打印结果为4
    
    
	printf("%d\n", sizeof(a[0] + 1)); // 4/8
    // a[0] + 1 ==> *(a+0) + 1
    // 当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单独放在sizeof中,或者是&a放在sizeof中,都是整个数组的大小
	// 一维数组a表示首元素的地址,二维数组的首元素是它的第一行,a就是第一行的地址
	// a+1就是跳过第一行,表示第二行的地址(此时a没有单独使用就是首行的地址)
    
	printf("%d\n", sizeof(*(a + 1))); // 16
    // a二维数组的数组名,也就是首行元素的地址
	// *(a + 1)是对第二行数组地址的解引用,拿到的是第二行(a + 1 指向的是整个数组,解引用后,拿到的是整个数组的数据)
    // a+1的类型是 int (*)[4],解引用后是数组 int [4]
	// *(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] ,单独使用就是首行的地址,不单独使用就是首行首元素的地址
	// &a[0] 对第一行的数组名取地址,拿出的是第一行的地址
	// &a[0]+1  得到的是第二行的地址
	
    
	printf("%d\n", sizeof(*(&a[0] + 1))); //16
    // &a[0] 是第一行数组的地址, &a[0]+1第二行数组的地址,类型是 int (*)[4]
    // *(&a[0] + 1)就是对第二行地址的解引用,拿到的就是整个第二行的整个数组,类型为 int [4]
    
	printf("%d\n", sizeof(*a));//16
	// a表示首元素的地址,就是第一行的地址, 类型是 int (*)[4]
	// *a就是对第一行地址的解引用,拿到的就是第一行,类型为 int [4]
	
    
	printf("%d\n", sizeof(a[3]));//16
	// 并没有访问a[3],所以没有越界
    // sizeof(a[3]); 只是分析a[3]类型的大小 
    // 所以 sizeof(a[3]) 的大小相当于 sizeof(a[0])
	
    
    //int a = 10;
	//sizeof(int);
	//sizeof(a);

	
	return 0;
}

总结:

数组名的意义:

1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表示首元素的地址。

4.指针笔试题

笔试题1:

int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int *ptr = (int *)(&a + 1);
    printf( "%d,%d", *(a + 1), *(ptr - 1));
    // 注:ptr - 1,是跳过4个字节,因为ptr的类型是int*
    // &a + 1是跳过20个字节,因为&a的类型是int(*)[5]
    return 0; 
}
//程序的结果是什么?
  • 解析如下:
&a 是数组的地址,类型为 int(*)[5];
&a+1 是数组的地址跳过整个数组的地址,类型仍为int(*)[5]
所以*(ptr - 1) 打印的整型是5*(a + 1) 打印的整型是2

image-20240327161355792

笔试题2:

//由于还没学习结构体,这里告知结构体的大小是20个字节
struct Test
{
	int Num;
	char *pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;                        // p为结构体的指针变量 即 struct test *p

//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0; 
}
  • 解析如下:
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}* p = (struct Test*)0x100000;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节(在x86环境下)

int main()
{
	printf("%p\n", p + 0x1);// 指针变量+1,跳过test类型变量大小的字节
	//0x100000+20-->0x100014
	printf("%p\n", (unsigned long)p + 0x1);// 整型+1, 将地址转化为整型+1;
    // 0x100000变为10进制数 --> 1,048,576
	// 1,048,576+1 --> 1,048,577
	// 0x100001
	printf("%p\n", (unsigned int*)p + 0x1);
    // 强转为无符号整型的指针变量,所以指针变量+1,跳过4byte
	//0x100000+4-->0x100004
	return 0;
}

笔试题3

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; 
}
  • 解析如下:
int main()
{
    int a[4] = { 1, 2, 3, 4 };
    int *ptr1 = (int *)(&a + 1);
    // &a是数组的地址,&a+1 的类型是int(*)[4], &a+1 是数组的地址跳过整个数组之后的地址,
    // ptr1[-1] <==> *(ptr - 1); 所以 ptr1[-1]打印的结果是数组元素4
    
    int *ptr2 = (int *)((int)a + 1);
    // a为首元素的地址,类型是 int*,将a强转为int类型
    // 此时(int)a + 1 就是向高地址前进了一个字节
    // *ptr2 打印的结果为 2000000
    
    printf( "%x,%x", ptr1[-1], *ptr2);
    // %x 表示用16进制打印输出
    return 0; }

image-20220818132311786

笔试题4:

#include <stdio.h>

int main()
{
    int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    // 注:二维数组的初始化,只有用大括号括起来的才是对每一行的初始化
    // 这里用的是小括号,其实是逗号表达式
    // 逗号表达式,从左向右依次执行,最后一个表达式的结果是整个表达式的结果
    // 如:(0, 1),第一个表达式的结果为0,第二个表达式的结果为1,所以这个逗号表达式的结果为1
    // 即int a[3][2] ={1,3,5};
    int *p;
    p = a[0];
    printf( "%d", p[0]);
 	return 0; 
}
  • 解析如下:

image-20240327162939571

笔试题5

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]);
    // 对于&p[4][2],因为指针p的类型是int(*p)[4],所以p+1每次是跳过4个int
    // 而对于&a[4][2],类型是int(*)[5],每次是跳过5个int
    return 0;
}

// 打印结果是:FFFFFFFC,   -4
  • 解析如下:

image-20220818141522538

笔试题6

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; 
}
  • 解析如下:
int main()
{
    int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int *ptr1 = (int *)(&aa + 1);
    // &aa 是整个二维数组的地址,&aa+1 是跳过整个二维数组之后的地址
    
    int *ptr2 = (int *)(*(aa + 1));
    // aa 是二维数组的数组名,就是首元素的地址,也就是二维数组第一行的地址
    // aa+1 是二维数组第二行的地址
    // *(aa + 1) <==> aa[1] 也就是第二行首元素的地址
    
    printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
    // 因为ptr1 , ptr2 的类型为int* 所以解引用访问4byte
    // 所以*(ptr1 - 1) 打印的结果是10
    // *(ptr2 - 1) 打印的结果是5
    
    return 0; 
}

第一行: 1 2 3 4 5

第二行: 6 7 8 9 10

笔试题7

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

image-20220818144212428

因此最终打印的值为 at

笔试题8

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;
}
// 打印结果:
POINT
ER
ST
EW
  • 解析如下

image-20220818151627673

image-20220818160945254

image-20220818160915308

image-20220818161454150

image-20220818162354037

// 注:操作数两侧的操作符进行优先级的比较
// 前置++ 的优先级高于 +;
  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值