在经历的数组、指针、指针数组、数组指针...的学习过后,我们真的很难很清晰的去理解,它们之间的关系究竟是什么,下面小编就以一道笔试题来为大家清晰地讲解一下sizeof和strlen中计算情景不同的类型,以及使用方式。
我们首先需要掌握sizeof和strlen不同使用方式:
sizeof其实仅仅只是一个操作符,我们要注意它并不是一个函数,他就类似与常见的+、=、-......的操作符,并且sizeof是一个单目操作符。sizeof实际上是获取了数据在内存中所占用的存储空间,以字节为单位来计数。
我们在初学C语言时,就已经知道了sizeof可以计算各种数据类型的所占的存储空间。例如,回忆一下以下代码:
#include
int main()
{
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(char));
printf("%d\n", sizeof(short));
printf("%d\n", sizeof(long));
printf("%d\n", sizeof(long long));
printf("%d\n", sizeof(double));
printf("%d\n", sizeof(float));
printf("%d\n", sizeof(long double));
return 0;
}
运行出来的结果为(不同的计算器运行的结果部分不同):
4
1
2
4
8
8
4
8
sizeof用于变量,计算整个数组的大小,单位是字节。
我们可以看一下下面的例子:
#include
int main()
{
int a = 5;
int b = 10;
int arr[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(b));
printf("%d\n", sizeof(arr));
return 0;
}
运行的结果为:
4
4
16
strlen()是一个函数,是字符串中字符的个数。 sizeof () 可以用于任何类型的数据,而 strlen () 只能用于以空字符 '\0' 结尾的字符串。
size_t strlen ( const char * str ) ;
#include
这里我们注意需要向strlen中传入一个字符串地址,通过这个地址strlen函数计算其字符的个数。例如,以下代码:
int a = 0;
pritnf("%d\n", strlen(a));
这个代码,它明显是错误的,因为他不满足一下两点:
而正确的使用方式:
#include
#include
int main()
{
char arr[] = {'a','b','c','d','e','f'};
char arr1[] = { 'a','b','c','d','e','f','\0'};
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr1));
return 0;
}
要注意strlen计算的是字符串‘\0’以前的字符个数,而在上面的代码中arr数组,并没有‘\0’,所以并不知道‘\0’在内存的什么地方。
运行的结果为:
随机值
6
除了上面我们需要掌握的之外,更重要的我们要知道,数组名的含义;我们在学习过程中,似乎经常遇到“首元素地址”,那么数组名究竟是如何理解的呢?我为大家整理了一下总结:
数组名是首元素地址,但是有两个例外:
- sizeof(数组名),计算的是整个数组的大小;
- &数组名,取的是整个数组的地址。
int a[] = {1,2,3,4}; printf("%d\n",sizeof(a)); printf("%d\n",sizeof(a+0)); printf("%d\n",sizeof(*a)); printf("%d\n",sizeof(a+1)); printf("%d\n",sizeof(a[1])); printf("%d\n",sizeof(&a)); printf("%d\n",sizeof(*&a)); printf("%d\n",sizeof(&a+1)); printf("%d\n",sizeof(&a[0])); printf("%d\n",sizeof(&a[0]+1));
sizeof用于变量,计算整个数组的大小,单位是字节。
那我们现在依次来分析:
int a[] = {1,2,3,4};//4个元素,每个元素是int类型(4个字节)
printf("%d\n",sizeof(a));//16
//数组名a单独放在sizeof内部,数组名表示整个数组,计算的是整个数组的大小
//单位是字节,16字节
a[ ] 这个数组整个是占16个字节,我们看这个sizeof(a) ,而这里sizeof(数组名)表示的是整个数组,其计算的是整个数组的大小,单位是字节。
运行结果:
16
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a+0));
这里的sizeof(a+0)中a并没有单独放到sizeof内部,也没有取地址,所以数组名表示首元素地址。sizeof(a+0)中a+0还是首元素地址,地址的大小是4/8个字节。
int a[] = {1,2,3,4};
printf("%d\n",sizeof(*a));
a并没有单独放到sizeof内部,也没有取地址,所以数组名表示首元素地址。sizeof(*a)中*a就是指向首元素地址解引用,所以就是首元素,大小是4个字节。也可以理解为:
*a == *(a+0)== a [ 0 ]
所以运行结果:
4
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a+1));
这里的sizeof(a+1)中a并没有单独放到sizeof内部,也没有取地址,所以数组名表示第二个元素嗯的地址,是地址,地址的大小是4/8个字节。
a + 1 == &a[ 1 ]
所以运行结果:
4 、8
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a[1]));
a[1]就是数组的第二个元素,这里计算的就是第二个元素的大小,单位是字节,所以是4字节。
所以运行结果:
4
int a[] = {1,2,3,4};
printf("%d\n",sizeof(&a));
&a是取出数组的地址,但是数组的地址也是地址,是地址就是4/8个地址。
数组的地址 和 数组首元素的地址 的本质区别:
是类型的区别,并非大小的区别。
所以运行结果:
4、8
int a[] = {1,2,3,4};
printf("%d\n",sizeof(*&a));
sizeof(*&a)这个是数组指针类型,对数组指针类型解引用,访问的数组的大小,所以是4个字节。
sizeof(*&a)--- sizeof(a)
int a[] = {1,2,3,4};
printf("%d\n",sizeof(&a+1));
sizeof(&a+1)中&a是地址,&a+1仍然是地址,是地址就是4/8字节。
所以运行结果:
4、8
int a[] = {1,2,3,4};
printf("%d\n",sizeof(&a[0]));
sizeof(&a[0])中&a[0]是首元素的地址,计算的是地址的大小4/8个字节。
所以运行结果:
4、8
int a[] = {1,2,3,4};
printf("%d\n",sizeof(&a[0]+1));
sizeof(&a[0]+1)中&a[0]是首元素地址,所以 sizeof(&a[0]+1) 是第二个元素的地址,大小为4/8个字节。
第二个元素的地址表达方式:
- &a[ 1 ]
- &a[ 0 ] + 1
- a+1
char arr[] = {'a','b','c','d','e','f'};
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));
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
数组名arr单独放在sizeof内部,计算的是整个数组大小,单位是字节,所以大小是6个字节。
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr+0));
数组名arr没有单独放在sizeof内部,所以arr是首元素地址,arr+1是第二个元素的地址,是地址就是4/8个字节。
指针类型的大小和类型无关,不管什么类型的之争变量,大小都是4/8个字节。指针变量是用来存放地址的,地址存放需要多大的空间,指针变量的大小就是几个字节。
32位环境下,地址是32个二进制位,需要4个字节,所以指针变量的大小就是4个字节。
64位环境下,地址是64个二进制位,需要8个字节,所以指针变量的大小就是8个字节。
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(*arr));
sizeof(*arr)中*arr中arr没有单独放在sizeof内部,也没有取地址,所以这个arr是首元素的地址,所以*arr是首元素。大小为1个字节。
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr[1]));
sizeof(arr[1])就是第二个元素,也就是1个字节。
char arr[] = {'a','b','c','d','e','f'}
printf("%d\n", sizeof(&arr));
sizeof(&arr)中&arr是数组的地址,就是4/8个字节。
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(&arr+1));
sizeof(&arr+1)中&arr+1,是跳过数组后的地址,是地址就是4/8个字节。
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(&arr[0]+1));
sizeof(&arr[0]+1)中&arr[0]+1就是第二个元素的地址,是地址就是4/8字节。
char arr[] = {'a','b','c','d','e','f'};
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));
strlen求字符串长度,统计的是在字符串中 ' \0 '之前出现字符的个数。
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));
注意这个arr中并没有 ' \0 ',所以并不知道什么时候会遇见 ' \0 '。arr是首元素地址,所以大小是随机值。
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr+0));
注意这个arr+0中arr是首元素地址,所以并不知道什么时候会遇见 ' \0 '。所以大小是随机值。
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(*arr));
arr是首元素地址,*arr就是首元素,就是字符 ' a ',在ASCII码值中,字符' a '就是97。而在strlen的角度,认为传参进去的 ' a '97就是地址,直接进行访问,就是非法访问,所以会报错。
size_t strlen ( const char * str ) ;
所以strlen需要传入一个地址
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr[1]));
同理上面的,arr[ 1 ]就是字符' b '98,在strlen的角度,认为传参进去的 ' a '97就是地址,直接进行访问,就是非法访问,所以会报错。
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(&arr));
strlen(&arr)中&arr是arr的地址,仍然因为这个数组中并没有' \0 ',所以是随机值。
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(&arr+1));
与上面的同理,&arr+1也是同样的随机值。
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(&arr[0]+1));
同理这个我们,&arr[ 0 ]+1也是随机值。
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));
我们要注意:字符串中默认最后一个是有 ' \0 '的。
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
arr单独放在sizeof中,表示的整个数组的大小,加上' \0 '其实就是7个字节。
char arr[] = "abcdef";
printf("%d\n", sizeof(arr+0));
数组名arr没有单独放在sizeof内部,所以arr是首元素地址,arr+1是第二个元素的地址,是地址就是4/8个字节。
char arr[] = "abcdef";
printf("%d\n", sizeof(*arr));
arr没有单独使用在sizeof中,所以arr是首元素地址,所以*arr是首元素,所以大小是1个字节。
char arr[] = "abcdef";
printf("%d\n", sizeof(arr[1]));
第二个元素,大小是1个字节。
char arr[] = "abcdef";
printf("%d\n", sizeof(&arr));
&arr取出数组的地址,数组的地址也是地址,是地址就是4/8个字节。
char arr[] = "abcdef";
printf("%d\n", sizeof(&arr+1));
&arr+1也是地址,是地址就是4/8个字节。
char arr[] = "abcdef";
printf("%d\n", sizeof(&arr[0]+1));
第二个元素的地址,是地址就是4/8个字节。
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));
char arr[] = "abcdef";
printf("%d\n", strlen(arr));
arr没有取地址,也没有单独放在sizeof内部,所以数组名arr是首元素的地址,所以加上字符串最后的' \0 ',所以大小是6。
char arr[] = "abcdef";
printf("%d\n", strlen(arr+0));
rr没有取地址,也没有单独放在sizeof内部,所以数组名arr是首元素的地址,arr+0仍然是加上字符串最后的' \0 ',所以大小是6。
char arr[] = "abcdef";
printf("%d\n", strlen(*arr));
arr是首元素地址,*arr是首元素,因为strlen需要传入一个地址,所以会报错。
char arr[] = "abcdef";
printf("%d\n", strlen(arr[1]));
arr[ 1 ]是第二个元素,所以会报错。
char arr[] = "abcdef";
printf("%d\n", strlen(&arr));
&arr地址,取出arr的地址,从从前往后统计知道' \0 ',所以是6个字节。
char arr[] = "abcdef";
printf("%d\n", strlen(&arr+1));
取arr的地址,跳过arr的地址,我们并不知道什么时候会遇到' \0 ',所以是随机值。
char arr[] = "abcdef";
printf("%d\n", strlen(&arr[0]+1));
arr[ 0 ]+1的地址,就是第二个元素地址,所以大小是5个字符。
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));
这里注意我们不是单纯地将"abcdef"放到*p中去的,一定有一块常量字符串被存储起来,假设这块的起始地址是0x0012ff40,而还有一块内存p存储着0x0012ff40,其指向"abcdef"。
char *p = "abcdef";
printf("%d\n", sizeof(p));
计算的是指针变量的大小,相当于地址,就是4/8。
char *p = "abcdef";
printf("%d\n", sizeof(p+1));
假设p是0x0012ff40,所以p+1,指向0x0012ff41,p+1仍然是一个地址,是地址就是4/8个字节。
char *p = "abcdef";
printf("%d\n", sizeof(*p));
*p是char*的指针,解引用访问一个字节。
*p == ' a '
char *p = "abcdef";
printf("%d\n", sizeof(p[0]));
仍然是字符' a ' ,所以是1个字节。
p[0] == *(p+0)
char *p = "abcdef";
printf("%d\n", sizeof(&p));
这里就是相当于二级指针char**,是地址就是4/8个字节。
char *p = "abcdef";
printf("%d\n", sizeof(&p+1));
取地址+1,仍然是地址,是地址就是4/8个字节。
char *p = "abcdef";
printf("%d\n", sizeof(&p[0]+1));
&p[ 0 ]就是第一个地址,&p[ 0 ]+1就是第二个元素的地址,所以就是5个字节。
char *p = "abcdef";
printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
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);
char *p = "abcdef";
printf("%d\n", strlen(p));
默认字符串有' \0 ',所以是6个字节。
char *p = "abcdef";
printf("%d\n", strlen(p+1));
跳过一个字节,所以是5个字节。
char *p = "abcdef";
printf("%d\n", strlen(*p));
*p指向的是一个字符' a ' ,与前面的相同,strlen需要传入一个地址,所以会报错。
char *p = "abcdef";
printf("%d\n", strlen(p[0]));
p[ 0 ]相当于就是第一个字符' \0 ',同样也会报错。
char *p = "abcdef";
printf("%d\n", strlen(&p));
&p取的是地址,此时并不知道什么时候会出现' \0 ',所以是一个随机值。
char *p = "abcdef";
printf("%d\n", strlen(&p+1));
与上面的同理,此时并不知道什么时候会出现' \0 ',所以是随机值。
char *p = "abcdef";
printf("%d\n", strlen(&p[0]+1);
&p[ 0 ] +1是第二个字符' b ',所以从字符' b '开始是5字符。