指针进阶(3) -- 关于sizeof和strlen的详细总结

目录

1.知识准备

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

整型数组的sizeof

int a[]={1,2,3,4}的sizeof的使用

字符数组的sizeof和strlen

char arr[]={'a','b','c','d','e','f'}的sizeof的使用

char arr[]={'a','b','c','d','e','f'}的strlen的使用

字符串的sizeof和strlen

char arr="abcdef"的sizeof的使用

char arr="abcdef"的strlen的使用

指针的sizeof的使用

char*p的sizeof的使用

char*p的strlen的使用

二维数组的sizeof

int a[3][4] 的sizeof的使用

3.指针笔试题

笔试题1

笔试题2


1.知识准备

数组和指针的定义:

数组和指针

数组 - 能够存放一组相同类型的元素,数组的大小取决于数组的元素个数和元素类型

指针 - 地址/指针变量,大小是4/8个

数组和指针的关系:

数组是数组,指针是指针,二者不等价
1.数组名是数组首元素的地址,这个地址就可以存放在指针变量中
2.我们就可以使用指针变量来遍历数组

🚀数组名的注意事项:

1.通常情况:

大部分情况下数组名是数组首元素的地址 

2.例外

1.sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小

2.&数组名 - 数组名表示整个数组,取出的是数组的地址

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

注意:编译环境是x86环境!,sizeof是一个操作符,和+- * /类似

整型数组的sizeof

int a[]={1,2,3,4}的sizeof的使用

#include<stdio.h>
int main(){
int a[] = {1,2,3,4};
    printf("%d\n",sizeof(a));//1
    printf("%d\n",sizeof(a+0));//2
    printf("%d\n",sizeof(*a));//3
    printf("%d\n",sizeof(a+1));//4
    printf("%d\n",sizeof(a[1]));//5
    printf("%d\n",sizeof(&a));//6
    printf("%d\n",sizeof(*&a));//7
    printf("%d\n",sizeof(&a+1));//8
    printf("%d\n",sizeof(&a[0]));//9
    printf("%d\n",sizeof(&a[0]+1))//10
    return 0;
}

接下来我将尽力解释每一行的含义 

1.sizeof(a)

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

数组4个元素,每个元素是int类型,一个int类型4个字节,   4*4=16 byte

sizeof(a)就是数组名单独放在sizeof内部,计算的是数组的总大小, 单位是字节

运行结果:

2.sizeof(a + 0)

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

a+0,数组名(a)没有单独放在sizeof内部,这里属于参与运算变成地址了

              ||

a (首元素地址) + 0 == a(数组第一个元素地址) 这里并没有跳过任何距离,+0等于没加

所以大小为4个字节

运行结果:

 

3.sizeof(*a)

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

a是数组首元素的地址 --> &a[0]

*a -> *&a[0] -> a[0]  ==> * 和 & 可以互相抵消

个人理解:

因为a是数组首元素的地址,可以写成&a[0],即*a==*&a[0],所以*a可以表示为: *&a[0]

整体思想:

&a[0]是取出这个元素所在的内存空间的对应唯一的地址,而 *地址,是对地址解引用,找到那块空间对应的元素,而数组a的类型是int,所以它每个元素的数据类型也是int,所占内存空间字节是4 byte ,所以打印的结果为4

运行结果:

 

4.sizeof(a + 1)

a是数组首元素的地址 -- int* ,a+1 跳过一个整型,是第二个元素的地址

运行结果:

 

5.sizeof(a[1])

a[1]表示数组第二个元素,一个int大小就是4/8个字节

运行结果:

 

6.sizeof(&a)

 &a - 取出的是数组的地址,但是数组的地址也是地址,是地址大小就是4/8个字节

&a存起来:

int (*pa)[4]= &a --> 去掉pa,该指针类型为int (*)[4]

运行结果:

 

7.sizeof(*&a)

* 和 & 可以互相抵消,所以*&a==a,等于没有操作

个人理解:当对指针解引用的时候,访问几个字节取决于指针的类型

当是一个整型指针的时候,解引用访问的是一个整型,

当是一个字符指针的时候,解引用访问的是一个字符,

当是一个数组指针的时候,解引用访问的是一个数组,a[4],其类型是int,有4个元素,所以大小为16byte

运行结果:

 

8. sizeof(&a + 1)

优先级是靠近的先算&a,先取地址,再解引用,跟优先级无关

区别:

数组名取地址取到的是整个数组的地址,从值上来看整个数组的地址和数组首元素地址的值相同,区别就是数组首元素地址+1时,跳过一个元素,整个数组地址+1时,跳过整个数组

&a+1 地址跳过一个数组,还是指向内存里面的,是地址就是4/8个byte

&a[0]是取出数组首元素地址 <==> &a是数组的地址

注意:

&a[0]和a(数组名)是等价的

运行结果:

 

图解:

9.sizeof(&a[0] )

数组第一个元素的地址

运行结果:

 

 10.sizeof(&a[0] + 1)

数组第二个元素的地址

运行结果:

 

字符数组的sizeof和strlen

char arr[]={'a','b','c','d','e','f'}的sizeof的使用

预备知识:

1.sizeof计算的是占用内存空间的大小,单位是字节,不关注内存中到底存放的是什么

2.sizeof不是函数,是操作符

3.strlen是函数

4.strlen是针对字符串的,求的是字符串的长度,本质上是统计 \0 之前出现的字符个数

//字符数组
#include<string.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f'};
	printf("%d\n", sizeof(arr));//6
	printf("%d\n", sizeof(arr + 0));//arr+0是数组首元素地址
	printf("%d\n", sizeof(*arr));//*arr是首元素的,计算的是首元素的大小
	printf("%d\n", sizeof(arr[1]));//1
	printf("%d\n", sizeof(&arr));//&arr是数组的地址 4/8
	printf("%d\n", sizeof(&arr + 1));//&arr + 1跳过一个数组后的地址 4/8
	printf("%d\n", sizeof(&arr[0] + 1));//4/8 第二个元素的地址
	return 0;
}

1.sizeof(arr)

数组名单独放在sizeof()内部,是数组的地址,这个数组是没有放'\0'的,所以仅仅只有6个元素,所以计算的是整个数组的大小,为6byte

运行结果:

2.sizeof(arr + 0)

数组名arr,没有单独放在sizeof()内部,arr+0就是数组首元素地址是地址字节大小就是4/8byte

运行结果:

3.sizeof(*arr)

*arr是首元素,计算的是首元素的大小,而一个char类型大小就1byte

运行结果:

4.sizeof(arr[1])

char类型数组第二个元素,字节大小是1byte

运行结果:

 

5.sizeof(&arr)

&arr,是取出char a数组的地址,是地址大小就是4/8byte

运行结果:

 

 6.sizeof(&arr + 1)

&arr + 1跳过一个数组后的地址,字节大小是4/8

运行结果:

 

7.sizeof(&arr[0] + 1)

字符数组第二个元素的地址,字节大小是4/8

运行结果:

 

char arr[]={'a','b','c','d','e','f'}的strlen的使用

#include<string.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f'};

	printf("%d\n", strlen(arr));//随机值,因为不知道\0的位置
	printf("%d\n", strlen(arr + 0));//随机值
	printf("%d\n", strlen(*arr));//非法访问
	printf("%d\n", strlen(arr[1]));//'b' - 98当成地址,形参非法访问
	printf("%d\n", strlen(&arr));//随机值
	printf("%d\n", strlen(&arr + 1));//随机值 - 6 
	printf("%d\n", strlen(&arr[0] + 1));//随机值 - 1

    return 0;
}

1.strlen(arr)

数组名没有单独放在sizeof内部,因为这是strlen,这时就从第一个元素'a'这个地址开始统计,直到遇到'\0'的地址停止,但是这个数组没有存'\0',所以'\0'的地址在内存中的位置是随机存放的,所以结果是随机值

运行结果:

2.strlen(arr + 0)

没有取值地址,没有单独放在sizeof内部,+0还是指向'\a'位置,所以结果还是随机值

运行结果:

 

3.strlen(*arr)

*arr得到了首元素'a',对应的ascll码值是 -- 97,是整型类型,而strlen的参数必须是 char*  类型,

所以就是非法访问了

运行结果:

4.strlen(arr[1])

数组第二个元素,也就是'b',其对应的ascll码值为98,非法访问

运行结果:

 

关于1和2点以下图解:

strlen的参数必须是 char*  类型 

 5.strlen(&arr)

 数组的地址,跟1和2点的值是一样的,随机值

运行结果:

 

6.strlen(&arr + 1)

随机值- 6

运行结果:

 

7.strlen(&arr[0] + 1)

随机值 -  1

运行结果:

 

字符串的sizeof和strlen

char arr="abcdef"的sizeof的使用

前提:

sizeof会根据数据类型进行计算

特殊:

'\0'这个放进去数组里面,如果是字符数组,那它的字节大小就是1,如果是整型数组那它的字节大小就是4

也就是说,无论里面放什么类型的数据,sizeof的统计是按数组的大小去统计的,不关心数据类型

图解:

int main()
{
  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));

 return 0;

}

1.sizeof(arr)

统计结果整个数组大小:7

运行结果:

 

 

2.sizeof(arr + 0)

arr+0是首元素地址,是地址大小为4/8

运行结果:

 

3.sizeof(*arr)

字符数组第一个元素大小:1

arr[0] ==*arr== *(arr+0)

计算数组大小另一种方式:

总数/首元素:  int sz= sizeof(arr)/sizeof(*arr) <==> sizeof(arr)/sizeof(arr[0])

运行结果:

 

4. sizeof(arr[1])

字符数组第二个元素大小:1

运行结果:

 

 

5.sizeof(&arr)

数组的地址,是地址就是4/8byte

运行结果:

 

6.sizeof(&arr + 1)

跳过一个数组,依然是地址,大小4/8byte

运行结果:

 

7. sizeof(&arr[0] + 1)

字符数组第二个元素地址,4/8字节

运行结果:

sizeof(字符串) 与 sizeof ( 数组 )的区别

区别:字符串比字符数组存储时要多一个'\0'

sizeof(字符串):

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));

双引号会在尾部自动添加转义字符'\0',即数据0X00,所以结果是7

双引号作用: (1)字符串尾部加0, (2)开辟内存空间, (3)提取地址

sizeof ( 数组 ): 

 返回数组的大小,因为arr1[]没有写入数字,所以就按照字符个数统计

下面是[]写入数据的情况:

sizeof ( 数组 ):

char arr[50] = { 'a','b','c','d','e','f' };
printf("%ld", sizeof(arr));

因为数组的下标长度是50,所以返回数组总字节大小

char arr="abcdef"的strlen的使用

int main(){
   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));

return 0;
}

1.strlen(arr)

数组名没有单独放在sizeof内部,这是strlen,所以表示为数组首元素地址

运行结果:

 

2.strlen(arr + 0)

没有距离上的跨越,依旧是数组首元素地址,

运行结果:

 

3.strlen(*arr)

数组第一个元素'a',strlen需要的是地址,错误

运行结果:

4.strlen(arr[1])

数组第二个元素'b',strlen需要的是地址,错误

运行结果:

5.strlen(&arr)

会报出警告:这里的&arr取出的地址所用的指针类型是 char(*)[7]

然而strlen的参数类型是const char*

不影响使用,strlen会转化成它所适配的类型

运行结果:

6.strlen(&arr + 1)

跳过了一个数组,注意是连'\0'也跳过了,那就是随机值了

运行结果:

7.strlen(&arr[0] + 1) 

看上图,数组第二个元素地址,向后数到'\0',5个数

运行结果:

 

指针的sizeof的使用

char*p的sizeof的使用

把*p当成数组来理解即可,跟数组名是一个道理

int main()
 {
   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));
  
   
  return 0;
}

1.sizeof(p)

p是指向字符串首字符'a'的,是用来存放地址的,但是地址本身是不占用内存的,p这个字符指针变量把'a'的地址存放起来了,那么创建一个变量就要开辟一块内存空间,但是指针变量的大小就是4/8 byte

运行结果:

 

2.sizeof(p + 1) 

char*的指针+1跳过一个字符,也就是'b'的地址,还是一个指针变量,大小还是4/8 byte 

运行结果:

 

3.sizeof(*p)

指针p的类型是char*,对p解引用访问1个字符,字符'a'的大小是1byte

运行结果:

 

4. sizeof(p[0])

p[0] --> *(p+0) --‘a’ ,大小1byte

运行结果:

 

5.sizeof(&p) 

但凡在内存中开辟空间就有地址,跟字符串是没有关系的,p的类型是char*,所以&p的类型是二级指针

运行结果:

6.sizeof(&p + 1) 

&p+1为什么跳过一个p:

图解:

运行结果:

 

7.sizeof(&p[0] + 1) 

p[0]是'a',&p[0]那就是'a'的地址,&p[0]+1,那就是'b'的地址,指向'b'的指针就是4/8byte

运行结果:

char*p的strlen的使用

int main()
{

  char* p = "abcdef";

	printf("%d\n", strlen(p));//6
	printf("%d\n", strlen(p + 1));//p+1是'b'的地址 5 
	printf("%d\n", strlen(*p));//err
	printf("%d\n", strlen(p[0]));//err
	printf("%d\n", strlen(&p));//随机值
	printf("%d\n", strlen(&p + 1));//随机值
	printf("%d\n", strlen(&p[0] + 1));

	return 0;
}

1.strlen(p)

从'a'开始数,到'\0',6个字符

运行结果:

 

2.strlen(p + 1)

从'b'开始数,到'\0',5个字符

运行结果:

 

3.strlen(*p)

解引用的结果--‘a’,ascll码值是97,非法访问,err

运行结果:

4.strlen(p[0])

和3是一样的结果,err

运行结果:

5.strlen(&p)

 共同点:

不管是p指向的字符串地址,还是&p的这个地址,传入strlen 都是计算长度的,计算长度的结束标志就是遇到\0

关于这里我要详细说明一下p和&p的区别:

传入p后, 从字符串首元素地址开始向后找  能找到\0  因为字符串就是以\0结尾的

例如:

传入p能确定的原因就是 "abcdef\0" 这个是顺序存储的, p的地址就是字符a的地址 ,从a开始向后找\0 是能找到的

而传入&p这个地址,每次执行程序都是不确定的,内存中数据的存储也是不确定的,所以\0的位置也是不确定的

可以看到三次的执行结果都不一样的:

三次&p的结果都是不同的,所以从&p这个地址向后寻找\0也是不确定的

虽然p的地址  执行三次也可能不同,但是它后面的\0位置是确定的

然而关于'\0'后面还有数据我的理解是:

并不是说\0之后就不存数据了,内存中肯定是有数据的,但是strlen关心的是首次出现\0的位置在哪里,计算的是这个长度

运行结果:

6.strlen(&p + 1)

 跳过了一个p变量,也是随机值

运行结果:

 

注意:strlen(&p + 1)和strlen(&p),不存在大小问题,还是随机的

7.strlen(&p[0] + 1) 

字符串第二个元素,向后数,5个字符

运行结果:

 

二维数组的sizeof

int a[3][4] 的sizeof的使用

#include<stdio.h>
int main()
{

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

return 0;
}

数组在内存中是连续存放的,这种二维数组题其实脑子要清晰一点,什么都是相对的,无论是sizeof还是strlen,其实只要记住:它们就计算两样东西:元素和地址(针对上面的题),第一时间先弄明白它到底是元素还是地址,二者选一,题就会做出来了。

图解:

  1.sizeof(a)

a这个二维数组的数组名单独放在sizeof内部,计算整个二维数组的大小

运行结果:

2.sizeof(a[0][0])

二维数组第一行第一个元素,4个字节

运行结果:

3.sizeof(a[0]) 

二维第一行的数组名,参照上图arr1,这时数组名单独放在sizeof内部了,就是计算数组第一行的大小

运行结果:

4.sizeof(a[0] + 1)

a[0]不是单独放在sizeof内部

a[0]表示的首元素的地址,即第一行第一个元素的地址 - &a[0][0]

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

个人理解:把a[0]理解成上图arr1,那么就算数组arr1的第一个元素的地址,也就是二维数组第一行第一个元素地址,如果arr1+1,那就是第一行第二个元素地址,是地址那大小为4/8 byte

运行结果: 

5.sizeof(*(a[0] + 1)) 

二维数组第一行第二个元素地址解引用,那就是二维数组第一行第二个元素,大小4byte

运行结果: 

6.sizeof(a + 1) 

a作为二维数组的数组名并非单独放在sizeof内部,所以表示首元素的地址

二维数组的首元素是第一行,这里的a就是第一行的地址---  int (*)[4]

a+1是跳过第一行,指向了第二行

由上图得

那就是二维数组数组名没有单独放在sizeof内部,此时表示二维数组第一个行的地址,也就是arr1(a[0]),那么arr1+1(a[0]+1),变成arr2(a[1]),也就是二维数组第二行的地址,是地址大小为4/8 byte

 运行结果:

7.sizeof(*(a + 1)) 

由6得此时是二维数组第二行的地址解引用,也就是第二行所有元素,也就是*(a[1])

*(a+1)-->a[1],计算第二行元素,那么结果为16byte

运行结果: 

8.sizeof(&a[0] + 1)

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

个人理解:

由图:

&a[0]是二维数组第一行的数组名,数组名表示首元素地址,也就算二维数组第一行的地址,

类比为&arr1,即为整个数组的地址,那么&arr1+1是跳过第一个数组,也就是arr2(二维数组第二行地址)

可以写成:&a[1]

运行结果: 

9.sizeof(*(&a[0] + 1))

由8得,*&a[1]-->a[1]-->二维数组第二行元素

运行结果: 

10.sizeof(*a) 

*a - 就是第一行元素

运行结果:

 

11.sizeof(a[3]) 

这里是下标是存在越界问题,但是sizeof还是可以正常计算,也就是二维数组的第4行(下标:3)

 数组名单独放在sizeof内部,计算的是第4行的大小,为16 byte

为解释此问题,举一个例子:

#include<stdio.h>
int main()
{
    int a = 5;
	short s = 11;
	printf("%d\n", sizeof(s = a + 2));//2
	printf("%d\n", s);//11

	return 0;
}

 s=a+2,a和2类型都是int,但是赋值的时候s是short类型,那么结果的类型就是short,表达式就不计算了,直接看由s的类型算出2byte,由于表达式没有运算所以值是不会改变的,答案依旧为11

无论是值还是表达式都有值属性和类型属性,像int a=10 , a+3

a+3的值属性是10,a是int型,3是int型,类型属性是int,其实无论a+3怎么样都是要看它赋给什么类型,要是double型,那sizeof就是8

所以回到题目,sizeof(a[3])在编译期间就完成了运算,直接由类型判断算出值

运行结果: 

3.指针笔试题

笔试题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

//由于还没学习结构体,这里告知结构体的大小是20个字节
struct Test
{
 int Num;
 char *pcName;
 short sDate;
 char cha[2];
 short sBa[4];
}*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;
}

16进制,10进制,8进制,2进制只是数值的表示形式而已,

就像 f  15  17  1111表达的形式的值都为15

 图解:

代码运行: 

 接下来还有不少笔试题,还待继续。欢迎大佬补充!

  • 24
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 30
    评论
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dream_Chaser~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值