C语言指针相关知识(第五篇章)(非常详细版)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、sizeof和strlen对比
  • 二、数组之间的比较(依据strlen和sizeof来呈现)
    • (一)、一维整型数组
    • (二)、字符数组
    • (三)、二维数组
  • 三、复杂指针运算讲解
  • 总结


前言

`本文主要介绍strlen与sizeof的区别,利于strlen与sizeof来对比一维数组,字符数组以及二维数组,加深理解数组之间的区别与联系,最后讲解几个复杂指针的运算


提示:以下是本篇文章正文内容,下面案例可供参考

一、sizeof和strlen对比

  • sizeof是一种操作符,它所计算的是所占内存空间的大小,单位是字节,如果操作数是类型的话计算的是使用类型的创建的变量所占内存空间的大小。在这里注意:sizeof只关注占用内存空间的大小,不在乎内存中存放什么数据,而且sizeof参数中的表达式不实际运算。
#include<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,不管内存中存放的是什么数据,只关注占用的内存空间大小,int类型也好,a变量也好,它们都占有4个字节大小。

  • strlen是C语言库函数,它的功能就是求字符串长度,函数原型为: size_t strlen ( const char * str );它统计的是从参数str中这个地址开始向后,到\0之前的字符个数,如果没有找到\0,strlen函数会一直向后统计,直到找到\0为止,所以可能会存在越界的行为。
#include<stdio.h>
int main()
{char arr1[3] = { 'a', 'b', 'c' };char arr2[] = "abc";printf("%d\n", strlen(arr1));printf("%d\n", strlen(arr2));
}

在这里插入图片描述
理论上来说,打印出来的数应该一样,但由运行结果来看,arr1的字符数量明显不对,这是因为arr1没有设置\0,而arr2用字符串赋值后面自带一个\0,所以在统计arr1的字符数量时候,存在了越界问题

  • 两者对比:
    在这里插入图片描述

二、数组之间的比较(依据strlen和sizeof来呈现)

下面代码我们是在X86环境下运行

(一)、一维整型数组

  • 分析以下代码:
//在X86环境下运行
#include<stdio.h>
int main()
{int a[] = { 1,2,3,4 };printf("%zd\n", sizeof(a));//第一条:16,a表示的是整个数组,整个数组的大小,单位是字节printf("%zd\n", sizeof(a + 0));//第二条:4,a代表首元素地址printf("%zd\n", sizeof(*a));//第三条:4,a代表首元素地址,解引用后为第一个整型大小printf("%zd\n", sizeof(a + 1));//第四条:4,a代表首元素地址,a+1代表第二个元素地址printf("%zd\n", sizeof(a[1]));//第五条:4,为数组第二个元素整型大小printf("%zd\n", sizeof(&a));//第六条:4//为整个数组的地址,只要是地址就是4printf("%zd\n", sizeof(*&a));//第七条:16//解释://1.&和*相互抵消sizeof(*&a)==sizeof(a)//2.&a-是数组地址,类型是int(*)[4]数组指针类型,*&a访问的就是这个数组printf("%zd\n", sizeof(&a + 1));//第八条:4,第二个数组的地址,只要是地址就是4printf("%zd\n", sizeof(&a[0]));//第九条:4,第一个元素地址,只要是地址就是4printf("%zd\n", sizeof(&a[0] + 1));//第十条:4,第二个元素地址,只要是地址就是4
}

在这里插入图片描述
分析:

  • 第一条代码:运行结果为16.这里的数组名因为sizeof的参数只有a自己,所以代表的是整个数组,一个数组4个元素,一个整型元素4个字节(在X86环境下)所以,最终结果为16
  • 第二条代码:运行结果为4.这里的数组名代表的是首元素地址,因为sizof中参数不只有a自己还有+0,在X86环境下,只要是地址,它的大小就是4个字节
  • 第三条代码:运行结果为4.a代表的是首元素地址,即整型元素1的地址,然后进行解引用,得出的是整型1,它所占空间为4个字节。
  • 第四条代码:运行结果为4.a代表的是首元素地址,加1后代表的是第二个元素的地址,只要是地址,其所占用的空间就是4个字节。
  • 第五条代码:运行结果为4.a[1]代表的是数组第二个元素2,它是int型元素,是4个字节
  • 第六条代码:运行结果为4.&a代表整个数组的地址,只要是地址,在此环境下就是4个字节。
  • 第七条代码:运行结果为16.两种解释,第一种:&和 * 相互抵消sizeof( * %a)==sizeof(a),与第一条运行结果相同。第二种解释:&a是代表的是整个数组地址,类型是int( * )[4]数组指针类型,* &a访问的就是这个数组,而这个数组就是16个字节。
  • 第八条代码:运行结果为4。&a+1代表的是第二个数组的地址,只要是地址就是4个字节。
  • 第九条代码:运行结果为4.&a[0]代表的是第一个元素的地址,只要是地址那么大小就是4个字节。
  • 第十条代码:运行结果为4.&a[0]+1代表的是第二个元素的地址,只要是地址那么大小就是4个字节。

以上的每一条代码深刻领会后,就会感受到一维数组的细节内容。像对一维数组数组名的理解,一维数组中解引用的理解,一维数组的地址的理解,一维数组中的运算操作等等。

(二)、字符数组

  • 分析下面代码1:
#include<stdio.h>
int main()
{char arr[] = { 'a','b','c','d','e','f' };printf("%zd\n", strlen(arr));//第一条:随机值,arr代表数组首元素地址printf("%zd\n", strlen(arr + 0));//第二条:随机值,arr代表数组首元素地址//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));//第七条:随机值
}

在这里插入图片描述
分析:

  • 第一条代码:运行结果为19,arr代表的是数组首元素的地址,从此地址出发,直到找到\0为止,但是我们一开始没有在数组中赋值\0,所以最终得出的是随机值。

  • 第二条代码:运行结果为19.arr+0也代表首元素地址,分析同第一条代码

  • 第三条代码:运行结果为程序错误,我们这里给注释掉。arr维首元素地址,对其进行解引用,*arr代表的就是‘a’字符,它的ASCII值为97,将其传给strlen,strlen会认为97就是地址,然后去访问内存,会发生访问冲突

  • 第四条代码:运行结果为程序错误,arr[1],代表的是’b’字符,它的ASCII值为98,将其传给strlen,strlen会认为98就是地址,然后去访问内存,会发生访问冲突.

  • 第五条代码:运行结果为19。&arr,这里arr代表的是整个数组,参数是整个数组的地址,从此地址出发,直到找到\0为止,但是我们一开始没有在数组中赋值\0,所以最终得出的是随机值。

  • 第六条代码:运行结果为13.&arr+1代表的是第二个数组的地址。从此出发,后面有没有\0仍然未知,是随机值,但会比第5条代码少5,因为他们之间一定差了一个数组的元素个数

  • 第七条代码:运行结果为18.&arr[0]+1,代表的是第二个元素的地址,从此地址开始访问,因为后面没有设置\0,所以访问的仍然是随机值,但比第一条代码会少1.

  • 分析下面代码2:

#include<stdio.h>
int main()
{
char arr[] = "abcdef";
printf("%zd\n", sizeof(arr));//第一条:7,arr数组名表示的是整个数组计算的是整个数组的大小
printf("%zd\n", sizeof(arr+0));//第二条:4,arr+0代表的是数组首元素的地址,既然是地址,大小就是4个字节
printf("%zd\n", sizeof(*arr));//第三条:1,*arr代表的是数组首元素--a大小为一个字节
printf("%zd\n", sizeof(arr[1]));//第四条:1,arr[1]代表的是数组中第二个元素-b的大小
printf("%zd\n", sizeof(&arr));//第五条:4//&arr代表的是数组的地址
printf("%zd\n", sizeof(&arr+1));//第六条:4//&arr代表的是数组的地址,+1后代表跳过整个数组后,这指向了数组后面的那个位置,但是&arr+1仍然是地址,只要是地址,就是4个字节
printf("%zd\n", sizeof(&arr[0]+1));//第七条:4,代表的是数组第二个元素的地址,只要是地址,就是4个字节
printf("%zd\n", strlen(arr));//第八条:6,数组名代表第一个元素地址
printf("%zd\n", strlen(arr+0));//第九条:6,数组名+0代表第一个元素地址
//	printf("%zd\n", strlen(*arr));//第十条:字符a-97,97作为地址传给了strlen,不能使用这个空间,注释掉
//	printf("%zd\n", strlen(arr[1]));//第十一条:字符b-98,98作为地址传给了strlen,不能访问这个空间,注释掉
printf("%zd\n", strlen(&arr));//第十二条:6,&arr是数组的地址,数组的地址和数组首元素的地址是指向同一个位置的
printf("%zd\n", strlen(&arr+1));//第十三条:随机值
printf("%zd\n", strlen(&arr[0]+1));//第十四条:5,从数组第二个位置开始访问
}

在这里插入图片描述
分析:

  • 第一条代码:运行结果为7.这里arr代表的是整个数组,sizeof(arr)计算的是整个数组的大小,因为一开始赋值的时候为字符串类型,所以末尾自动加了个\0。故而应该是7个字节大小。

  • 第二条代码:运行结果为4.这里arr代表数组首元素的地址+0也代表首元素的地址,只要是地址,在X86环境下,大小就是4个字节

  • 第三条代码:运行结果为1.这里arr代表的是数组首元素的地址,对其进行解引用,我们得到的是字符’a’.一个字符大小是一个字节

  • 第四条代码:运行结果为1。arr[1]代表的是数组第二个元素-‘b’,它是字符,其大小就是一个字节

  • 第五条代码:运行结果为4.&arr代表的是整个数组的地址,只要是地址,在x86环境下,大小就是4个字节。

  • 第六条代码:运行结果为4.%arr+1代表的是下一个数组的地址,只要是地址,在X86的环境下,大小就是4个字节

  • 第七条代码:运行结果为4.&arr[0]+1,代表的是第二个元素的地址,只要是地址,在X86环境下,大小就是4个字节。

  • 第八条代码:运行结果为6.arr代表的是数组首元素的地址,arr数组末尾有\0,从此地址出发,到\0之间共有6个元素,故而显示的结果就是6

  • 第九条代码:运行结果为6.arr+0代表的也是首元素的地址,分析结果同第一条代码。

  • 第十条代码:运行结果为程序错误。arr代表的是数组首元素的地址,对其进行解引用,指的是’a’其ASCII码值为97,将其传给strlen,strlen会认为97就是地址,然后去访问内存,会发生访问冲突

  • 第十一条代码:运行结果为程序错误。arr[1]代表的是字符’b’其ASCII码值为98,将其传给strlen,strlen会认为98就是地址,然后去访问内存,会发生访问冲突

  • 第十二条代码:运行结果为6.&arr是整个数组的地址,而整个数组的地址和首元素地址指向的是同一个位置,故而分析同第一条代码。

  • 第十三条代码:运行结果为随机值。&arr+1代表的是下一个数组的地址,而下一个数组我们没有赋值或者定义\0,故而显示的是随机值。

  • 第十四条代码:运行结果为5.&arr[0]+1代表的是第二个元素’b’的地址,从此地址到\0之间的元素有5个,故而最后显示的结果是5。

  • 分析下面代码3:
    字符串指针:可以理解成不能改变的字符数组

#include<stdio.h>
int main()
{
char* p = "abcdef";//可以理解成不能被改变的字符数组
printf("%zd\n", sizeof(p));//第一条:4,p是指针变量。计算的是指针变量p的大小,为4个字节
printf("%zd\n", sizeof(p+1));//第二条:4,p+1代表的是b元素的地址 为4个字节
printf("%zd\n", sizeof(*p));//第三条:1,代表a元素的大小
printf("%zd\n", sizeof(p[0]));//第四条:p[0]->*(p+0),代表a元素的大小1
printf("%zd\n", sizeof(&p));//第五条:4,p指针的地址,只要是地址就是4个字节
printf("%zd\n", sizeof(&p+1));//第六条:4,&p是p的地址,&p+1是跳过p变量,指向了p的后边,只要是地址就是4个字节
printf("%zd\n", sizeof(&p[0]+1));//第七条:4,代表b的地址,只要是地址就是4个字节
printf("%zd\n", strlen(p));//第八条:6,
printf("%zd\n", strlen(p+1));//第九条:5
//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));//第十四条:5
}

在这里插入图片描述
分析:

  • 第一条代码:运行结果为4.p是指针变量,同时也代表的是字符’a’的地址,在X86环境下指针变量的大小为4字节.
  • 第二条代码:运行结果为4,p+1代表的是‘b’字符的地址,在X86环境下,其大小为4个字节
  • 第三条代码:运行结果为1.*p代表的是’a’字符,其大小为一个字节。
  • 第四条代码:运行结果为1.p[0]等价于* (p+0)指的是’a’字符,其大小为1个字节
  • 第五条代码:运行结果为4.&p代表的是p指针变量的地址,只要是地址,在X86环境下,其大小就是4个字节
  • 第六条代码:运行结果为4.&p代表的是p的地址,&p+1是跳过p变量,指向了p的后边,也代表地址,只要是地址,在X86环境下就是4个字节。
  • 第七条代码:运行结果为4.&p[0]+1代表的是’b’字符的地址,只要地址,在X86环境下就是4个字节
  • 第八条代码:运行结果为6.p代表的是’a’的地址,将其传给strlen函数,就是从此地址出发,统计到\0之前的元素个数,字符串末尾会自动加一个\0,故而结果为6.
  • 第九条代码:运行结果为5.p+1代表的是’b’字符的地址,将其传给strlen函数,就是从此地址出发,统计到\0之前的元素个数,故而会比第八条代码少1,结果为5.
  • 第十条代码:运行结果为程序错误。*p代表的是字符’a’,其ASCII码值为97,将其传给strlen,strlen会认为97就是地址,然后去访问内存,会发生访问冲突符。
  • 第十一条代码:运行结果为程序错误。p[0]代表的也是字符’a’,分析同第十条代码
  • 第十二条代码:运行结果为3.&p代表p变量的地址,,从此地址出发,因为后边我们没有一开始定义\0,所以最后统计的是随机值。
  • 第十三条代码:运行结果为随机值。&p+1代表的是跳过p变量,指向了p的后边,从此地址出发,因为后边我们没有一开始定义\0,所以最后统计的是随机值。
  • 第十四条代码:运行结果为5.&p[0]+1,代表的是’b’字符的地址,从此地址出发,到\0共有5个元素,故而结果为5.

以上的每一条代码深刻领会后,就会感受到字符数组的细节内容。像对字符数组数组名的理解,字符数组中解引用的理解,字符数组的地址的理解,字符数组中的运算操作,字符指针变量与字符数组关系等等。

(三)、二维数组

  • 分析以下代码:
#include<stdio.h>
int main()
{
int a[3][4] = { 0 };
printf("%zd\n", sizeof(a));//第一条:48,a作为二维数组数组名,单独放在sizeof中,说明这里a代表整个数组,计算的是整个数组的大小,单位是字节
printf("%zd\n", sizeof(a[0][0]));//第二条:4
printf("%zd\n", sizeof(a[0]));//第三条:16
printf("%zd\n", sizeof(a[0]+1));//第四条:4
printf("%zd\n", sizeof(*(a[0]+1)));//第五条:4,代表第一行第二个元素,大小是4个字节
//************
printf("%zd\n", sizeof(a+1));//第六条:4,代表第二行第一个元素的地址。a是二维数组数组名,并没有单独放在sizeof中,a代表的是首元素地址即第一行的地址,a+1代表第二行的地址,是地址大小就是4个字节
//************
printf("%zd\n", sizeof(*(a + 1)));//第七条:16
//1.*(a+1)--a[1]-是第二行的数组名,单独放到sizeof中,指的是整个数组的大小
//2.第一行地址+1,即第二行的地址,为数组指针类型,对其进行解引用,访问的是整个第二行数组,大小是16个字节
printf("%zd\n", sizeof(&a[0]+1));//第八条:4,代表的是第二行的地址
//a[0]表示的是第一行的数组名,&数组名代表的是第一行的地址,&a[0]+1就是第二行的地址,是地址就是4个字节
printf("%zd\n", sizeof(*(&a[0] + 1)));//第九条:16;第二行地址解引用代表第二行整个数组
printf("%zd\n", sizeof(*a));//第十条:16.a代表的是第一行的地址,对第一行进行解引用代表的是第一行整个数组的大小,大小为16个字节
printf("%zd\n", sizeof(a[3]));//16第十一条:
//sizeof 内部的表达式是不会真实计算的,所以谈不上越界的问题
//a[3]-第四行的数组名,sizeof直接+数组名,代表访问整个数组(即第四行)大小为16个字节
}

在这里插入图片描述
分析:

  • 第一条代码:运行结果为48.a作为二维数组的数组名,单独放在sizeof中代表了整个二维数组,共有12个元素,一个整型元素为4个字节。所以共占48个字节大小。
  • 第二条代码:运行结果为4.a[0][0]代表的是第一行第一列元素,其为整型类型,故而大小为4个字节。
  • 第三条代码:运行结果为16.a[0],是第一行的数组名,单独放在sizeof中,所以a[0]代表的是二维数组中第一行的一维数组,共有4个整型元素,故而占16个字节
  • 第四条代码:运行结果为4.a[0]是第一行的数组名,在这里它代表的是第一行首元素的地址,然后+1代表第一行第二个元素的地址,只要是地址,在X86环境下,大小为4个字节
  • 第五条代码:运行结果为4.a[0]+1代表的是第一行第二个元素的地址,然后对其进行解引用,得到的是整型0元素,大小为4个字节。
  • 第六条代码:运行结果为4.a是指二维数组数组名,在这里代表的是第一行一维数组的地址,+1后,代表的是第二行一维数组的地址,只要是地址,在X86环境下就是4个字节。
  • 第七条代码:运行结果为16.有两种解释:第一种,(a+1)代表的是第二行一维数组的地址,然后对其进行解引用,代表的是整个第二行一维数组,共有4个整型元素,故而占16个字节;第二种,*(a+1)等价于a[1]是第二行的数组名,单独放在sizeof中代表的是整个第二行一维数组,共有4个整型元素,故而占16个字节。
  • 第八条代码:运行结果为4.&a[0]代表的是第一行一维数组的地址,加一后,代表的是第二行一维数组的地址,只要是地址,在X86环境下,就是4个字节。
  • 第九条代码:运行结果为16.&a[0]+1代表第二行一维数组的地址,对其进行解引用后,代表的是整个整个第一行,共有4个整型元素,故而占16个字节。
  • 第十条代码:运行结果为16.a是二维数组的数组名,在这里代表的是第一行的地址,对第一行进行解引用代表的是第一行整个数组的大小,大小为16个字节
  • 第十一条代码:运行结果为16.sizeof内部是不会真实计算的,所以谈不上越界问题。a[3]-第四行的数组名,sizeof直接+数组名,代表访问整个数组(即第四行)大小为16个字节

以上的每一条代码深刻领会后,就会感受到二维数组的细节内容。像对二维数组数组名的理解,将二维数组拆分成几个一维数组,它是几个一维数组的数组,二维数组地址的分析,解引用分析,运算等等。

三、复杂指针运算讲解

  • 代码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后,赋值给ptr指针,ptr就是简单的整型指针,-1后代表整型元素5的地址,进行解引用后,代表整型元素5.(a+1)等价于a[1],即2整型元素

  • 代码2:
//在X86环境下
//结构体的大小为20个字节
#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;}

运行结果为:
在这里插入图片描述
分析:%p打印的是地址。我们应该清楚,指针+1是跟类型有关系,而整数+1,就是之间+1.第一条打印代码为指针+1(16进制),struct Test*变量为20个字节,即加20个字节,因为是16进制,所以最终呈现的是00100014;第二条打印代码,为整数加1,直接加1即可,最终呈现为00100001。第三条打印代码仍然为指针+1,unsigned int类型变量是4个字节,所以最终呈现00100004

  • 代码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;}

运行结果:
在这里插入图片描述
分析:注意在给二维数组赋值的时候用的是小括号,即为逗号表达式形式,等价于int a[3][2]={1,3,5};即为以下形式
在这里插入图片描述
a[0]是第一行一维数组的数组名,代表第一行首元素的地址,将它赋值给p整型指针变量,p[0]等价于*(p+0)访问的是第一行的首元素1,故而输出为一。

  • 代码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;}

运行结果为:
在这里插入图片描述
分析:p是数组指针类型,int(*)[4],而a也可以表示成数组指针的形式,它的类型就是int( * )[5],但是将a赋值为p,所以最终以p的类型为主,一开始a的地址与p的地址指向统一个位置,但是它俩类型不同,所以+1后跳过的元素也不同,p+1跳过4个元素,而a+1跳过5个元素.所以最终结果指向为:
在这里插入图片描述
%d是有符号整数,结果就是-4.
%p是打印的地址,为无符号整数,最终是以补码形式存在
-4的补码为:11111111111111111111111111111100,它所对应的无符号整数就是FF FF FF FC

  • 代码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;}

运行结果为:
在这里插入图片描述
分析:
在这里插入图片描述
&aa代表整个二维数组的地址,+1后指向下一个二维数组,如图所示,然后强制转换成int型,-1后指向10整型元素,对其解引用后,打印出10;aa+1代表的是第二行一维数组的地址,对其解引用后代表整个第二行数组名,指的是第二行一维数组首元素的地址,然后强制类型转换成int型,-1后指向5元素地址,最后解引用后,指向元素5.

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

运行结果为:
在这里插入图片描述

分析:
在这里插入图片描述
a是二维数组数组名,代表的是二维数组首元素地址(即第一行一维数组的地址),这里用二级指针pa来接受,p++后,指向的二维数组第二行的地址(即第二行一维数组的地址),打印后即第二行的内容,即at.

  • 代码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;}

运行结果:
在这里插入图片描述
分析:
在这里插入图片描述
c 为指针数组类型,cp亦是指针数组类型,char为三级指针,代表的是cp首元素的地址,他们之间的对应关系如图。
注意++,–运算会改变cpp的值,影响后面计算
第一条打印代码分析:**++cpp,cpp+1是c+2的地址,进行解引用后代表c+2,在对c+2进行解引用后代表P的地址,然后进行打印后结果为POINT
第二条打印代码分析:* – ++cpp+3,前面cpp已经++了,现在cpp指向的是c+2的地址,加法的优先级比较低,++ – 的优先级比较高,顺序就是先算加加再解引用,再算–,再解引用,最后算+3.cpp先++,代表的是c+1的地址,进行解引用后代表c+1然后–,代表的是c,解引用后 c代表的是"ENTER"的首元素‘E’的地址,然后加3代表的是后面’E’的地址,然后打印出来的结果就是ER。
第三条打印代码分析:cpp[-2]+3。 cpp[-2]等价于
(cpp-2),cpp现在是代表c+1的地址,-2后代表c+3的地址,进行解引用后代表c+3,c+3,进行解引用 * (c+3)代表的是”FIRST“的首元素‘F’地址,然后+3代表的是‘S’的地址,最后打印出来的结果就是ST
第四条打印代码分析:cpp[-1][-1] + 1。cpp现在代表的是c+1的地址,cpp[-1][-1]等价于
(* (cpp-1)-1),即代表“NEW”首元素’N’的地址,然后+1即‘W’的地址,最后打印出来的结果就是EW。

总结

本文主要介绍strlen与sizeof的区别,利于strlen与sizeof来对比一维数组,字符数组以及二维数组,加深理解数组之间的区别与联系,最后讲解几个复杂指针的运算,如有错误请批评指正,感谢支持

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

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

相关文章

Value-Based Reinforcement Learning(2)

Temporal Difference &#xff08;TD&#xff09; Learning 上节已经提到了如果我们有DQN&#xff0c;那么agent就知道每一步动作如何做了&#xff0c;那么DQN如何训练那&#xff1f;这里面使用TD算法。 简略分析&#xff1a; 是的估计 是的估计 所以&#xff1a; Deep Re…

对vue3/core源码ref.ts文件API的认识过程

对toRef()API的认识的过程: 最开始认识toRef()是从vue3源码中的ref.ts看见的,右侧GPT已经举了例子 然后根据例子,在控制台输出ref对象是什么样子的: 这就是ref对象了,我们根据对象中有没有__v_isRef来判断是不是一个ref对象,当对象存在且__v_isRef true的时候他就判定为是一个…

Linux-组管理和权限管理

1 Liunx组的基本介绍&#xff1a; 在Linux中的每个用户必须属于一个组&#xff0c;不能独立于组外。在Linux中每个文件都有所有者、所在组、其他组的概念 所有者所在组其它组改变用户所在的组 2 文件/目录的所有者 一般文件的创建者&#xff0c;谁创建了该文件&#xff0c;就…

从程序被SQL注入来MyBatis 再谈 #{} 与 ${} 的区别

缘由 最近在的一个项目上面&#xff0c;发现有人在给我搞 SQL 注入&#xff0c;我真的想说我那么点资源测试用的阿里云服务器&#xff0c;个人估计哈&#xff0c;估计能抗住他的请求。狗头.png 系统上面的截图 数据库截图 说句实在的&#xff0c;看到这个之后我立马就是在…

游戏找不到d3dcompiler_43.dll怎么办,教你5种可靠的修复方法

在电脑使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“找不到d3dcompiler43.dll”。这个问题通常出现在游戏或者图形处理软件中&#xff0c;它会导致程序无法正常运行。为了解决这个问题&#xff0c;我经过多次尝试和总结&#xff0c;找到了以下五…

idea2023的git从dev分支合并到主分支master

1.本地项目切换到主分支master 右键项目-git-Branches 依次点击项目-Remote-Origin-master-CheckOut 现在你的idea中的这个项目就是远程master分支的代码了。 2.合并dev分支到master 右击项目-git-Merge 选择origin-dev 点击Merge按钮&#xff0c;此时只是合并到本地的maste…

每日一题---有效的括号问题

文章目录 前言1.题目以及分析2.参考代码 前言 前面我们学习了栈的相关操作&#xff0c;现在我们做一道题&#xff0c;进行巩固 Leetcode—有效的括号 1.题目以及分析 这道题就可以使用栈进行操作&#xff0c;因为把最左边的括号当成栈底&#xff0c;最右边的是栈顶&#xff0c…

【每日刷题】Day49

【每日刷题】Day49 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 110. 平衡二叉树 - 力扣&#xff08;LeetCode&#xff09; 2. 501. 二叉搜索树中的众数 - 力扣&…

基于YOLOv8的车牌检测与识别(CCPD2020数据集)

前言 本篇博客主要记录在autodl服务器中基于yolov8实现车牌检测与识别&#xff0c;以下记录实现全过程~ yolov8源码&#xff1a;GitHub - ultralytics/ultralytics: NEW - YOLOv8 &#x1f680; in PyTorch > ONNX > OpenVINO > CoreML > TFLite 一、环境配置 …

27【Aseprite 作图】盆栽——拆解

1 橘子画法拆解 (1)浅色3 1 0;深色0 2 3 就可以构成一个橘子 (2)浅色 2 1;深色1 0 (小个橘子) (3)浅色 2 1 0;深色1 2 3 2 树根部分 (1)底部画一条横线 (2)上一行 左空2 右空1 【代表底部重心先在右】 (3)再上一行,左空1,右空1 (4)再上一行,左突出1,…

省市区(输入code) 转相应省市区工具类(两种方式)

方式一 通过调用接口&#xff08;时间高达1s&#xff09; package cn.iocoder.yudao.module.supplier.utils;import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element;import java.io.BufferedReader; import java.io.InputStreamReader; i…

Java 泛型基础

目录 1. 为什么使用泛型 2. 泛型的使用方式 2.1. 泛型类 2.2. 泛型接口 2.3. 泛型方法 3. 泛型涉及的符号 3.1. 类型通配符"?" 3.2. 占位符 T/K/V/E 3.3. 占位符T和通配符&#xff1f;的区别。 4. 泛型不变性 5. 泛型编译时擦除 1. 为什么使用泛型 Java 为…

基于深度学习的入侵检测系统综述文献概述

好长时间不发博客了&#xff0c;不是因为我摆烂了&#xff0c;是我换研究方向了&#xff0c;以后我就要搞科研了。使用博客记录我的科研故事&#xff0c;邀诸君共同见证我的科研之路。 1、研究方向的背景是什么&#xff1f; &#xff08;1&#xff09;互联网发展迅速&#xff…

基于ssm的蛋糕商城系统java项目jsp项目javaweb

文章目录 蛋糕商城系统一、项目演示二、项目介绍三、系统部分功能截图四、部分代码展示五、底部获取项目源码&#xff08;9.9&#xffe5;带走&#xff09; 蛋糕商城系统 一、项目演示 蛋糕商城管理系统 二、项目介绍 系统角色 : 管理员、用户 一&#xff0c;管理员 管理员有…

Mixed-precision计算原理(FP32+FP16)

原文&#xff1a; https://lightning.ai/pages/community/tutorial/accelerating-large-language-models-with-mixed-precision-techniques/ This approach allows for efficient training while maintaining the accuracy and stability of the neural network. In more det…

【排序算法】选择排序以及需要注意的问题

选择排序的基本思想&#xff1a;每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;存放在序列的起始位置&#xff0c;直到全部待排序的数据元素排完 。 第一种实现方法&#xff1a; void SelectSort(int* arr, int n) {for (int j 0…

【kubernetes】探索k8s集群中金丝雀发布后续 + 声明式资源管理yaml

目录 一、K8S常见的发布方式 1.1蓝绿发布 1.2灰度发布&#xff08;金丝雀发布&#xff09; 1.3滚动发布 二、金丝雀发布 三、声明式管理方法 3.1YAML 语法格式 3.1.1查看 api 资源版本标签 3.1.2查看资源简写 3.2YAML文件详解 3.2.1Deployment.yaml 3.2.2Pod.yaml …

C++系列-C/C++内存管理方式

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” C/C内存分布 在这篇文章开始之前&#xff0c;我们先以一道题目来进行引入&#xff1a; int glovalvar 1; static int staticGlovalvar 1; void Test() {static int staticva…

Java进阶学习笔记27——StringBuilder、StringBuffer

StringBuilder&#xff1a; StringBuilder代表可变字符串对象&#xff0c;相当于一个容器&#xff0c;它里面装的字符串是可以改变的&#xff0c;就是用来操作字符串的。 好处&#xff1a; StringBuilder比String更适合做字符串的修改操作&#xff0c;效率会更高&#xff0c;…

在CSDN上成长的感悟,你的粉丝长啥样?

文章目录 一、写作的初衷1. 记录所学内容2.巩固所学知识3.分享与帮助4.方便后续查找5.获取激励 二、你的粉丝长啥样&#xff1f;1. 粉丝的特点与困惑2. 关于粉丝&#xff0c;细思极恐 三、继续前行、坚持初心 在CSDN上写博文&#xff0c;对于我来说&#xff0c;不仅仅是一个记录…