个人主页(找往期文章包括但不限于本期文章中不懂的知识点):我要学编程(ಥ_ಥ)-CSDN博客
目录
sizeof和strlen的对比
sizeof
在学习操作符的时候,我们学习了 sizeof , sizeof 计算变量所占内存空间的大小,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。sizeof 只关注占用内存空间的大小,不在乎内存中存放什么数据。
#include <stdio.h>
int main()
{
int a = 10;
printf("%zd\n", sizeof(a));
printf("%zd\n", sizeof a);
printf("%zd\n", sizeof(int));//类型一定要加括号
return 0;
}
注意这个sizeof 的返回类型是size_t 。
strlen
strlen 是C语言库函数,功能是求字符串长度。函数原型如下:
//返回类型是size_t,参数是字符指针
size_t strlen ( const char * str );
统计的是从 strlen 函数的参数 str 中这个地址开始向后, ’\0‘ 之前字符串中字符的个数。 strlen 函数会一直向后找 ’\0‘ 字符,直到找到为止,所以可能存在越界查找。
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[3] = { 'a', 'b', 'c' };
char arr2[] = "abc";
printf("%zd\n", strlen(arr1));//随机值
printf("%zd\n", strlen(arr2));//3
printf("%zd\n", sizeof(arr1));//3
printf("%zd\n", sizeof(arr1));//3
return 0;
}
sizeof 和 strlen的对比
1. sizeof是操作符 | 1. strlen是库函数,使用需要包含头文件 string.h |
2. sizeof计算操作数所占内存的大小, 单位是字节 | 2. srtlen是求字符串长度的,统计的是 ‘\0’ 与最开始的字符之间相隔个数 |
3. 不关注内存中存放什么数据 | 3. 关注内存中是否有 ‘\0’ ,如果没有 ‘\0’ ,就会持续往后找,可能会越界 |
数组和指针笔试题解析
以下代码的运行都是在VS2022,X64的环境。
一维数组
#include <stdio.h>
int main()
{
//数组名有特殊两种情况下,是表示整个数组的地址
//1.sizeof(数组名),注意:这个数组名是指只有单独的数组名
//2.&数组名,这里取出的是整个数组的地址
int a[] = { 1,2,3,4 };//a是数组名
//sizeof(数组名),这个数组名是只有单独的数组名,
//因此计算的是整个数组在内存中的大小。
printf("%zd\n", sizeof(a));//16
//sizeof(数组名),这个数组名不是只有单独的数组名,
// 因此,做常规处理,是数组首元素的地址,+0,相当于没加
printf("%zd\n", sizeof(a + 0));//指针大小:4/8
//不是上面两种情况,就是首元素的地址,解引用之后,就是首元素
//相当于计算sizeof(1)
printf("%zd\n", sizeof(*a));//4
//不是上面两种情况,就是首元素的地址,+1,跳过的4个字节,
//也就是计算sizeof(&a[1]),还是地址
printf("%zd\n", sizeof(a + 1));// 4/8
printf("%zd\n", sizeof(a[1]));//4
//这里虽然取出的是整个数组的地址,但是还是计算地址的大小
printf("%zd\n", sizeof(&a));// 4/8
//先把整个数组的地址取出来,再解引用,
//那么拿到的就是整个数组的大小
//可以简单理解为* 与 & 相互抵消了
printf("%zd\n", sizeof(*&a));//16
//地址+1,还是地址
printf("%zd\n", sizeof(&a + 1));// 4/8
//地址
printf("%zd\n", sizeof(&a[0]));// 4/8
//地址+1
printf("%zd\n", sizeof(&a[0] + 1));// 4/8
return 0;
}
字符数组
代码1:
#include <stdio.h>
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
//sizeof(数组名)
printf("%zd\n", sizeof(arr));//6
//sizeof(地址)
printf("%zd\n", sizeof(arr + 0));// 4/8
//sizeof('a')
printf("%zd\n", sizeof(*arr));//1
//sizeof('b')
printf("%zd\n", sizeof(arr[1]));//1
//sizeof(地址)
printf("%zd\n", sizeof(&arr));// 4/8
//sizeof(地址)
printf("%zd\n", sizeof(&arr + 1));// 4/8
//sizeof(地址)
printf("%zd\n", sizeof(&arr[0] + 1));// 4/8
return 0;
}
代码2:
#include <stdio.h>
#include <string.h>
int main()
{
//注意这里不再是sizeof,而是strlen,
// strlen统计的是最开始的那个元素到'\0'之前的个数
char arr[] = { 'a','b','c','d','e','f' };
//注意这里是一个一个的字符,没有'\0'结尾
printf("%zd\n", strlen(arr));//随机值
//这里的arr是首元素的地址,与上面的一样,加0,不变
printf("%zd\n", strlen(arr + 0));//随机值
//通过前面的函数原型学习,我们知道strlen的参数是一个指针
//然而这里却是一个arr[0]-->'a',因此这个是不正确的
printf("%zd\n", strlen(*arr));//错误
//与上一个一样
printf("%zd\n", strlen(arr[1]));//错误
//&arr,取出的是整个数组的地址,
//整个数组的地址的起始位置与数组首元素的地址相同
printf("%zd\n", strlen(&arr));//随机值
//这也是一个随机值,只不过比起上面的要小6,
//因为+1,跳过了整个数组了
printf("%zd\n", strlen(&arr + 1));//随机值-6
//这个就是从第二个元素开始统计
printf("%zd\n", strlen(&arr[0] + 1));//随机值-1
return 0;
}
上面的那个错误,会导致整个程序崩溃,只能打印出其前面的值。
通过上面两幅图的比较,可以得出上面的结论。具体细节可以使用调试来找到:
但是又有两个个新的问题来了,为什么后面的数组明明已经越界访问了,但是没有报错呢?其实这个编译器认为这个错误比起刚刚那个错误不算什么,所以就没有报错,但是我们平时在写代码时,就不要犯这样的错误。另外一个问题就是明明是随机值,但是为什么我们打印出来的是一个定值呢?其实这个是因为我们在第一次创建这个字符数组的时候,它所对应的地址已经确定了(即'\0',出现的位置已经确定),不会再发生改变了,因此无论我们调用多少次这个strlen函数,其返回值都不会发生变化。这也就意味着这个随机值已经固定。
代码3:
#include <stdio.h>
int main()
{
//这个字符数组与上面那个字符数组的区别在于:
//这个字符数组后面跟了一个'\0',而上面那个没有
char arr[] = "abcdef";
//sizeof(数组名)
printf("%zd\n", sizeof(arr));//7(包括了'\0')
//sizeof(地址)
printf("%zd\n", sizeof(arr + 0));// 4/8
//sizeof('a')
printf("%zd\n", sizeof(*arr));//1
//sizeof('b')
printf("%zd\n", sizeof(arr[1]));//1
//sizeof(地址)
printf("%zd\n", sizeof(&arr));// 4/8
//sizeof(地址)
printf("%zd\n", sizeof(&arr + 1));// 4/8
//sizeof(地址)
printf("%zd\n", sizeof(&arr[0] + 1));// 4/8
return 0;
}
代码4:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "abcdef";
printf("%d\n", strlen(arr));//6
//arr是数组首元素的地址,+0,还是一样的
//与上面的结果相同
printf("%d\n", strlen(arr + 0));//6
//arr是数组首元素的地址,解引用之后就是首元素'a'
//strlen的参数是指针,因此这个会报错
//printf("%d\n", strlen(*arr));//错误
//arr[1]是'b',与上面一样会报错
//printf("%d\n", strlen(arr[1]));//错误
//&arr,虽然是取出整个数组的地址,但是也是从首元素开始的
printf("%d\n", strlen(&arr));//6
//&arr+1,会跳过整个数组,但是后面的我们就不知道了
//就意味着是随机值
printf("%d\n", strlen(&arr + 1));//随机值
//&arr[0]+1,拿到首元素的地址后,在跳过1个元素,即&arr[1]
printf("%d\n", strlen(&arr[0] + 1));//5
return 0;
}
代码5:
#include <stdio.h>
int main()
{
//这里是把a的地址存放到p指针里的,而不是这个字符串地址存放到p指针里头
char* p = "abcdef";
//p是一个指针
printf("%zd\n", sizeof(p));// 4/8
//p+1就是b的地址
printf("%zd\n", sizeof(p + 1));// 4/8
//*p的结果就是'a'
printf("%zd\n", sizeof(*p));//1
//p[0]<-->*(p+0)<-->*p
printf("%zd\n", sizeof(p[0]));//1
//注意p本来就是一个指针,现在把它的地址拿到,就是二级指针
printf("%zd\n", sizeof(&p));// 4/8
//在上一个的基础上,再跳过一个char*
printf("%zd\n", sizeof(&p + 1));// 4/8
//&p[0]<-->& *(p+0),这个&与*在效果上是一样的,即为p,指针
printf("%zd\n", sizeof(&p[0] + 1));// 4/8
return 0;
}
代码6:
#include <stdio.h>
#include <string.h>
int main()
{
char* p = "abcdef";
//p=&a
printf("%d\n", strlen(p));//6
//p+1=&b
printf("%d\n", strlen(p + 1));//5
//*p=a,strlen的参数是指针
printf("%d\n", strlen(*p));//错误
//p[0]<-->*(p+0)<-->*p
printf("%d\n", strlen(p[0]));//错误
//p是一个二级指针,不知道其后是什么,因此是随机值
printf("%d\n", strlen(&p));//随机值
//与上面一样,也是随机值,但是与上面那个随机值没有任何关系
printf("%d\n", strlen(&p + 1));//随机值
//&p[0]<-->& *(p+0)<-->p
printf("%d\n", strlen(&p[0] + 1));//5
return 0;
}
说明一下'\0'的ASCll值为0,对应的十六进制也是为0
char *p = "abcdef";
假设p的地址为0x0012ff40,那么strlen的结果就是0,p+1,我们也不知道其后面是什么,可能也有'\0',因此这两个随机值并没有任何关系。