关于数组和指针的笔试题解析(详解)

在这里插入图片描述

文章目录

  • 说明
  • 🚩数组笔试题
    • 💻一维数组
      • 📄练习:
      • 💡解析
    • 💻字符数组
      • 📄练习1:
      • 💡解析
      • 📄练习2:
      • 💡解析
      • 📄练习3:
      • 💡解析
      • 📄练习4:
      • 💡解析
      • 📄练习5:
      • 💡解析
      • 📄练习6:
      • 💡解析
    • 💻二维数组
      • 📄练习:
      • 💡解析
    • 🗞️小结
  • 🚩指针运算笔试题
      • 📄练习1:
      • 💡解析
      • 📄练习2:
      • 💡解析
      • 📄练习3:
      • 💡解析
      • 📄练习4:
      • 💡解析
      • 📄练习5:
      • 💡解析
      • 📄练习6:
      • 💡解析
      • 📄练习7:
      • 💡解析
      • 📄练习8:
      • 💡解析
  • 🚩总结

说明

X64环境下,8个字节
X86环境下,4个字节

小编在运行代码,数据是在VS 2019 X86环境下打印的。

🚩数组笔试题

💻一维数组

📄练习:

code:

#include<stdio.h>int main()
{int a[] = { 1,2,3,4 };printf("%zd\n", sizeof(a));printf("%zd\n", sizeof(a + 0));printf("%zd\n", sizeof(*a));printf("%zd\n", sizeof(a + 1));printf("%zd\n", sizeof(a[1]));printf("%zd\n", sizeof(&a));printf("%zd\n", sizeof(*&a));printf("%zd\n", sizeof(&a + 1));printf("%zd\n", sizeof(&a[0]));printf("%zd\n", sizeof(&a[0] + 1));return  0;
}

运行结果:

16
4
4
4
4
4
16
4
4
4

在这里插入图片描述

💡解析

printf("%zd\n", sizeof(a));  //16

➿➿数组名的理解:

数组名是数组首元素的地址

但是有2个例外:

  1. sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小,单位是字节
  2. &数组名,数组名表示整个数组,取出的是数组的地址

⭕所以在此代码中,有四个整型,一个整型占4个字节,故总共占16个字节。


printf("%zd\n", sizeof(a + 0)); //4

数组名a没有单独放在sizeof()中,也没有进行单独取地址&,因此,(a+0)不是数组名,这里的a是数组首元素地址,加上0,相当于没有加
a+0<======>&a[ 0 ]

⭕故,是地址大小,4或者8个字节(X64环境下和X86环境下不一样)


printf("%zd\n", sizeof(*a));   //4

这里没有将单独的一个a放进sizeof()中,也没有取地址&,那么a就是除那两种情况之外,即a就是数组首元素地址,a==>&a[ 0 ]

⭕故,*a 其实就是第一个元素,也就是a[ 0 ] 的大小:4个字节


printf("%zd\n", sizeof(a + 1));  //4

和上面(a+0)一样,a是首元素地址

(a+0)–>&a[ 0 ]
(a+1)–>&a[ 1 ]
⭕故,(a+1)就是第2个元素的地址,大小是4或者8个字节


printf("%zd\n", sizeof(a[1]));    //4

a[1]就是这个数组的第二个元素

⭕故,大小是4个字节


printf("%zd\n", sizeof(&a));

&a➡️取出的是数组的地址,但是数组的地址也是一个地址呀😏,数组的地址可没有高人一等。是地址大小就是4或者8个字节

⭕故,大小是4或者8个字节


printf("%zd\n", sizeof(*&a));     //16

两种解读方式:
1️⃣抵消:
·这里取地址,然后再解引用,抵消掉了,相当于就是a

2️⃣数组指针类型:
· &a类型是一个数组指针,&a<==>int(*p)[ 4 ]

· 我们知道,指针在进行加一或者解引用的时候,跳过多少个字节是取决于指针类型:
*p访问一个数组的大小
p+1是跳过一个数组的大小
·
那么现在p指向一个大小为4,类型为整型的一个数组

取出整个数组的地址,再进行解引用,访问的就是整个数组

因此:

printf("%zd\n", sizeof(*&a));
<==>
printf("%zd\n", sizeof(a));

⭕故,大小是16个字节


printf("%zd\n", sizeof(&a + 1)); //4

在这里插入图片描述

&a+1是跳过整个数组后的地址,是地址大小就是4或者8个字节。

⭕故,大小是4或者8个字节


printf("%zd\n", sizeof(&a[0]));    //4

没什么好说的,就是首元素地址🤨,是地址大小就是4或者8个字节。

⭕故,大小是4或者8个字节


printf("%zd\n", sizeof(&a[0] + 1)); //4

&a[0] + 1表示第二个元素的地址(&a[ 1 ])

⭕故,大小是4或者8个字节


💻字符数组

📄练习1:

code:

#include<stdio.h>
int main()
{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));return 0;
}

运行结果:

6
4
1
1
4
4
4

在这里插入图片描述

💡解析

printf("%d\n", sizeof(arr));  //6

arr表示整个数组,计算的是整个数组的大小

此数组有6个字符,一个字符1个字节

⭕故,一共有6个字节


printf("%d\n", sizeof(arr + 0));   //4

arr是数组首元素的地址,arr+0还是首元素的地址

⭕故,是地址,大小就是4或者8个字节


printf("%d\n", sizeof(*arr)); //1

arr就是首元素地址,*arr解引用,就是首元素,就站一个字符,即1个字节

⭕故,大小就是1个字节


printf("%d\n", sizeof(arr[1]));  //1

arr[ 1 ]表示数组第2个元素,即占一个字节

⭕故,大小就是1个字节


printf("%d\n", sizeof(&arr)); //4

&arr是数组的地址,数组的地址也是地址

⭕故,是地址,大小就是4或者8个字节


printf("%d\n", sizeof(&arr + 1));

&arr+1跳过整个数组,指向的是f后面,

在这里插入图片描述

⭕故,是地址,大小就是4或者8个字节


printf("%d\n", sizeof(&arr[0] + 1)); //4

&arr[0]是首元素地址,+1后变成第二个元素地址

⭕故,是地址,大小就是4或者8个字节


📄练习2:

code:

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

💡解析

printf("%zd\n", strlen(arr));  //随机值

数组中没有明确给出\0

⭕故,计算出的结果是随机值


printf("%zd\n", strlen(arr + 0)); //随机值

arr+0:首元素地址+0,和没加一样,依然表示arr,数组中也是没有明确给出\0

⭕故,计算出的结果是随机值


printf("%zd\n", strlen(*arr)); //非法访问-err

strlen()函数参数是指针类型

size_t strlen ( const char * str );

而*arr得到是首元素‘a’

这样就意味着将’a’(97)传递给strlen,将97当作地址传递给strlen

⭕故,形成非法访问


printf("%zd\n", strlen(arr[1])); //非法访问-err

arr[1]表示数组第二个元素,‘b’(98)

将98当作地址传递给strlen,依然是非法访问,和上面一样

⭕故,形成非法访问


printf("%zd\n", strlen(&arr)); //随机值

&arr是一个字符数组指针类型–>char (*p)[ 6 ]

对于strlen依然是找到首元素地址,往后读取,但是没有\0

⭕故,计算出的结果是随机值


printf("%zd\n", strlen(&arr + 1)); //随机值

加一后,跳过整个数组

在这里插入图片描述

跳过一个数组后再去往后找,不知道找什么

和上面的随机值是不一样的

差6个字节

⭕故,计算出的结果是随机值


printf("%zd\n", strlen(&arr[0] + 1)); //随机值

&arr[0] + 1表示数组第二个元素,即’b’,从‘b’开始往后数,没有明确给出\0

⭕故,计算出的结果是随机值


📄练习3:

code:

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

运行结果:

7
4
1
1
4
4
4

在这里插入图片描述

💡解析

printf("%zd\n", sizeof(arr)); //7

不管有没有\0,只管占了多少字节

有7个元素

⭕故,占7个字节


printf("%zd\n", sizeof(arr + 0)); //4

arr表示首元素地址,即arr+0也表示数组首元素地址,是地址,大小就是4或者8个字节

⭕故,占4或者8个字节


printf("%zd\n", sizeof(*arr)); //1

arr表示首元素地址,*arr就是首元素,大小就是1个字节

⭕故,占1个字节


printf("%zd\n", sizeof(arr[1])); //1

arr[1]表示数组第二个元素,大小1个字节

⭕故,占1个字节


printf("%zd\n", sizeof(&arr)); //4

&arr是数组的地址,是地址,大小就是4或者8个字节

⭕故,占4或者8个字节


printf("%zd\n", sizeof(&arr + 1)); 

&arr是数组地址

&arr+1是跳过整个数组的那个地址

是地址,大小就是4或者8个字节

在这里插入图片描述

⭕故,占4或者8个字节


printf("%zd\n", sizeof(&arr[0] + 1)); //4

&arr[0] + 1表示第二个元素地址

是地址,大小就是4或者8个字节

⭕故,占4或者8个字节


📄练习4:

code:

#include<stdio.h>
#include <string.h>int main()
{char arr[] = "abcdef";printf("%zd\n", strlen(arr));printf("%zd\n", strlen(arr + 0));//printf("%lld\n", strlen(*arr)); //printf("%lld\n", strlen(arr[1])); printf("%zd\n", strlen(&arr)); printf("%zd\n", strlen(&arr + 1));printf("%zd\n", strlen(&arr[0] + 1));return 0;
}

运行结果:

6
6
6
12
5

在这里插入图片描述

💡解析

printf("%zd\n", strlen(arr)); //6

arr是数组首元素地址,顺着往后读取,读到\0结束

strlen统计的是\0之前的字符串个数

⭕故,计算结果是6


printf("%zd\n", strlen(arr + 0)); //6

arr是数组名,首元素地址

arr+0 还是首元素地址,顺着往后读取,读到\0结束

⭕故,计算结果是6


//printf("%lld\n", strlen(*arr));   //err - 非法访问

*arr表示’a’(97)

将97当作地址传递给strlen

非法访问

⭕故,非法访问


//printf("%lld\n", strlen(arr[1])); //err - 非法访问

arr[1]表示数组第二个元素,即‘b’(98)

将987当作地址传递给strlen

非法访问

⭕故,非法访问


printf("%zd\n", strlen(&arr)); //6

&arr取的是整个数组的地址

但这个地址依然指的是数组的起始位置

传递给strlen后,依然从起始位置往后读取,直到\0停止

⭕故,计算结果是6


printf("%zd\n", strlen(&arr + 1)); //随机值

&arr取的是整个数组的地址

&arr+1跳过整个数组后的地址

从跳过后的地址开始找\0,就是随机值

⭕故,随机值


printf("%zd\n", strlen(&arr[0] + 1)); //5

&arr[0] 表示第一个元素地址

&arr[0] + 1第二个元素地址,即b的地址

从b的地址开始找\0,找到\0后停止

⭕故,计算结果是5


📄练习5:

code:

#include<stdio.h>
int main()
{char* p = "abcdef";printf("%zd\n", sizeof(p));printf("%zd\n", sizeof(p + 1));printf("%zd\n", sizeof(*p));printf("%zd\n", sizeof(p[0]));printf("%zd\n", sizeof(&p));printf("%zd\n", sizeof(&p + 1));printf("%zd\n", sizeof(&p[0] + 1));return 0;
}

运行结果:

4
4
1
1
4
4
4

在这里插入图片描述

💡解析

思路:这里是常量字符串,将常量字符串首字符放在p中。

图示:

在这里插入图片描述


printf("%zd\n", sizeof(p));

p是一个指针变量

sizeof(p)求的就是指针变量的大小

⭕故,大小是4或者8个字节


printf("%zd\n", sizeof(p + 1)); //4

p放的是‘a’的地址

p+1则放的是’b’的地址

依然是地址

⭕故,大小是4或者8个字节


printf("%zd\n", sizeof(*p)); //1

p指向’a’

*p解引用,是首字符

⭕故,大小是1个字节


printf("%zd\n", sizeof(p[0])); //1

两种解读方式:

1️⃣类似数组:
这里,可以把字符串“abcdef”也当作有个数组
p是数组名
那么,p[0]访问的就是‘a’

2️⃣计算:
p[0]===>*(p+0)

⭕故,大小是1个字节


printf("%zd\n", sizeof(&p));  //4

&p是取出p所占的地址

也是个地址

是地址,大小就是4或者8个字节

⭕故,大小是4或者8个字节


 printf("%zd\n", sizeof(&p + 1));

&p + 1也是地址

跳过p变量的地址,指向的是p后面的地址

在这里插入图片描述

是地址,大小就是4或者8个字节

⭕故,大小是4或者8个字节


 printf("%zd\n", sizeof(&p[0] + 1));

&p[0]是’a’的地址

&p[0] + 1是“b’的地址

是地址,大小就是4或者8个字节

⭕故,大小是4或者8个字节


📄练习6:

code:

#include<stdio.h>
#include<string.h>int main()
{char* p = "abcdef";printf("%zd\n", strlen(p));printf("%zd\n", strlen(p + 1));//printf("%zd\n", strlen(*p));//printf("%zd\n", strlen(p[0]));printf("%zd\n", strlen(&p));printf("%zd\n", strlen(&p + 1));printf("%zd\n", strlen(&p[0] + 1));return 0;
}

运行结果:

6
5
3
11
5

在这里插入图片描述

💡解析

printf("%zdd\n", strlen(p)); //6

字符串中有\0

p中存放的是a的地址

从a的地址开始向后访问

⭕故,长度是6


printf("%zd\n", strlen(p + 1)); //5

p指向a

p+1指向b

从b开始往后数,直到遇到\0为止

⭕故,长度是5


//printf("%zd\n", strlen(*p));//err

p指向a

*p就是a

将p的值传递给strlen

⭕故,非法访问


//printf("%zd\n", strlen(p[0]));//err

p[0]=>*(p+0)=>*p

⭕故,非法访问


printf("%zd\n", strlen(&p)); //随机值

&p是p的地址

从p所占空间的起始位置开始查找的

完全不可知的

⭕故,随机值


printf("%zd\n", strlen(&p + 1));//随机值

&p+1指向的是p的地址后面

从&p后面的地址开始读取

也是不可知的

⭕故,随机值


printf("%zd\n", strlen(&p[0] + 1)); //5

&p[0] + 1是‘b’的地址

从’b’后面开始数

⭕故,长度是5


💻二维数组

📄练习:

code:

#include<stdio.h>int main()
{int a[3][4] = { 0 };printf("%zd\n", sizeof(a));printf("%zd\n", sizeof(a[0][0]));printf("%zd\n", sizeof(a[0]));printf("%zd\n", sizeof(a[0] + 1));printf("%zd\n", sizeof(*(a[0] + 1)));printf("%zd\n", sizeof(a + 1));printf("%zd\n", sizeof(*(a + 1)));printf("%zd\n", sizeof(&a[0] + 1));printf("%zd\n", sizeof(*(&a[0] + 1)));printf("%zd\n", sizeof(*a));printf("%zd\n", sizeof(a[3]));return 0;
}

运行结果:

48
4
16
4
4
4
16
4
16
16
16

在这里插入图片描述

💡解析

printf("%zd\n", sizeof(a));  //48

计算的是整个二维数组的大小,单位是字节

数组里共有3*4=12个元素

每个元素都是int类型

则344

⭕故,大小是48个字节


printf("%zd\n", sizeof(a[0][0]));  //4

a[0][0]表示第一行第一个元素

⭕故,大小是4个字节


printf("%zd\n", sizeof(a[0]));  //16

a[0]表示第一行数组名

在这里插入图片描述

将第一行数组名单独放在sizeof内部

计算的是第一行大小

⭕故,大小是16个字节


printf("%zd\n", sizeof(a[0] + 1));  //4

a[0]是第一行数组名,但是没有把a[0]单独放在sizeof中

这里的a[0]表示第一行第一个元素的地址

a[0] + 1表示第一行第一个元素地址+1,即第一行第二个元素地址==>a[0][1]的地址

是地址,大小是4或者8个字节

在这里插入图片描述
⭕故,大小是4或者8个字节


printf("%zd\n", sizeof(*(a[0] + 1))); //4

a[0] + 1是第一行第二个元素的地址

*(a[0] + 1)解引用,访问的就是第一行第二个元素

⭕故,大小是4个字节


printf("%zd\n", sizeof(a + 1)); //4

这里的a是二维数组的数组名

没有将a单独放在sizeof中

那么,a就是数组首元素地址,也就是第一行的地址😏

a+1跳过一行的地址,就是第二行地址

既然是地址,那么…

⭕故,大小是4或者8个字节


printf("%zd\n", sizeof(*(a + 1))); //16

a+1是第二行地址

*(a + 1)解引用,表示第二行

*(a + 1)==>a[1]

printf("%zd\n", sizeof(*(a + 1)));
<==>
printf("%zd\n", sizeof(a[1]);

两种表示方法,你get到了嘛??😏

⭕故,大小是16个字节


printf("%zd\n", sizeof(&a[0] + 1)); //4

a[0]是第一行数组名

&a[0]取出第一行的地址

&a[0] + 1第一行的地址+1,表示第二行地址

是地址…

⭕故,大小是4或者8个字节


printf("%zd\n", sizeof(*(&a[0] + 1))); //16

&a[0] + 1是第二行的地址

*(&a[0] + 1))对第二行地址解引用,访问第二行数组

⭕故,大小是16个字节


printf("%zd\n", sizeof(*a)); //16

a表示首元素地址,也就是第一行地址

*a对一行地址解引用,就是第一行

⭕故,大小是16个字节


printf("%zd\n", sizeof(a[3])); //16

a[3]是第四行,但是刚开始我们开的数组没有第四行

我们知道,sizeof()是根据类型计算的

实际上不会访问a[3]

a[3]和前面a[0]类型是一样的

⭕故,大小是16个字节


🗞️小结

  1. sizeof(数组名),这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩。
  2. &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址。
  3. 除此之外所有的数组名都表⽰⾸元素的地址。

关于sizeof和strlen的介绍,可以看小编的文章《sizeof和strlen的对比》,里面有详细解释!!!

🚩指针运算笔试题

📄练习1:

code:

#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;
}

运行结果:

2,5

💡解析

1️⃣对*(a + 1)的分析:

  • a表示数组首元素地址

  • a+1首元素地址+1,表示第二个元素地址

  • *(a + 1)解引用,表示第二个元素,即2

2️⃣对*(ptr - 1)的分析:

  • 首先分析int* ptr = (int*)(&a + 1);

  • &a表示整个数组地址

  • &a+1表示跳过整个数组地址

  • &a的类型是int(*)[5] ,+1后的类型还是这个类型

  • 现在要将(&a + 1)赋值给ptr

  • ptr是int* 类型,所以需要强制类型转换

在这里插入图片描述

  • 分析*(ptr - 1)

  • ptr-1表示前移动,即5的地址:

在这里插入图片描述

  • *(ptr-1)解引用,表示5

📄练习2:

在X86环境下

假设结构体的⼤⼩是20个字节

code:

#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;
}

运行结果:

00100014
00100001
00100004

💡解析

printf("%p\n", p + 0x1);  //00100014

0x1表示1(在16进制中)

p+0x1即p+1

那么,指针+1到底加几呢??😥

这个取决于指针类型,现在是一个结构体指针

那么加1就是跳过一个结构体大小

结构体大小是20个字节,加的是0x100014

⭕所以,大小变成00100014(16进制中)


printf("%p\n", (unsigned long)p + 0x1); //00100001

p的类型被强制转换成unsigned long,无符号整型

此时就不是指针类型啦

那么就是:整型+1

不再是整型指针+1,小小的细节,要清楚

0x100000+1=0x100001

⭕所以,输出结果:00100001


printf("%p\n", (unsigned int*)p + 0x1);  //00100004

p被强制转换成整型指针(unsigned int)

整型指针+1就是+4,即0x100004

⭕所以,输出结果:00100004


📄练习3:

cod:

#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;
}

运行结果:

4,2000000

💡解析

  • &a:取数组 a 的地址

  • &a + 1:将指向数组的指针做加法运算,相当于移动了一个整个数组的大小。由于 a 是一个包含4个元素的 int 数组,所以加上1后指向数组之外的内存。

  • (int*)(&a + 1):将指向数组之外的内存地址转换为 int 指针。
    ptr1[-1]:通过指针 ptr1 往前访问前一个位置的值,即指向数组的最后一个元素。

  • (int)a:将数组 a 转换为 int 类型的值,实际上是取数组首元素的地址。

  • (int*)((int)a + 1):将数组首元素地址加上1后再转换为 int 指针。

  • *ptr2:通过指针 ptr2 访问指向的值。

  • 根据上述分析,我们可以预测输出结果。

  • 由于 a 是一个包含4个 int 类型元素的数组,在内存中占用4个 int 大小的连续空间。

  • ptr1[-1]:指向数组最后一个元素,即 a[3] 的值为4。

  • *ptr2:指向数组首元素后面的1字节内存,此内存内容未定义。

  • 因此,我们可以预测输出结果为 4,未定义的值(可能是未初始化的值或者随机值)。

这道题在随着后面的学习,会有更深刻的理解,可以在学习完《数组在内存中的储存》后,再来看这道题


📄练习4:

code:

#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;
}

运行结果:

1

💡解析

这里的数组初始化可不是下面这样:

在这里插入图片描述

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

里面是逗号表达式

逗号表达式:从左向右依次计算,但是整个表达式的结果是最后一个表达式的结果

int a[3][2] = { (0, 1), (2, 3), (4, 5) };
<===>
int a[3][2] = { 1, 3, 5 };

在这里插入图片描述

a[0]表示第一行数组名,是第一行首元素的地址,即a[0]==>&a[0][0]

p[0]==>*(p+0),即1

⭕所以,输出结果:1


📄练习5:

X86环境:

code:

#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;
}

运行结果:

FFFFFFFC,-4

💡解析

int a[5][5];

在这里插入图片描述

int(*p)[4];p = a;

在这里插入图片描述


&p[4][2]

在这里插入图片描述


 &p[4][2] - &a[4][2]

在这里插入图片描述


printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);

以%p打印和以%d打印是不一样的

%d打印就直接是-4

%p打印的是-4的补码,即FFFFFFFC


📄练习6:

code:

#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;
}

运行结果:

10,5

💡解析

int* ptr1 = (int*)(&aa + 1);

在这里插入图片描述
将&aa + 1强制转换成int* 型,因为ptr1是int*型

然后赋值给ptr1

在这里插入图片描述
*(ptr1 - 1)是10


int* ptr2 = (int*)(*(aa + 1));

*(aa + 1)<==>aa[1]

aa[1]本来就是一个地址,这里的强制类型转换其实是没有意义的,完全就是迷惑人的

将aa[1]赋值给ptr2

那么*(ptr2 - 1)就是5


📄练习7:

code:

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

运行结果:

at

💡解析

这里是将‘w’,’a’,‘a’的地址存到数组里面去

内存布局:

在这里插入图片描述


char** pa = a;
pa++;

a是数组首元素地址,即char*的地址

pa存放首元素地址

在这里插入图片描述


printf("%s\n", *pa);

对pa解引用,访问的就是第二个元素,即打印at


📄练习8:

code:

#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;
}

运行结果:

POINT
ER
ST
EW

💡解析

本道题是比较难的一道题目!!!笔试原题

char* c[] = { "ENTER","NEW","POINT","FIRST" };

内存布局:

在这里插入图片描述


char** cp[] = { c + 3,c + 2,c + 1,c };

内存布局:

在这里插入图片描述


char*** cpp = cp;

图解:

在这里插入图片描述


printf("%s\n", **++cpp);

++cpp=cpp+1

在这里插入图片描述
*++cpp第一层解引用,访问到的是c+2

通过c+2,得到的是P的地址

则再次解引用,以%s打印,得到POINT


printf("%s\n", *-- * ++cpp + 3);

再次++cpp

从上面基础上再+1

在这里插入图片描述
*++cpp解引用,得到c+1

– * ++cpp:在c+1上减去1,得到c

此时也就不再指向N,而是指向E

*-- * ++cpp:再解引用,得到E的地址

*-- * ++cpp + 3:得到ER


printf("%s\n", *cpp[-2] + 3);

cpp[-2]其实就是*(cpp-2)

*cpp[-2]+3也就是 * *(cpp-2)+3

cpp-2在上面的额基础上减2,cpp不会变
在这里插入图片描述
*(cpp-2)通过解引用,得到c+3

**(cpp-2) 再次解引用,得到F

**(cpp-2)+3 ,得到ST


printf("%s\n", cpp[-1][-1] + 1);

cpp[-1][-1]也就是 * (*(cpp-1)-1)

即, * (*(cpp-1)-1)+1

cpp-1图解:
在这里插入图片描述
*(cpp-1)解引用,得到c+2,也就是P的地址

*(cpp-1)-1,得到N的地址

 *(*(cpp-1)-1)再次解引用,拿到N的值* (*(cpp-1)-1)+1 ,再加1,得到EW

🚩总结

本节题目来自于公司笔试,前面的题目不算很难,需要我们熟练掌握
后面的题目比较难,需要我们逐个分析
理解完这些题目,相信大家对指针有更深刻理解
😏😏😏😏😏😏😏😏😏😏😏😏😏😏😏😏😏😏😏😏😏😏😏

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/51875.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

诚迈科技子公司智达诚远与Unity中国达成合作,打造智能座舱新时代

2023 年 8 月 23 日&#xff0c;全球领先的实时 3D 引擎 Unity 在华合资公司 Unity 中国举办发布会&#xff0c;正式对外发布 Unity 引擎中国版——团结引擎&#xff0c;并带来专为次世代汽车智能座舱打造的团结引擎车机版。发布会上&#xff0c;诚迈科技副总裁、诚迈科技子公司…

Wireshark数据抓包分析之UDP协议

一、实验目的&#xff1a; 通过使用wireshark对UDP数据包的抓取分析UDP协议的内容 二、预备知识&#xff1a; UDP协议的概念&#xff1a;UDP使用底层的互联网协议来传送报文&#xff0c;同IP一样提供不可靠的无连接传输服务。它也不提供报文到达确认、排序及流量控制等功能。 …

VScode中写Verilog时,iverilog语法自动纠错功能不起作用

VScode中编写Verilog时&#xff0c;iverilog语法自动纠错功能不起作用 问题&#xff1a;按照教程搭建vscode下Verilog编译环境&#xff0c;发现语法纠错功能一直无效&#xff0c;检查了扩展Verilog-HDL/SystemVerilog/Bluespec SystemVerilog的配置也没有任何问题。 错误原因&a…

uniapp离线打包apk - Android Studio

uniapp 离线打包 基于uni-app的andiord 离线打包 开发工具及所需要的jar包​1.将下载的App离线SDK解压打开&#xff0c;找到HBuilder-Integrate-AS &#xff0c;在Android Studio打开2.打开HBuilder X&#xff0c;发行->原生app本地打包->生成本地打包app资源3.在“HBuil…

R语言lasso惩罚稀疏加法(相加)模型SPAM拟合非线性数据和可视化

全文链接&#xff1a;https://tecdat.cn/?p33462 本文将关注R语言中的LASSO&#xff08;Least Absolute Shrinkage and Selection Operator&#xff09;惩罚稀疏加法模型&#xff08;Sparse Additive Model&#xff0c;简称SPAM&#xff09;。SPAM是一种用于拟合非线性数据的强…

RocketMQ-(7-1)-可观测-Metrics

RocketMQ 以 Prometheus 格式公开以下指标。您可以使用这些指标监视您的集群。 服务端 Metrics 指标生产者 Metrics 指标消费者 Metrics 指标 版本支持&#xff1a;以下指标 Metrics 是从 5.1.0 版本开始支持。 Metrics 指标详情​ Metric types​ 消息队列 RocketMQ 版定义…

centos 下扩容根目录

大体情况&#xff1a; 在VM虚拟机上安装了移动云的BCLinux镜像&#xff0c;磁盘设定为8G&#xff0c;但是用过一段时间之后根目录下磁盘已满&#xff0c;无法创建文件夹等操作&#xff0c;因此在VM上进行了磁盘扩容&#xff0c;扩容之后需要在系统上自行挂载&#xff0c;使用m…

DOS常见命令

DOS常见命令 DOS是什么如何打开DOScmd常见的命令集合 DOS是什么 DOC命令是我们浏览器中的终端 &#xff0c;但不同的是我们打开软件的方式 使用的是点击文件图标&#xff0c;点击图标的同时 我们也相当于使用一个命令 只是我们看不见而已 在电脑上操作的时候 通常都是使用命令…

Zoho CRM有哪些优势?

CRM能帮助企业管理客户信息、跟进销售机会、提高客户转化&#xff0c;显著提升企业的竞争力&#xff0c;是企业发展的重要工具。市场上有很多CRM品牌&#xff0c;企业该如何选型&#xff1f;这里给大家分享一个不错的CRM品牌 Zoho CRM有哪些优势&#xff1f; 功能全面&#x…

WebGL 缓冲区对象介绍,创建并使用缓冲区,使用缓冲区对象向顶点着色器传入多个顶点数据的所有步骤

目录 使用缓冲区对象 使用缓冲区对象向顶点着色器传入多个顶点的数据&#xff0c;需要遵循以下五个步骤。 创建缓冲区对象&#xff08;gl.createBuffer&#xff08;&#xff09;&#xff09; gl.createBuffer&#xff08;&#xff09;的函数规范 gl.deleteBuffer &#…

Java小项目|拼图小游戏|黑马

项目技术需求 Java基础 基本if、forio流File集合JFrame【看得懂就行】 项目素材以及打包exe&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rPazJezTwS9O6e8BoYNIYA?pwd6666 项目运行截图 项目来源 哔哩哔哩-黑马程序员上 哔哩哔哩-黑马程序员下 项目介绍&…

线性代数(五) 线性空间

前言 《线性代数(三) 线性方程组&向量空间》我通过解线性方程组的方式去理解线性空间。此章从另一个角度去理解 空间是什么 大家较熟悉的&#xff1a;平面直角坐标系是最常见的二维空间 空间由无穷多个坐标点组成 每个坐标点就是一个向量 反过来&#xff0c;也可说&…

iptables的使用规则

环境中为了安全要限制swagger的访问&#xff0c;最简单的方式是通过iptables防火墙设置规则限制。 在测试服务器中设置访问swagger-ui.html显示如下&#xff0c;区分大小写&#xff1a; iptables设置限制访问9783端口的swagger字段的请求&#xff1a; iptables -A INPUT -p t…

云计算在线实训系统建设方案

一、 人工智能与云计算系统概述 人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;是一种模拟人类智能的科学和工程&#xff0c;通过使用计算机系统来模拟、扩展和增强人类的智能能力。人工智能涉及多个领域&#xff0c;包括机器学习、深度学习、自然…

HarmonyOS开发:超详细了解项目的工程结构

前言 系列文章目录&#xff1a; HarmonyOS开发第一步&#xff0c;熟知开发工具DevEco Studio 当我们熟练的掌握了DevEco Studio之后&#xff0c;就可以创建项目进行练习了&#xff0c;和市场上大多数IDE一样&#xff0c;DevEco Studio也给我们提供了很多的实例模板&#xff0c…

uniapp-form表单

<template><view class"ptb-20 plr-30 bg min100"><view class"bg-white radius-20 pd-30"><view class"bold mt-30 mb-50 size-32">选择方式&#xff1a;</view><u--form labelPosition"left" :mod…

基于PIC单片机篮球计分计时器

一、系统方案 本设计采用PIC单片机作为主控制器&#xff0c;矩阵键盘控制&#xff0c;比分&#xff0c;计时控制&#xff0c;24秒&#xff0c;液晶12864显示。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统初始化 2、液晶显示程序 /*************…

系统架构合理性的思考 | 京东云技术团队

最近牵头在梳理部门的系统架构合理性&#xff0c;开始工作之前&#xff0c;我首先想到的是如何定义架构合理性&#xff1f; 从研发的角度来看如果系统上下文清晰、应用架构设计简单、应用拆分合理应该称之为架构合理。 基于以上的定义可以从以下三个方面来梳理评估&#xff1…

怎么检测UI卡顿?(线上及线下)

什么是UI卡顿&#xff1f; 在Android系统中&#xff0c;我们知道UI线程负责我们所有视图的布局&#xff0c;渲染工作&#xff0c;UI在更新期间&#xff0c;如果UI线程的执行时间超过16ms&#xff0c;则会产生丢帧的现象&#xff0c;而大量的丢帧就会造成卡顿&#xff0c;影响用…

Matlab彩色图像转索引图像

索引图像 索引图像是一种把像素值直接作为RGB调色板下标的图像。索引图像包括一个数据矩阵X&#xff0c;一个调色板矩阵map&#xff0c;也称为颜色映像矩阵。其中&#xff0c;数据矩阵X可以是8位无符号整型、16位无符号整型或双精度类型。调色板矩阵map是一个m3的数据阵列&…