目录
char arr[]={'a','b','c','d','e','f'}的sizeof的使用
char arr[]={'a','b','c','d','e','f'}的strlen的使用
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
图解:
代码运行:
接下来还有不少笔试题,还待继续。欢迎大佬补充!