数组那些事

什么是数组名

数组名是数组的首元素的地址

#include<stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9.10 };
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);
	return 0;
}}

在这里插入图片描述

两个例外

1.sizeof(数组名):这里的数组名不表示数组的首元素的地址,这里的数组名表示的是整个数组。所以sizeof(数组名)计算的是整个数组的大小,单位是字节。
2.&数组名:这里的数组名也表示的是整个数组的地址,&arr取出的是整个数组的地址。

#include<stdio.h>
int main()

{
	int arr[]={1,2,3,4,5,6,7,8,9,10};
	int sz = sizeof(arr);
	printf("%d",sz);
	return 0;
}

在这里插入图片描述

为啥sizeof(数组名)计算的结果是40字节呢?
因为这是int类型的数组,int类型的元素大小是4个字节,计算的结果也就是4*元素个数。

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

在这里插入图片描述
为啥&数组名和数组名的结果相同呢?
我们在举一个例子:

#include<stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%p\n", arr);
	printf("%p\n",arr + 1);

	printf("%p\n", &arr);
	printf("%p\n", &arr + 1);
}

在这里插入图片描述
从上面的程序中我们可以直观的看出:
arr+1比arr跳过了4个字节的空间,也就是1个整型的空间;
&arr+1跳过了40字节的空间,也就是当前整个数组的空间。

练习题

在进行练习之前,我们需要知道,地址的大小是4/8字节
在32位机器上有32根地址线,每根地址线都会产生0/1这样的二进制数字,每个二进制数字的大小是1bit,组成32位长度的二进制序列,8个0/1组成一个字节,32个0/1的大小是4字节;
在64位机器上有64根地址线,同样的每根地址线都会产生0/1这样的二进制数字,每个二进制数字的大小的1bit,组成长度位64的二进制序列,8个0/1组成一个字节,64个0/1数字组成的二进制序列的大小是8字节。
sizeof -计算变量/数组大小的操作数
strlen -计算字符串长度的函数,需要向strlen参数部分需要传地址,需要string.h头文件
在这里插入图片描述

一维数组
#include<stdio.h>
int main()
{
    int a[] = { 1,2,3,4 };
    
    printf("%d\n", sizeof(a));     //16      元素个数*元素类型
    printf("%d\n", sizeof(a + 0)); //4/8     a是数组首元素的地址,a+0还是第一个元素的地址,地址的大小就是4/8字节
    printf("%d\n", sizeof(*a));    //4       a表示数组首元素地址 *a表示数组第一个元素  sizeof(*a)就是数组第一个元素的大小
    printf("%d\n", sizeof(a + 1)); //4/8     a是数组首元素的地址,地址+1指向第二个元素的地址,地址的大小是4/8个字节
    printf("%d\n", sizeof(a[1]));  //4       计算的是数组第二个元素的大小
    printf("%d\n", sizeof(&a));    //4/8     &a取出的是数组的地址,数组的地址也是地址,是地址大小就是4/8字节
    printf("%d\n", sizeof(*&a));   //16      &a取出的是数组的地址,*拿到整个数组的元素,求的是整个数组元素的大小
    printf("%d\n", sizeof(&a + 1));//4/8     &a取出的是数组的地址,+1跳过整个数组,产生的是最后一个元素后面的地址 是地址,它的大小就是4/8,sizeof此时访问的是&arr+1的位置,属于数组越界,但sizeof只是看它的类型,并不会去进行访问arr+1后的空间
    printf("%d\n", sizeof(&a[0])); //4/8     &a[0]取出的是第一个元素的地址   
    printf("%d\n", sizeof(&a[0] + 1));//4/8  数组第二个元素的地址
	return 0;
}

在这里插入图片描述
这是在x32环境下运行的结果

字符数组

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

    printf("%d\n", sizeof(arr));      //6      sizeof只计算数组元素所占内存空间的大小,和\0无关
    printf("%d\n", sizeof(arr + 0));  //4/8    首元素地址,地址的大小就是4/8字节
    printf("%d\n", sizeof(*arr));     //1      arr没有单独放在sizeof内部,所以arr表示数组首元素地址,*arr后得到数组第一个元素的大小
    printf("%d\n", sizeof(arr[1]));   //1      这里表示的是数组第二个元素的大小
    printf("%d\n", sizeof(&arr));     //4/8    &arr是数组的地址,地址的大小是4/8字节
    printf("%d\n", sizeof(&arr + 1)); //4/8    &arr+1是从数组的地址开始向后跳过了整个数组的地址
    printf("%d\n", sizeof(&arr[0] + 1));//4/8  是数组第二个元素的地址

    printf("%d\n", strlen(arr));      //随机值    arr数组中没有\0,会一直向后寻找
    printf("%d\n", strlen(arr + 0));  //随机值    arr+0是数组首元素地址,但数组中没有\0,会一直向后寻找
    // printf("%d\n", strlen(*arr));     //err    arr是数组首元素地址,*a就是数组首元素,也就是'a',a的sacll码值是97,相当于传参的内容是97,告诉strlen函数从97地址处向后寻找\0并且统计字符个数,但我们字符数组的首元素的地址并不是97,访问的不是我们的空间,属于非法访问
    // printf("%d\n", strlen(arr[1]));   //err    同上
    printf("%d\n", strlen(&arr));     //随机值    &arr取出的是整个数组的地址,是从首元素地址开始向后寻找\0
    printf("%d\n", strlen(&arr + 1)); //随机值    数组地址+1意思是跳过整个数组,从最后一个元素后面开始寻找\0,因为是从&arr+1地址处开始寻找,所以随机值也就是&arr的随机值-arr的数组所占空间的大小
    printf("%d\n", strlen(&arr[0] + 1));//随机值  &arr[0]取出的是第一个元素的地址,&arr[0]+1从第二个元素的地址开始向后寻找\0,产生的随机值比&arr的随机值少了1,因为是从&arr[0]+1开始寻找的
    return 0;
}

在这里插入图片描述
同样是x32环境下的运行的结果
在这里插入图片描述
如果我们将下面两行代码放开,就会发生非法访问
printf(“%d\n”, strlen(arr));
printf(“%d\n”, strlen(arr[1]));
将0x 00 00 00 61这个16进制的地址转换为十进制地址是6
16+1=97,就是读取内存位置97时发生了非法访问。
因为用a,b,c,d,e,f初始化arr数组,arr数组又是局部变量,arr数组有是存放在栈区。

字符串

#include<stdio.h>
#include<string.h>
int main()
{
    char arr[] = "abcdef";//[ a b c d e f \0 ]
    //这里我们是用字符串初始化数组arr

    printf("%d\n", sizeof(arr));     //7       sizeof计算的数组总大小,\0也是占内存空间的
    printf("%d\n", sizeof(arr + 0)); //4/8     arr+0是首元素地址
    printf("%d\n", sizeof(*arr));    //1       a-97  *arr是数组首元素
    printf("%d\n", sizeof(arr[1]));  //1       b-98  arr[1]数组的第二个元素
    printf("%d\n", sizeof(&arr));    //4/8     &arr数组的地址,但是数组的地址依然是地址
    printf("%d\n", sizeof(&arr + 1));//4/8     &arr+1跳过整个数组,是\0后面的地址,但是依然是地址
    printf("%d\n", sizeof(&arr[0] + 1));//4/8  是数组第二个元素的地址

    printf("%d\n", strlen(arr));     //6       strlen函数统计的是\0之前元素的个数
    printf("%d\n", strlen(arr + 0)); //6       依旧是首元素的地址,开始向后寻找\0
    printf("%d\n", strlen(*arr));    //err     非法访问
    printf("%d\n", strlen(arr[1]));  //err     非法访问
    printf("%d\n", strlen(&arr));    //6       取出的是整个数组的地址,但是是从首元素地址开始向后读取
    printf("%d\n", strlen(&arr + 1));//随机值  &arr+1跳过整个数组,在arr数组后还有没有/0未知   
    printf("%d\n", strlen(&arr[0] + 1));//5    首元素地址+1是表示第二个元素的地址
	return 0;
}

在这里插入图片描述

#include<stdio.h>
#include<string.h>
int main()
{
    char* p = "abcdef";
    //这里并不是将字符串abcdef整个的地址放到了指针变量p中,而是将字符串首元素'a'的地址放到了指针变量p中
    printf("%d\n", sizeof(p));         //4/8   p是指针变量,计算的是指针变量的大小
    printf("%d\n", sizeof(p + 1));     //4/8   p+1是字符‘b’的地址
    printf("%d\n", sizeof(*p));        //1     *p - a
    printf("%d\n", sizeof(p[0]));      //1     p[0] -> *(p+0) -> *p
    printf("%d\n", sizeof(&p));        //4/8   &p是指针变量p在内存中的地址  可以理解为char* *pp = &p;
    printf("%d\n", sizeof(&p + 1));    //4/8   &p是跳过p之后的地址 
    printf("%d\n", sizeof(&p[0] + 1)); //4/8   &p[0]是a的地址,+1就是b的地址

    printf("%d\n", strlen(p));         //6     从a的地址向后找\0
    printf("%d\n", strlen(p + 1));     //5     从b的地址向后找\0
    // printf("%d\n", strlen(*p));        //err  
    // printf("%d\n", strlen(p[0]));      //err
    printf("%d\n", strlen(&p));        //随机值  因为这里是从p的地址向后寻找\0
    printf("%d\n", strlen(&p + 1));    //随机值  同上
    printf("%d\n", strlen(&p[0] + 1)); //5       a的地址+1就是b的地址,从b向后数元素
    return 0;
}

在这里插入图片描述

二维数组
#include<stdio.h>
int main()
{
    int a[3][4] = { 0 };

    printf("%d\n", sizeof(a));            //48    计算的是整个数组的大小,单位是字节3*4*4=16
    printf("%d\n", sizeof(a[0][0]));      //4     第一行第一个元素的大小
    printf("%d\n", sizeof(a[0]));         //16    二维数组的每一行可以认为是一个一维数组,那么a[0]可以理解为一维数组的数组名
    printf("%d\n", sizeof(a[0] + 1));     //4/8   a[0]作为第一行的数组名,并没有单独放在sizeof内部,
    //所以a[0]就是数组首元素的地址,就是第一行第一个元素的地址,所以a[0]+1就是第一行第二个元素的地址                                         

    printf("%d\n", sizeof(*(a[0] + 1)));  //4/8 表示第一行第二个元素
    printf("%d\n", sizeof(a + 1));        //4/8  a表示首元素地址,也就是第一行的地址,第一行的地址+1就是第二行的地址
    printf("%d\n", sizeof(*(a + 1)));     //16   对第二行的地址*就是访问到第二行 *(a+1) -> a[1]
    printf("%d\n", sizeof(&a[0] + 1));    //4/8    a[0]是第一行的数组名,&就是取出第一行的地址,+1就是第二行的地址
    printf("%d\n", sizeof(*(&a[0] + 1))); //16     对第二行的地址*就是访问第二行的全部元素
    printf("%d\n", sizeof(*a));           //16     a是首元素地址,等于第一行的地址,*就是访问第一行的所以元素   
    printf("%d\n", sizeof(a[3]));         //16     sizeof不会去访问内存,而是根据类型判断大小,这里的类型是int类型,第四行的数组名
    return 0;                          
}

在这里插入图片描述

总结:

数组名是首元素的地址
两个例外:
1.sizeof(数组名):这里的数组名是指整个数组,sizeof计算的空间大小是整个数组所占空间的大小;
2.&数组名:这里的数组名是指整个数组,&数组名取出的是整个数组的地址。

sizeof -是一个操作符
sizeof 计算的是对象所占内存空间的大小 -单位是字节
sizeof不在乎内存中存放的是什么,不会对内部存放的变量进行计算,只在乎存放的类型

strlen - 计算字符串长度的函数
从给定的地址向后访问字符,找到/0并且统计\0之前出现的字符个数
strlen参数部分需要传递地址

地址所占空间大小取决于当前机器是32位还是64位,32位机器下地址所占空间大小是4字节,64位机器下地址所占空间大小是8字节

非法访问:访问一块不属于你创建的空间或者molloc的空间

如果希望以上内容对您有所帮助!当然,如果文章出现错误,欢迎您在评论区或者私信我指出哦~

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值