【C语言】10.C语言指针(5)

news2025/1/13 10:37:50

文章目录

  • 1.sizeof和strlen的对比
    • 1.1 sizeof
    • 1.2 strlen
    • 1.3 sizeof 和 strlen的对⽐
  • 2.数组和指针笔试题解析
    • 2.1 ⼀维数组
    • 2.2 字符数组
    • 2.3 ⼆维数组
  • 3.指针运算笔试题解析
    • 3.1 题⽬1
    • 3.2 题⽬2
    • 3.3 题⽬3
    • 3.4 题⽬4
    • 3.5 题⽬5
    • 3.6 题⽬6
    • 3.7 题⽬7


1.sizeof和strlen的对比

1.1 sizeof

在学习操作符的时候,我们学习了 sizeofsizeof 计算变量所占内存内存空间大小的,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。

sizeof 只关注占用内存空间的大小,不在乎内存中存放什么数据。

#inculde <stdio.h>
int main()
{
    int a = 10;
    printf("%d\n", sizeof(a));
    printf("%d\n", sizeof a);
    printf("%d\n", sizeof(int));

    return 0;
}

打印:

4
4
4

sizeof是操作符不是函数。


1.2 strlen

strlen 是C语言库函数,功能是求字符串长度。

函数原型如下:

size_t strlen ( const char * str );

统计的是从 strlen 函数的参数 str 中这个地址开始向后, \0 之前字符串中字符的个数。

strlen 函数会一直向后找 \0 字符,直到找到为止,所以可能存在越界查找。

#include <stdio.h>
#include <string.h>
int main()
{
    char arr1[3] = {'a', 'b', 'c'};
    char arr2[] = "abc";
    printf("%d\n", strlen(arr1));
    printf("%d\n", strlen(arr2));

    printf("%zd\n", sizeof(arr1));
    printf("%zd\n", sizeof(arr2));
    return 0;
}

打印:

35
3
3
4

在这里插入图片描述

printf("%d\n", strlen(arr1));:因为arr1里面没有\0,所以会往后找到\0才结束,打印的是随机值。

printf("%d\n", strlen(arr2));:因为arr2里面有\0,所以打印3。

printf("%zd\n", sizeof(arr1));:因为arr1里面有3个元素,打印3。

printf("%zd\n", sizeof(arr2));:因为arr2里面有4个元素,打印4。


1.3 sizeof 和 strlen的对⽐

sizeof

  1. sizeof是操作符
  2. sizeof计算操作数所占内存的大小,单位是字节
  3. 不关注内存中存放什么数据

strlen

  1. strlen是库函数,使用需要包含头文件 string.h
  2. srtlen是求字符串长度的,统计的是 \0 之前字符的个数
  3. 关注内存中是否有 \0 ,如果没有 \0 ,就会持续往后找,可能会越界

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

2.1 ⼀维数组

数组名是数组首元素地址,但是有两个例外:

  1. sizeof(数组名)
  2. &数组名
int main(){
    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));
    
    return 0;
}

打印:

16//sizeof(a)
8//sizeof(a+0)
4//sizeof(*a)
8//sizeof(a+1)
4//sizeof(a[1])
8//sizeof(&a)
16//sizeof(*&a)
8//sizeof(&a+1)
8//sizeof(&a[0])
8//sizeof(&a[0]+1)

printf("%d\n",sizeof(a));:16

数组名a单独放在sizeof内部,a表示整个数组,计算的是整个数组的大小,单位是字节。


printf("%d\n",sizeof(a+0));:8

这里的a是数组名表示数组首元素地址,a+0还是首元素的地址。

这里sizeof计算的是首元素地址的大小,也就是4/8。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


printf("%d\n",sizeof(*a));:4

这里的a是数组名表示数组首元素地址,*a是对首元素地址的解引用,也就是首元素,就是a[0],所以是4个字节。

*a 就是 *(a+0) 就是 a[0]


printf("%d\n",sizeof(a+1));:8

这里的a是数组名表示数组首元素地址,a+1是第二个元素的地址,也就是a[1]的地址。

这里sizeof计算的是第二个元素地址的大小,也就是4/8。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


printf("%d\n",sizeof(a[1]));:4

这里的a[1]就是数组第二个元素,所以是4个字节。


printf("%d\n",sizeof(&a));:8

&a:这里的a表示整个数组,&a就是整个数组的地址。

数组的地址也是地址,和首元素地址大小一样。是地址就是4/8个字节的长度。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


printf("%d\n",sizeof(*&a));:16

这里用两种角度讲一下:

  1. *&a,这里的*&抵消了,所以sizeof(*&a) == sizeof(a),计算的是整个数组的大小,单位是字节,也就是16。

  2. &a这是数组的地址,类型是:int(*)[4],也就是数组指针类型。

    而指针类型,决定了它解引用操作能访问多大空间。

    一个整型指针解引用访问4个字节,一个字符指针解引用访问1个字节,而一个数组指针解引用访问1个数组。

    所以*&a是对一个数组的地址的解引用,访问的是一个数组。这个数组的大小是16个字节。


printf("%d\n",sizeof(&a+1));:8

&a是数组的地址,&a+1是跳过整个数组后的那个地址。

既然&a+1是地址,那么就是4/8个字节。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


printf("%d\n",sizeof(&a[0]));:8

&a[0]是数组首元素地址,是地址,那么就是4/8个字节。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


printf("%d\n",sizeof(&a[0]+1));:8

&a[0]是数组首元素地址,&a[0]+1是数组第二个元素地址,是地址,那么就是4/8个字节。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


2.2 字符数组

代码1:

char arr[] = {'a','b','c','d','e','f'};//6个字符
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));

打印:

6
8
1
1
8
8
8

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

里面有6个字符元素,所以是6。


printf("%d\n", sizeof(arr+0));:8

这里的arr是数组名表示数组首元素地址,arr+0还是首元素的地址。

这里sizeof计算的是首元素地址的大小,也就是4/8。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


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

这里的arr是数组名表示数组首元素地址,*arr是对首元素地址的解引用,也就是首元素,就是arr[0],所以是1个字节。


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

这里的arr[1]就是字符数组第二个元素,所以是1个字节。


printf("%d\n", sizeof(&arr));:8

&arr:这里的arr表示整个数组,&arr就是整个数组的地址。

数组的地址也是地址,和首元素地址大小一样。是地址就是4/8个字节的长度。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


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

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

既然&arr+1是地址,那么就是4/8个字节。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


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

&arr[0]是数组首元素地址,&arr[0]+1是数组第二个元素地址,是地址,那么就是4/8个字节。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。

代码2:

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));

printf("%d\n", strlen(arr));:随机值

因为arr是首元素地址,我们不知道里面有没有\0,也不知道\0之前有多少个字符


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

因为arr+0是首元素地址,我们不知道里面有没有\0,也不知道\0之前有多少个字符

而且这个打印的随机值,应该是和上面打印的随机值一样的


printf("%d\n", strlen(*arr));:这个地方程序直接就崩了

这个*arr是对首元素地址的解引用,得到的是arr[0]的值。

arr[0]'a'a的ASCII码值是97。

就相当于把97传给了strlenstrlen就会把97当成地址去访问。

这个地方程序直接就崩了。

在这里插入图片描述

这里的61是16进制,换成10进制就是97。


printf("%d\n", strlen(arr[1]));:这个地方程序直接就崩了

arr[1]'b'b的ASCII码值是98。

就相当于把98传给了strlenstrlen就会把98当成地址去访问。

这个地方程序直接就崩了。


printf("%d\n", strlen(&arr));:随机值

&arr是数组的地址,我们不知道里面有没有\0,也不知道\0之前有多少个字符

而且这个打印的随机值,应该是和上面打印的随机值一样的


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

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

我们不知道里面有没有\0,也不知道\0之前有多少个字符

而且这个打印的随机值,应该比上面打印的随机值少6。因为跳过了这个数组,这个数组 里面有6个元素。


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

&arr[0]是数组首元素地址,&arr[0]+1是数组第二个元素地址。

我们不知道里面有没有\0,也不知道\0之前有多少个字符

而且这个打印的随机值,应该比上面打印的随机值少1。因为跳过了第一个数组元素。

代码3:

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));

打印:

7
8
1
1
8
8
8

数组名是数组首元素地址,但是有两个例外:

  1. sizeof(数组名)
  2. &数组名

printf("%d\n", sizeof(arr));:7

数组名单独放在sizeof内部,计算的是数组的大小。(满足上面的第一个例外)


printf("%d\n", sizeof(arr+0));:8

arr+0是数组首元素地址。是地址就是4/8个字节的长度。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


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

*arr是首元素 ,sizeof(*arr)就是1。


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

arr[1]是数组第二个元素,sizeof(arr[1])就是1。


printf("%d\n", sizeof(&arr));:8

&arr是数组的地址。是地址就是4/8个字节的长度。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


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

&arr+1是跳过整个数组,指向数组后的那个位置的地址。是地址就是4/8个字节的长度。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


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

&arr[0]是首元素地址,&arr[0]+1是数组第二个元素的地址。是地址就是4/8个字节的长度。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。

代码4:

char arr[] = "abcdef";
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));

printf("%d\n", strlen(arr));:6

arr是数组首元素地址,strlen是从第一个元素开始统计\0之前的字符个数。


printf("%d\n", strlen(arr+0));:6

arr+0是数组首元素地址,strlen是从第一个元素开始统计\0之前的字符个数。


printf("%d\n", strlen(*arr));:这个地方程序直接就崩了

*arr是数组第一个元素,也就是'a'a的ASCII码值是97。

就相当于把97传给了strlenstrlen就会把97当成地址去访问。

这个地方程序直接就崩了。


printf("%d\n", strlen(arr[1]));:这个地方程序直接就崩了

arr[1]是数组第二个元素,也就是'b'b的ASCII码值是98。

就相当于把98传给了strlenstrlen就会把98当成地址去访问。

这个地方程序直接就崩了。


printf("%d\n", strlen(&arr));:6

&arr是数组的地址,数组的地址和数组首元素的地址是指向同一个位置的。

那么strlen也是从第一个元素的位置开始向后访问的。


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

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

我们不知道里面有没有\0,也不知道\0之前有多少个字符

而且这个打印的随机值,应该比上面打印的随机值少6。因为跳过了这个数组,这个数组 里面有6个元素。


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

&arr[0]是首元素地址,&arr[0]+1是数组第二个元素的地址。

那么strlen是从第二个元素的位置开始向后访问的。

代码5:

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));

打印:

8
8
1
1
8
8
8

数组名是数组首元素地址,但是有两个例外:

  1. sizeof(数组名)
  2. &数组名

printf("%d\n", sizeof(p));:8

p是指针变量。sizeof(p)计算的是指针变量p的大小,4/8个字节。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


printf("%d\n", sizeof(p+1));:8

在这里插入图片描述

p是指针变量。我们假设p的地址是:0x0012ff40p+1的地址是:0x0012ff41

也就是第二个元素的地址。

sizeof(p+1)计算的是二个元素的地址的大小,4/8个字节。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


printf("%d\n", sizeof(*p));:1

p的类型是char**p解引用只能访问一个字符,所以sizeof(*p)为1。


printf("%d\n", sizeof(p[0]));:1

p[0]就是*(p+0)也就是*p*p解引用只能访问一个字符,所以sizeof(*p)为1。


printf("%d\n", sizeof(&p));:8

&p是指针变量p的地址。是地址就是4/8个字节的长度。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


printf("%d\n", sizeof(&p+1));:8

&p的类型是char* *,也就相当于char* *ptr = &p;

前面的char*说明ptr指向的对象是char*,这里的*ptr说明ptr是一个指针。

在这里插入图片描述

&p+1相当于ptr+1,也就相当于它跳过了一个char*的指针。

在这里插入图片描述

跑到了图中橙色箭头的地方。和b没什么关系。

&pp的地址,&p+1是跳过p变量,指向了p的后面。

是地址就是4/8个字节的长度。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


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

p[0]就是*(p+0)就是*p

&p[0]就是*&p[0],实际上就是p

&p[0]+1就是b的地址。

是地址就是4/8个字节的长度。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。

代码6:

char *p = "abcdef";
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));

printf("%d\n", strlen(p));:6

p是指针变量。p里面存的是a的地址,指向的是字符串首元素的地址。


printf("%d\n", strlen(p+1));:5

p是指针变量。p里面存的是a的地址,p+1里面存的是b的地址,指向的是字符串第二个元素的地址。


printf("%d\n", strlen(*p));:程序崩溃

*p是数组第一个元素,也就是'a'a的ASCII码值是97。

就相当于把97传给了strlenstrlen就会把97当成地址去访问。

这个地方程序直接就崩了。


printf("%d\n", strlen(p[0]));:程序崩溃

p[0]也就是*(p+0)也就是*p

*p是数组第一个元素,也就是'a'a的ASCII码值是97。

就相当于把97传给了strlenstrlen就会把97当成地址去访问。

这个地方程序直接就崩了。


printf("%d\n", strlen(&p));:随机数

&p是指针变量p的地址。

如果p的地址是0x0012ff40,那么就是去p的内存空间里面去找的。

我们不知道里面有没有\0,也不知道\0之前有多少个字符


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

&p是指针变量p的地址。&p+1是指向指针变量p后面的地址。

我们不知道里面有没有\0,也不知道\0之前有多少个字符


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

&p[0]是首元素地址,&p[0]+1是第二个元素地址。


2.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]));

打印:

48
4
16
8
4
8
16
8
16
16
16

讲这个前面我们先复习一下:

在这里插入图片描述

我们一般说的时候是理解上面的那个3x4的图的,但是实际上内存里是连续存放的,也就是说内存里是像下面这样1x12这么放的。

数组名是数组首元素地址,但是有两个例外:

  1. sizeof(数组名)
  2. &数组名

printf("%d\n",sizeof(a));:48

a作为数组名,单独放在sizeof内部,a表示的是整个数组,计算的是整个数组的大小,单位是字节。

三行四列12个元素。每个元素乘4。


printf("%d\n",sizeof(a[0][0]));:4

a[0][0]是第一行第一列元素,大小是4个字节。


printf("%d\n",sizeof(a[0]));:16

a[0]是第一行的数组名,单独放在sizeof内部,计算的是数组的大小。

第一行4个元素,也就是4x4是16个字节。


printf("%d\n",sizeof(a[0]+1));:8

这里面a[0]是第一行的数组名,但是没有单独放在sizeof内部。所以这个a[0]表示的是首元素地址。&a[0][0]

也就是第一行第一个数组元素的地址。

a[0]+1就等价于&a[0][0]+1就等价于&a[0][1]

也就是a[0][1]的地址。

是地址就是4/8个字节的长度。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


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

a[0]+1就等价于&a[0][0]+1就等价于&a[0][1]

*(a[0]+1)就相当于*&a[0][1]就相当于a[0][1]

一个元素,也就相当于1x4等于4个字节。


printf("%d\n",sizeof(a+1));:8

a是二维数组的名字,并没有单独放在sizeof内部,

a表示的是首元素的地址,也就是第一行的地址。类型是int(*)[4]

a+1是第二行的地址。

是地址就是4/8个字节的长度。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


printf("%d\n",sizeof(*(a+1)) );:16

下面从两个角度讲一下:

  1. *(a+1)其实就是a[1],也就是第二行的数组名,单独放在sizeof内部,计算的是第二行的大小。

    第二行4个元素,也就是4x4等于16个字节。

  2. a+1是第二行的地址,类型是int(*)[4],解引用访问的是这个数组a[1]

    第二行4个元素,也就是4x4等于16个字节。


printf("%d\n",sizeof(&a[0]+1));:8

&a[0]是第一行的数组名,&数组名,表示第一行的地址,&a[0]+1就是第二行地址,

是地址就是4/8个字节的长度。

因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。


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

&a[0]是第一行的数组名,&数组名,表示第一行的地址,&a[0]+1就是第二行地址,

*(&a[0]+1))第二行的地址解引用,就是第二行。

第二行4个元素,也就是4x4等于16个字节。


printf("%d\n",sizeof(*a));:16

下面从两个角度讲一下:

  1. a是首元素的地址,也就是第一行的地址 ,*a就是第一行的元素。

    第一行4个元素,也就是4x4等于16个字节。

  2. *a就是*(a+0)就是a[0],就是第一行的元素。

    第一行4个元素,也就是4x4等于16个字节。


printf("%d\n",sizeof(a[3]));:16

乍一看越界了,但其实程序还是可以运行的。

为什么呢?

因为sizeof内部的表达式是不会真实计算的。

就像a[3]是第四行的数组名,但他不会真实去访问第四行,也就谈不上越界。

那这里为什么会算出结果呢?

因为a[3]第四行的数组名,他的类型是int[4](如果第四行存在的话 , 就是这个类型)

第四行4个元素,也就是4x4等于16个字节。(也就相当于说sizeof是根据类型来推断的)


数组名的意义

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

3.指针运算笔试题解析

3.1 题⽬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;
}

打印:

2,5

为什么呢?

在这里插入图片描述


3.2 题⽬2

在X86环境下

假设结构体的大小是20个字节

程序的结果是什么?

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

为什么呢?

  1. 指针+1和类型有关系。

  2. 整数+1就是+1。

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

p + 0x1这个0x1是十六进制,也就是说这里其实是p + 1

p是结构体指针,+1就是跳过一个结构体,也就是20个字节(20字节这个条件前面给了)

这里相当于就是0x100014

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

把指针变量p强制类型转换成unsigned int,也就相当于整数+1。

0x100001

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

+1是跳过一个unsigned int类型的变量,是4个字节。

也就是0x100004


3.3 题⽬3

程序的结果是什么?

#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] = { 1, 3, 5 };

数组里面:

1 3
5 0
0 0

a[0]是首元素地址,p是一个指针变量。

p[0]就等价于*(p+0)就等价于*p

也就是1


3.4 题⽬4

假设环境是x86环境,程序输出的结果是啥?

#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(*p)[4];

p是一个数组指针,指向的类型是intp指向的数组有4个元素。

p = a;

a的类型是int(*)[5]

p的类型是int(*)[4]

最后会采用p的类型。

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

p[4][2]*(*(p+4)+2)等价。

*(p+4)就是p+4p+5之间的那段。

在这里插入图片描述

p[4][2]a[4][2]之间差了4个元素,所以&p[4][2] - &a[4][2]等于-4

%d是十进制形式

%p是补码形式

//-4
//原码
10000000 00000000 00000000 00000100
//反码
11111111 11111111 11111111 11111011
//补码
11111111 11111111 11111111 11111100

写成16进制就是:

0xFF FF FF FC


3.5 题⽬5

程序的结果是什么?

#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

为什么呢?

在这里插入图片描述


3.6 题⽬6

程序的结果是什么?

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

打印:

at

为什么呢?

char *a[] = {"work","at","alibaba"};

这个是char*的指针数组,里面有3个元素,分别是w的地址,at里面a的地址,alibaba里面a的地址。(也就是三个字符串的首地址)

char**pa = a;

a表示首元素地址。char**这个二级指针变量pa里面存的是a的第一个元素的地址。即指向字符串 work 的指针。

pa++;

这行代码将 pa 指针向前移动一个位置,即指向数组 a 的第二个元素,也就是指向字符串 at 的指针。

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

这行代码通过解引用 pa 指针(即 *pa ),打印出它当前指向的字符串 at,并换行。

%s打印的是地址,是从这个地址往后打印字符串。


3.7 题⽬7

程序的结果是什么?

#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

为什么呢?

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

在这里插入图片描述

然后%s往后打印,打印POINT


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

在这里插入图片描述

然后%s往后打印,打印ER


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

在这里插入图片描述

然后%s往后打印,打印ST


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

在这里插入图片描述

然后%s往后打印,打印EW

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

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

相关文章

正则表达式:从左到右一个个去匹配,api帮助文档搜:Pattern

正则匹配字符&#xff1a; \:在java里面是转义字符的意思&#xff0c;在java里面可以理解为&#xff1a;\\\ “你”匹配一个&#xff1a;. a匹配一个&#xff1a;. 匹配多个的情况&#xff1a; 正则表达式练习&#xff1a; 忽略大小写的书写方式&#xff1a;(?i) 正则表达式在…

【仿真建模-anylogic】Dynamic Event原理解析

Author&#xff1a;赵志乾 Date&#xff1a;2024-06-12 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 1. 类图 2. 原理解析 EventOriginator是Anylogic中各类事件的父类&#xff0c;对外暴露的接口主要有: 函数功能boolean isActive()判定…

泛微OA E9 浏览框显示的数据根据表单字段过滤

一、实现效果&#xff1a;如图所示&#xff0c;字段“物品名称”浏览框显示的数据根据“类型”字段进行过滤。 二、实现方法&#xff1a; 1、建模引擎-应用建模-浏览框-浏览框列表中单击“办公耗材”-“浏览框列表”-“操作”-“编辑” 2、sql语句中根据OA自带是示例增加where…

autoware lidar-centerpoint 点云在rviz上叠加显示问题

在使用自采数据包放入autoware中的lidar_centerpoint上进行检测时发现&#xff0c;在rviz可视化上出现问题&#xff1a;多帧点云在一个位置上不断叠加&#xff0c;不能正常随时间显示。 如下图所示&#xff1a; 解决方法&#xff1a; 出现上述问题是因为autoware默认使用的是…

多用户竞拍商城系统 挂售转卖竞拍商城系统源码 竞拍系统 竞拍系统开发定制 转拍闪拍系统 后端PHP+前端UNIAPP源码+教程

挂售转卖竞拍商城系统源码/竞拍系统/转拍闪拍系统/后端PHP前端UNIAPP源码 玩法简介 ①、后台可添加商品进行挂单 ②、后台设置场次以及场次开始时间 ③、用户抢单 ④、抢单以后可选择提货或者转售 ⑤、玩家寄售需按照后台设置百分比进行加价 ⑥、玩家寄售需支付手续费(余额支付…

图的存储表示

目录 概述 图的定义 图的存储结构 1&#xff09;邻接矩阵 2&#xff09;邻接表 3&#xff09;十字链表 4&#xff09;邻接多重表 概述 数据结构分为两类&#xff0c;一类是具有递归结构的数据结构&#xff0c;也就是可以给出一个递归定义的数据结构&#xff0c;一类是非递归结构…

你好!Python

目录 写在前面 编辑推荐 内容简介 作者简介 前言 推荐理由 写在后面 写在前面 本期博主给大家推荐一本全新出版的Python图书&#xff0c;感兴趣的小伙伴一起来看看吧&#xff01; 《你好&#xff01;Python 全彩印刷 从零开始学 语言轻松幽默 版式精美 视频讲解 提供代…

成功的管理者必做的10件事

管理者是团队的核心&#xff0c;他们不仅要有出色的领导能力&#xff0c;还要具备应对各种挑战的智慧和勇气。以下是成功的管理者必做的10件事&#xff1a; 1、设定明确的目标 管理者要为团队设定清晰、可衡量且具有挑战性的目标&#xff0c;这些目标不仅与组织的长期愿景相…

高考志愿填报,如何分析自己的优势特长?

据不完全统计&#xff0c;80%的高三学生&#xff0c;不清楚自己要报什么专业&#xff0c;以及将来从事哪种职业&#xff1f;有的人则是完全听从父母的安排&#xff0c;有人听老师的建议&#xff0c;有人追热门&#xff0c;有人查什么专业可以拿高薪.... 而现实就是&#xff1a…

水产养殖监测站的工作原理

TH-LSZ06水产养殖监测站是保障水产质量安全的重要设施&#xff0c;监测水产养殖环境&#xff1a;负责监测水产养殖基地的水质、底泥、养殖物质等&#xff0c;确保养殖环境的适宜性和安全性。通过对养殖环境的实时监测&#xff0c;可以及时发现和预警水产疾病和污染问题&#xf…

pdf文件怎么改变大小?在线快速压缩pdf的方法

pdf作为一种常用的文件格式&#xff0c;使用这种文件类型的好处在于不仅拥有更好的兼容性&#xff0c;还可以设置密码来保证安全性&#xff0c;防止未授权用户查看内容&#xff0c;所以现在导出文件展示都会采用这种格式的来做内容展示。当遇到pdf文件过大问题时&#xff0c;想…

Unity | Tilemap系统

目录 一、准备工作 1.插件导入 2.资源导入 二、相关组件介绍 1.Grid组件 2.Tilemap组件 3.Tile 4.Tile Palette 5.Brushes 三、动态创建地图 四、其他功能 1.移动网格上物体 2.拖拽缩放地图 Unity Tilemap系统为2D游戏开发提供了一个直观且功能强大的平台&#xff…

知网学术期刊《数学之友》投稿难度大吗?

知网学术期刊《数学之友》投稿难度的大小&#xff0c;实际上取决于多个因素的综合考量&#xff0c;包括论文的质量、研究方向的匹配度、期刊的审稿标准以及投稿者的学术背景等。 首先&#xff0c;从期刊的定位和特点来看&#xff0c;《数学之友》作为一份专注于数学领域的学术期…

利用R包“Phenotype”对表型值进行检查

首先&#xff0c;你需要确保你已经安装了R和RStudio&#xff08;如果你想用RStudio的话&#xff09;。然后&#xff0c;你可以按照以下步骤进行操作&#xff1a; 加载数据&#xff1a;首先&#xff0c;你需要加载你的表型数据。如果你的数据是以CSV、Excel等格式保存的&#x…

vue2中如何动态渲染组件

vue2中如何动态渲染组件 动态渲染组件代码解读通过函数调用渲染组件 封装一个函数调用的二次确认弹窗如何让外部知道用户点击了取消还是确定呢&#xff1f; 思考小结 vue2 的项目中&#xff0c;main.js 文件中有一个挂载 App.vue 组件的方法&#xff1a; new Vue({name: Root,…

Python酷库之旅-比翼双飞情侣库(01)

目录 一、xlrd库的由来 二、xlrd库优缺点 1、优点 1-1、支持多种Excel文件格式 1-2、高效性 1-3、开源性 1-4、简单易用 1-5、良好的兼容性 2、缺点 2-1、对.xlsx格式支持有限 2-2、功能相对单一 2-3、更新和维护频率低 2-4、依赖外部资源 三、xlrd库的版本说明 …

Linux C语言:多级指针(void指针和const)

一、多级指针 把一个指向指针变量的指针变量&#xff0c;称为多级指针变量对于指向处理数据的指针变量称为一级指针变量指向一级指针变量的指针变量称为二级指针变量 1、二级指针变量的说明形式 <数据类型> ** <指针名> &#xff1b; 一张图理解二级指针 2、多…

intouch的报警怎么发到短信/微信上

Intouch 与 GRM_OPCGATE 通讯协议 一、安装 FS GateWay&#xff08;INTOUCH 驱动盘里有这个驱动程序&#xff0c;根目录:\INTOUCH10.0\DA Server\WW\FSG-1.5.100&#xff09; 。注&#xff1a;安装 INTOUCH 时&#xff0c;可能已同时安装了这个驱动&#xff0c;如图在下查看&am…

【Java】如何提升RocketMQ顺序消费性能?

一、问题解析 我们先来了解一下 RocketMQ 顺序消费的实现原理。RocketMQ 支持局部顺序消息消费&#xff0c;可以保证同一个消费队列上的消息顺序消费。例如&#xff0c;消息发送者向主题为 ORDER_TOPIC 的 4 个队列共发送 12 条消息&#xff0c; RocketMQ 可以保证 1、4、8 这…

ultralytics框架讲解

ultralytics简介 Ultralytics是一个开源的计算机视觉和深度学习框架&#xff0c;旨在简化训练、评估和部署视觉模型的过程。该框架提供了一系列流行的视觉模型&#xff0c;包括YOLOv5、YOLOv4、YOLOv3、YOLOv3-tiny、YOLOv5-tiny、EfficientDet、PAN、PP-YOLO等&#xff0c;并提…