strlen与sizeof的区别及搞明白这些指针笔试题让你不在恐惧指针

目录

sizeof与strlen的区别

sizeof

strlen

sizeof与strlen的区别

数组名的意义


首先我们在做一些指针的笔试题,需要一些储备知识,第一就是strlen和sizeof区别,好多人到现在可能都不太明白今天就让你理解,第二就是数组名的含义。

sizeof与strlen的区别

  • sizeof

sizeof主要用于计算数据类型及数组的所占空间大小。只关注空间大小。单位是字节。

  • strlen

strlen主要计算的是字符串长度,遇到\0就停止,这里是从字符串起始地址开始计算一直到\0就停止。

  • sizeof与strlen的区别

sizeof用于计算类型大小,或者数组大小,这里的大小指的是所占空间大小strlen用于计算字符串长度,遇到\0截止  。char str[100] = "abc";注意这里\0不计算大小,计算的是\0前面的大小。

sizeof(str) 就是100

strlen(str) 就是3

当然sizeof的用处更多是为了计算类型大小

int a = 10;            sizeof(a) 等价于 sizeof(int); 其他类型也一样,包括自定义类型

但是要注意 sizeof(&a) 求的是指针的大小,跟指向类型没关系,指针大小不管指向什么类型大小都一样

sizeof是操作符,而strlen是库函数

数组名的意义

两个特例:sizeof(arr),sizeof括号里单独放了个数组名计算的是整个数组的大小。

&arr,&数组名代表 取的是整个数组的地址。

除了这两种情况外,都是指数组首元素地址。


  1. int a[ ] = {1,2,3,4};
  • printf("%d\n",sizeof(a));
    sizeof(a),sizeof(数组名)代表求整个数组的大小所以为4*4=16个字节;
  • printf("%d\n",sizeof(a+0));

    sizeof(a+0)a表示数组首元素地址,计算的是地址的大小,是地址大小就是4/8个字节

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

    sizeof(*a) ,a没有单独放在sizeof内部,也没有取地址数组名,所以a这里是数组首元素地址,*a,是对a解引用,找到的是第一个元素,第一个元素为整型所以大小就为4个字节。

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

    sizeof(a+1),a没有单独放在sizeof内部,也没有取地址数组名,所以a这里是数组首元素地址,a+1就是数组第二个元素的地址,只要是地址大小就为4/8个字节。

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

    sizeof(a[1])这里求的是下标为1的元素的大小,为整型所以大小为4个字节

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

    sizeof(&a)&数组名取出的是整个数组的地址,但也是地址,只要是地址,大小就为4/8.

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

    sizeof(*&a)这里可以理解为*与&互相抵消,所以这里是sizeof(数组名),是求整个数组的大小,为4*4=16;这里还可以为&a是取出整个数组的的地址,解引用之后访问的就是整个数组,求的就是整个数组的大小4*4=16;

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

    sizeof(&a+1)&a取出的是整个数组的大小,&a+1跳过的是整个数组,是地址大小就是4/8个字节

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

    sizeof(&a[0])这里取出的是第一个元素的地址,地址大小为4/8个字节。

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

    sizeof(&a[0]+1),&a[0]取出的是第一个元素的地址,+1就是第二个元素的地址,是地址大小就为4/8个字节。

    2 . char arr[ ] = {a,b,c,d,e,f};

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

sizeof(arr)求的是整个数组的大小,6*1=6;

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

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

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

    sizeof(*arr),arr代表数组首元素地址,对首元素地址解引用就是首元素,首元素的大小就是1个字节。

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

    sizeof(arr[1]),这里arr[1]就是代表下标为1的元素,计算的是b的大小,b为char类型所以大小为1个字节。

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

    &arr取出的是整个数组的地址,是地址大小就是4/8字节

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

    &arr取出的是整个数组的大小,&arr+1跳过的是整个数组,是地址大小就是4/8个字节

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

    &arr[0],取出的是第一个元素的地址,+1就是第二个元素的地址,是地址大小就是4/8个字节。

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

    strlen(arr)求的是字符串长度,因为结尾没有\0,所以计算的大小为随机值。

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

    strlen(arr+0)求的是字符串长度,因为结尾没有\0,所以计算的大小为随机值。

  • printf("%d\n", strlen(*arr));

    strlen需要的是这个地址,从这个地址开始找字符直到\0统计字符的个数,但是*arr表示的是数组首元素,也就是字符a,这时候传给strlen就是a的ascii码值97,strlen函数会把97作为起始地址统计字符串会形成内存访问冲突。

  • printf("%d\n", strlen(arr[1]));

    strlen(arr[1]),arr[1]访问的是下标为1的字符b,转化为ascii码值为98会发生错误。回发生内存访问冲突。

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

    &arr是arr数组的地址,虽然类型和strlen的参数类型有所差异,但是传参过去后还是从第一个字符位置向后数字符,但结果还是随机值

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

    strlen(&arr+1),传参过去后还是从第一个字符位置向后数字符,结果也是随机值

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

    strlen(&arr[0]+1),传参过去后也是从第一个字符位置向后数字符,结果也是随机值

   3. char arr【】 ="abcdef";

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

    sizeof(arr),计算的是整个数组的大小,还有\0,所以是7个字节。

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

    sizeof(arr+0),arr没有单独放在sizeof内部也没有取地址,所以arr代表首元素地址,所以地址大小就是4/8个字节。

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

    sizeof(*arr),这里的arr代表首元素地址对其解引用就是访问数组地第一个元素,大小就为1个字节<==>*(arr+0)<==>arr[0]。

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

    sizeof(arr[1])计算的是数组下标为1的元素的大小,所以大小就为1个字节。

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

    sizeof(&arr),&arr是取出整个数组的地址,只要是地址大小就为4/8个字节。

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

    sizeof(&arr+1)&arr取出的是整个数组的地址,+1就是跳过整个数组,但也是地址,那也是4/8个字节。

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

    sizeof(&arr[0]+1)&arr[0]取出的是第一个元素的地址,+1之后就是第二个元素的地址,是地址就是4/8个字节。

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

    strlen(arr)拿到首元素地址然后向后数到\0为止,计算\0前面的字符所以长度是6.

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

    strlen(arr)拿到首元素地址然后向后数到\0为止,计算\0前面的字符所以长度是6.

  • printf("%d\n", strlen(*arr));

    strlen(*arr),arr是数组首元素地址,加上*解引用访问的就是第一个字符a,转化为ascii码值就是97,会发生内存访问冲突。

  • printf("%d\n", strlen(arr[1]));

    strlen(arr[1]),a[1]是字符b,字符b转化为ascii码值就是98,会发生内存访问冲突。

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

    strlen(&arr),&arr取出的是整个数组的地址,数字符数到\0为止,所以大小为6.

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

    strlen(&arr+1),&arr取出的是整个数组的地址,&arr+1,跳过整个数组,所以结果为随机值。

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

    strlen(&arr[0]+1),&arr【0】是取出第一个元素的地址,&arr【0】+1是第二个元素的地址,所以大小就是5;

    4.  char* p ="abcdef";

这里的每个元素类型都是char*;

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

    sizeof(p),p是一个指针变量指向第一个元素的地址,是地址大小就是4/8个字节。

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

    sizeof(p+1),p+1指向第二个元素的地址,是地址大小就是4/8个字节。

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

    sizeof(*p),p指向首元素地址,对p解引用访问的是第一个元素,大小就是1个字节。p是char*的指针,解引用访问一个字节,大小就是一个字节。

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

    sizeof(p[0])p[0]是第一个元素,大小就是1个字节。<==>*(p+0);

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

    sizeof(&p),p是指向第一个元素的地址,&p也是地址,是地址就是4/8个字节。

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

    sizeof(&p+1),&p+1也是地址,是地址大小就是4/8个字节。

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

    sizeof(&p[0]+1)&p[0]是取出第一个元素的地址+1指向第二个元素地址,是地址大小就是4/8个字节。

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

    strlen(p),p是指针变量,指向第一个元素的地址,就从第一个元素的地址开始一直数到\0为止,所以大小就为6;

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

    strlen(p+1),p是指向首元素地址,p+1就是指向第二个元素的地址从第二个元素地址开始一直数到结尾\0为止,所以大小是5个字节

  • printf("%d\n", strlen(*p));

    strlen(*p),p是指向第一个元素的地址,对p解引用就是字符a,转化成ascii码值,就会发生内存访问冲突。

  • printf("%d\n", strlen(p[0]));

    strlen(p[0]),p[0]找到第一个元素,转化成ascii码值就是97会发生内存访问冲突。

  • printf("%d\n", strlen(&p));

    strlen(&p)&p是取出指针变量的地址,取出一级指针变量的地址也就是二级指针,所以大小就是随机值。

  • printf("%d\n", strlen(&p+1));

    strlen(&p+1)取出一级指针变量的地址+1,跳过p的地址,所以还是随机值

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

    strlen(&p【0】+1),&p[0]取出的是数组首元素地址+1,就是第二个元素的地址,所以大小就是5个字节。

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

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

    sizeof(a),a是一个二维数组,sizeof(a)表示整个数组,计算的是整个数组的大小,3*4*4=48;数组名单独放在sizeof内部计算的是整个数组的大小。

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

    sizeof(a[0][0]),a[0][0]代表二维数组第一行第一个元素,计算的是第一行第一个元素的大小所以大小就是4个字节。

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

    a【0】表示第一行的数组名,作为数组名单独放在sizeof内部,计算的是整个数组的大小,计算的是第一行的大小4*4=16;

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

    a[0]作为第一行的数组名,没有取地址,没有单独放在sizeof内部,所以a【0】表示首元素地址,表示第一行第一个元素的地址,a[0]+1就是第一行第二个元素的地址,是地址也就是4/8个字节a[0]+1<==>*(a+0)+1

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

    代表第一行第二个元素,答案就是4个字节

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

    sizeof(a+1),a是二维数组的数组名,没有取地址,没有单独放在sizeof内部,a表示首元素地址,a就为二维数组低第一行的地址,a+1就是第二行的地址,是地址大小就是4/8个字节

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

    sizeof(*(a+1)),*(a+1)就是第二行,相当于第二行的数组名,*(a+i)<==>a[1],sizeof(*(a+1))计算的就是第二行的大小,4*4=16;

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

    sizeof(&a[0]+1),a[0]是二维数组的第一行的地址,&a[0]就是取出二维数组的第一行的地址,+1就是取出第二行的地址,是地址大小就是4/8个字节。

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

    sizeof(*(&a[0]+1))对二维数组的第二行解引用,访问的是二维数组的第二行,也就是4*4=16个字节。

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

    sizeof(*a),a是一个二维数组的数组名,没有取地址,没有单独放在sizeof内部,a表示首元素地址,对a解引用就相当于取出二维数组第一行16个字节,*(a)<==>*(a+0)<==>a[0]16个字节

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

    sizeof(a[3]),a[3]是二维数组的第4行的大小,大小就是16,感觉a[3]越界了但是没有关系,sizeof不会计算内部表达式


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


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)这里a没有单独放在sizeof内部,也没有&数组名,所以a是首元素地址,首元素地址+1就是第二个元素的地址,解引用就是指向2;

 a是一个一维数组,&a,就是取出整个数组的地址,&a+1就是跳过整个数组,ptr-1就是指向5

 


//结构体的大小是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;
}

这里的考点就是不同类型的+1会是多少。

第一个是结构体类型+1,我们知道结构体类型大小是20,所以地址就是00000014.

第二个挺坑的,刚开始大家都会想long是4个字节所以+4,但是你想多了,这里只是整形的+-又不是指针+-整数,所以这里就跟正常+-整形是一样的,所以打印的地址就是00000001;

第三个是整形指针+1,这个大家都知道是跳过4个字节,所以地址是00000004;

如果大家对第二个还是有疑问不如我们来验证一下,整形的+-,和指针的+-

 


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

 

 &a是整个数组的地址,&a+1是跳过整个数组,ptr【-1】可以这么理解 *(ptr-1)就是指向4;

这里也有坑点,这里的a没有&a数组名,没有单独放在sizeof内部 ,所以a是数组首元素地址,但是这里要注意的是将强转成整形我们这里就要注意了,这里的+1不是下一个元素的地址,而是跳过一个字节,所以我们以小端存储为例,这里跳过一个字节,对ptr解引用向后访问4个字节,然后一小端的方式存储,以小端的方式取出来,就是02000000;

 


#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,3,5剩下的未初始化的,都初始化为0;

a没有&a数组名,没有单独放在sizeof内部,所以a是数组首元素的地址,a【0】就是第一行第一行第一个元素的地址,p指向的就是第一个元素的地址,p[0]可以看做*(p+0),访问的就是二维数组第一行第一个元素1了;


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

a是一个5行5列的一个二维数组,p是一个数组指针,数组里有4个元素每个元素类型为数组指针,我们先看&a[4][2] ,&除底4行第二个元素的地址,也就是图中红色的长方形,我们再来看&p[4][2],p一次访问4个字节,p+4就是图中位置,&p[4][2],可以理解为*(p+4)+2,向后访问两个字节,指向3的地址,&p[4][2] - &a[4][2] 指针-指针是两者之间的元素个数,低地址-高地址,所以答案是-4,当然转化为地址,先将-4转化成原码 10000000 00000000 00000000 00000100   再将原码变成反码 11111111 11111111 11111111 11111011 ,反码+1是补码  11111111 11111111 11111111 11111100    转化成16进制就是 ff ff ff fc .


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

 a数组里每个元素都是char*的元素,char**=a; 意思是char**指向a这一行的地址,pa++,指向下一行的地址,解引用访问的是第二行的元素,所以答案是at。

 


#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是取出整个数组低地址,&aa+1跳过整个数组,ptr1 -1得到的就是10的地址,解引用访问10这个元素。

aa是二维数组第一行的地址,aa+1跳过这一行拿到第二行的地址(相当于第二行的数组名),解引用找到6的地址,ptr2-1就是得到5的地址,一解引用就找到元素5;

 


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

 刚开始的指向

  •  **++cpp

先++cpp指向c+2,解引用拿到c+2的内容相当于c+2的地址,在解引用拿到c+2的内容然后访问得到POINT;

  •  *--*++cpp+3

++优先级高,先++cpp,指向c+1, 解引用找到c+1的内容,在--,将c+1 变成c ,相当于拿到c的地址,解引用找到c所指向的内容,在+3从E开始打印,所以结果是ER;

  • *cpp[-2] +3 

*cpp[-2]相当于*(*(cpp-2)),cpp相当于char***指向c(原来的c+1),-2就是减去两行,指向c+3,解引用相当于拿到c+3的内容,也相当于拿到c+3的地址,在解引用拿到c+3的内容, 在+3就是从s开始打印,结果就是ST;

  • cpp[-1][-1] +1

cpp[-1][-1]相当于  *(*(cpp-1)-1)  , cpp-1就指向c+2的地址,解引用访问到c+2的内容也相当于找到c+2的地址,在-1,访问到c+1的地址,在解引用访问到c+1所指向的内容,+1之后从E开始打印,所以答案是EW;

 

  • 38
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 22
    评论
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值