C语言进阶(sizeof、strlen对比分析和指针、数组相关问题解析)

一、理论知识

1.1 sizeof

  • 类别:操作符
  • 作用:只关注占用内存空间的大小,单位是字节,不关心内存中存放的是什么
  • 特点
    • 内部不参与运算

      • sizeof在源文件转成exe文件即编译 + 链接的时候,就完成了
        ,而表达式的运算则是在exe文件到运行的阶段完成了表达式的运算
    • 没有实际访问sizeof括号里面的内容

int main()
{
	int num = 10;      
	sizeof(int) == sizeof(num);  
	return 0;
}
//sizeof不需要通过访问这个变量来确定大小
//只需要知道这个是什么类型的就可以了

1.2 strlen

  • size_t strlen ( const char * str );
  • 头文件:#include <string.h>
  • 类别:库函数
  • 作用:用来计算字符串的长度,传过去元素的地址,然后从这个地址开始数,直到遇到’\0’,返回这之间字符的个数。
  • 特点:因为是以’\0’为标志,当没有’\0’的时候,会出现越界访问

非法访问
访问内存无法指哪打哪,只有这个内存分配给了你,才可以访问,否则就是非法访问会报错

1.3 数组名的各种表现形式

  • sizeof(数组名)
    数组名表示整个数组,计算的是整个数组的大小,单位是字节。注意里面是只有数组名,不能有其他表达式
  • &数组名
    数组名表示整个数组,取出整个数组的地址,数值上是首元素的地址,但是意义不同,+1跳过的是整个数组
  • 其他
    代表首元素的地址

1.4 关于二维数组

在这里插入图片描述

二、例子解析

2.1一维数组

int main()
{
	//一维数组
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));   //16
	printf("%d\n", sizeof(a + 0));   //4/8
	printf("%d\n", sizeof(*a));    //4
	printf("%d\n", sizeof(a + 1));   //4/8
	printf("%d\n", sizeof(a[1]));   //4
	printf("%d\n", sizeof(&a));    //4/8
	printf("%d\n", sizeof(*&a));     //16
	printf("%d\n", sizeof(&a + 1));     //4/8
	printf("%d\n", sizeof(&a[0]));      //4/8
	printf("%d\n", sizeof(&a[0] + 1));  //4/8
	return 0;
}

解析:

  • sizeof(a):
    表示计算整个数组的大小,数组有4个元素,每个元素是int类型,大小为4,所以为4*4=16个字节
  • sizeof(a + 0):
    单独的数组名表示首元素的地址,+0后意义不变,是4/8个字节大小(32位环境下就是4个字节,64位环境下就是8个字节)
  • sizeof(*a)
    对首元素地址解引用,得到的就是首元素,因为首元素就是int类型的,所以大小是4个字节
  • sizeof(a + 1)
    首元素地址+1,因为是int类型,+1跳过一个int类型,得到第二个元素的地址,是地址大小就是4/8个字节大小
  • sizeof(a[1]):
    可以翻译成*(a+1),得到的是第二个元素,int类型,大小为4个字节
  • sizeof(&a):
    &数组名得到的是整个数组的大小,是地址,大小就是4/8个字节
  • sizeof(*&a):
    解引用整个数组的地址,得到的就是整个数组,这个数组有4个元素,每个元素是4个字节大小,所以整个数组大小为4*4=16
  • sizeof(&a + 1):
    整个数组地址+1,类型是int(*)[4],+1跳过一个类型大小,得到数组后面的空间,是地址大小就是4/8个字节
  • sizeof(&a[0]):
    取出第一个元素的地址,大小为4/8个字节
  • sizeof(&a[0] + 1):
    第一个元素地址+1,得到第二个元素地址,大小为4/8个字节
    在这里插入图片描述

2.2 字符数组

❤️1.

int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));   //6
	printf("%d\n", sizeof(arr + 0));   //4/8
	printf("%d\n", sizeof(*arr));   //1
	printf("%d\n", sizeof(arr[1]));    //1
	printf("%d\n", sizeof(&arr));    //4/8
	printf("%d\n", sizeof(&arr + 1));    //4/8
	printf("%d\n", sizeof(&arr[0] + 1));   //4/8
	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;
}

解析:

  • sizeof(arr):
    单独在sizeof里面的数组名,表示的是整个数组,计算的是整个数组的大小,该数组里面有6个元素,且都是char类型,所以结果为6*1=6字节
  • sizeof(arr + 0):
    arr表示的是首元素的地址,+0不变,结果是第一个元素的地址,大小为4/8个字节
  • sizeof(*arr)
    arr表示的是首元素的地址,解引用得到首元素,首元素类型为char,大小为1字节
  • sizeof(arr[1])
    可以翻译为*(arr+1),arr表示首元素的地址,得到的结果是第二个元素,类型为char,大小为1字节
  • sizeof(&arr)
    arr表示整个数组,&arr即取出整个数组的地址,是地址大小就是4/8个字节
  • sizeof(&arr + 1)
    &arr表示整个数组的地址,类型为char(*)[]+1跳过了整个数组,来到了数组后面的空间,是地址大小就是4/8个字节
  • sizeof(&arr[0] + 1)
    取出arr[0[的地址,+1跳过一个对应类型(int*),得到第二个元素的地址,大小是4/8个字节
  • strlen(arr)
    arr表示首元素的地址,strlen是从传进去的地址开始数,直到遇到’\0’,但是arr这个数组里没有’\0’,所以返回的是一个随机值(不知道第几个字符会遇上’\0’)
  • strlen(arr + 0)
    arr表示首元素的地址,+0跳过一个类型,得到首元素的地址,从这个地址数到’\0’,不确定有几个字符,结果为随机值
  • strlen(*arr)
    arr表示首元素的地址,*arr得到首元素’a’,内存中存的是对应的ASCII值97,因为库函数strlen的参数是const char * str ,所以97被看作是地址,但是随意访问没有分配的内存是非法访问
  • strlen(arr[1])
    可以翻译成*(arr+1),得到第二个元素,得到字符’b’,字符在内存的存储是对应的ASCII值,为98,因为库函数strlen的参数是const char * str ,所以98被看作是地址,但是随意访问没有分配的内存是非法访问
  • strlen(&arr)
    &arr表示取出整个数组的地址,整个数组的地址和数组首元素的地址值相同,只不过一次跳的字节大小不同,从该地址数过去不知道过几个字符才会遇到’\0’,所以结果是随机值
  • strlen(&arr + 1)
    &arr+1表示取出整个数组的地址,然后+1跳过数组得到数组后面的空间,从该地址数过去不知道过几个字符才会遇到’\0’,所以结果是随机值
  • strlen(&arr[0] + 1)
    &arr[0]+1表示取出第一个元素的地址,然后+1跳过一个类型,得到第二个元素的地址,从该地址数过去不知道过几个字符才会遇到’\0’,所以结果是随机值
    在这里插入图片描述
    ❤️2.
#include <stdio.h>
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));    //7
	printf("%d\n", sizeof(arr + 0));   //4/8
	printf("%d\n", sizeof(*arr));    //1
	printf("%d\n", sizeof(arr[1]));   //1
	printf("%d\n", sizeof(&arr));    //4/8
	printf("%d\n", sizeof(&arr + 1));   //4/8
	printf("%d\n", sizeof(&arr[0] + 1));   //4/8
	printf("%d\n", strlen(arr));    //6
	printf("%d\n", strlen(arr + 0));   //6
	printf("%d\n", strlen(*arr));   //非法访问
	printf("%d\n", strlen(arr[1]));   //非法访问
	printf("%d\n", strlen(&arr));   //6
	printf("%d\n", strlen(&arr + 1));   //随机值
	printf("%d\n", strlen(&arr[0] + 1));   //5
	return 0;
}

解析:

  • sizeof(arr):
    sizeof(数组名)表示计算整个数组的大小,这个数组一共有7个元素,每个元素是char类型,所以结果是7*1=7字节大小
  • sizeof(arr + 0):
    arr+0表示首元素地址,是地址大小就是4/8字节
  • sizeof(*arr)
    *arr表示解引用首元素地址,得到首元素,元素是’a’,因为是char类型,所以大小是1字节
  • sizeof(arr[1])
    可以翻译为*(arr+1),得到第二个元素,因为是char类型,所以大小是1字节
  • sizeof(&arr)
    &arr取出的是整个数组的地址,是地址大小就是4/8字节
  • sizeof(&arr + 1)
    &arr+1得到的是数组后面的空间,还是地址,类型是char(*)[],是地址大小就是4/8字节
  • sizeof(&arr[0] + 1)
    &arr[0]+1表示取出首元素的地址,+1跳过一个类型大小,得到第二个元素的地址,是地址大小就是4/8字节
  • strlen(arr)
    arr表示首元素的地址,字符串是隐藏了’\0’的,从首元素开始数,到’\0’,一共有6个元素,所以结果是6
  • strlen(arr + 0)
    arr+0表示首元素的地址,结果和理由同上
  • strlen(*arr)
    *arr表示解引用首元素的地址,即得到了首元素’a’,内存中存的是对应的ASCII值97,因为库函数strlen的参数是const char * str ,所以97被看作是地址,但是随意访问没有分配的内存是非法访问
  • strlen(arr[1])
    arr[1]表示第二个元素’b’,结果是非法访问,原理同上
  • strlen(&arr)
    &arr表示取出整个数组的地址,从该地址开始数到’\0’,一共有6个元素,所以结果是6
    虽然传过去的类型依旧不匹配,但是从strlen的角度去解读,是6
  • strlen(&arr + 1)
    &arr+1表示取出整个数组的地址,然后跳过一个char(*)[]类型,得到数组后面的空间,该地址数过去不知道过几个字符才会遇到’\0’,所以结果是随机值
  • strlen(&arr[0] + 1)
    &arr[0]+1表示取出第一个元素的地址,然后+1跳过一个int*类型,得到第二个元素的地址,从该地址开始数,到’\0’,一共有5个元素,所以结果为5
    在这里插入图片描述

❤️3.

int main()
{
	char* p = "abcdef";
	printf("%d\n", sizeof(p));  //4/8
	printf("%d\n", sizeof(p + 1));  //4/8
	printf("%d\n", sizeof(*p));   //1
	printf("%d\n", sizeof(p[0]));   //1
	printf("%d\n", sizeof(&p));   //4/8
	printf("%d\n", sizeof(&p + 1));   //4/8
	printf("%d\n", sizeof(&p[0] + 1));   //4/8
	printf("%d\n", strlen(p));    //6
	printf("%d\n", strlen(p + 1));   //5
	printf("%d\n", strlen(*p));    //非法访问
	printf("%d\n", strlen(p[0]));    //非法访问
	printf("%d\n", strlen(&p));    //随机值
	printf("%d\n", strlen(&p + 1));    //随机值
	printf("%d\n", strlen(&p[0] + 1));    //5
	return 0;
}

解析:

  • sizeof(p):
    p里面放的是常量字符串中首元素的地址,是地址大小就是4/8字节大小
  • sizeof(p + 1):
    p里面放的是常量字符串中首元素的地址,+1跳过一个char*类型,得到第二个元素的地址,是地址大小就是4/8字节大小
  • sizeof(*p)
    *p得到常量字符串中的首元素’a’,因为是char类型的,所以结果是1字节
  • sizeof(p[0]):
    p[0]可以翻译为*(p+0),得到的是常量字符串中的首元素’a’,结果是1字节
  • sizeof(&p):
    &p表示取出指针变量p的指针,是地址大小就是4/8字节
  • sizeof(&p + 1):
    &p表示取出指针变量p的指针,+1表示跳过一个char**,得到p后面的空间的地址,是地址大小就是4/8字节大小
  • sizeof(&p[0] + 1):
    &p[0] + 1表示第二个元素的地址,是地址大小就是4/8字节大小
  • strlen§:
    p里面放的是常量字符串中首元素的地址,strlen是从传进去的地址开始数,直到遇到’\0’,常量字符串有个隐藏的‘\0’,结果是6
  • strlen(p + 1):
    p+1表示第二个元素的地址,从第二个元素开始数到’\0’一共有5个字符,所以结果是5
  • strlen(*p)
    *p表示首元素‘a’,访问没有分配的内存空间,会发生非法访问
  • strlen(p[0]):
    p[0]表示首元素‘a’,访问没有分配的内存空间,会发生非法访问
  • strlen(&p):
    &p表示取出指针变量p的指针,从该地址开始数,不知道什么时候才会遇到‘\0’,所以结果是随机值
  • strlen(&p + 1):
    &p+1表示指针变量p后面的空间地址,从该地址开始数,不知道什么时候才会遇到‘\0’,所以结果是随机值
  • strlen(&p[0] + 1):
    &p[0] + 1表示第二个元素的地址,从该地址开始数,需要经过5个字符才会遇到‘\0’,所以结果是5
    在这里插入图片描述

2.3 二维数组

int main()
{
	//二维数组
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));   //48
	printf("%d\n", sizeof(a[0][0]));   //4
	printf("%d\n", sizeof(a[0]));   //16
	printf("%d\n", sizeof(a[0] + 1));   //4/8
	printf("%d\n", sizeof(*(a[0] + 1)));   //4
	printf("%d\n", sizeof(a + 1));    //4/8
	printf("%d\n", sizeof(*(a + 1)));   //16
	printf("%d\n", sizeof(&a[0] + 1));   //4/8
	printf("%d\n", sizeof(*(&a[0] + 1)));   //16
	printf("%d\n", sizeof(*a));   //16
	printf("%d\n", sizeof(a[3]));   //16
	return 0;
}

解析:

  • sizeof(a):
    sizeof(数组名)表示计算整个数组所占内存空间的大小,该数组共有34=12个元素,每个元素是int类型,为4个字节大小,所以结果为124=48字节
  • sizeof(a[0][0]):
    a[0][0]可以翻译为*((a+0)+0)或者是(a[0]+0),得到的是第0行第0列的元素,因为元素是int类型的,所占空间大小为4字节,所以结果为4字节
  • sizeof(a[0]):
    a[0]可以翻译为*(a+0),得到的该数组的第一个一维数组,这个数组一共有4个元素,且是int类型的,所以结果为4*4=16字节大小

    sizeof(数组名)表示计算整个数组的大小,这里是计算第一个一维数组的大小,为16字节
  • sizeof(a[0] + 1):
    a[0]是数组名表示的是第一行第一列元素的地址,+1得到第一行第二个元素的地址,是地址大小就是4/8个字节
  • sizeof(*(a[0] + 1)):
    *(a[0])表示取出第一行第一列的元素,加一得到第一行第二个元素,因为是int类型,所以结果为4字节
  • sizeof(a + 1):
    a是该二维数组的数组名,表示首元素的地址,该二维数组的首元素是a[0],+1得到a[1]即第二行数组的地址,是地址大小就是4/8个字节
  • sizeof(*(a + 1)):
    (a+1)表示的是第二行数组的地址,解引用后得到第二行数组,这个数组一共有4个元素,且是int类型的,所以结果为4*4=16字节大小
  • sizeof(&a[0] + 1):
    &数组名表示取出整个数组的地址,+1来到第二个数组的地址,是地址大小就是4/8个字节
  • sizeof(*(&a[0] + 1)):
    得到的是第二个数组里的所有元素,大小是4*4=16个字节
  • sizeof(a):
    a表示首元素a[0]的地址,解引用得到第一个数组里的所有元素,大小是44=16个字节
  • sizeof(a[3]):
    a[3]是不存在的,如果存在的话,是计算第四个一维数组的大小,为16个字节
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值