什么是strlen?什么是sizeof?
1. strlen:通俗的来讲是一个用来计算字符串长度的库函数。
例如:
int main() { char arr[] = "abcdes"; int len = strlen(arr); printf("字符串的长度是:%d\n", len); return 0; }
运行结果:
由图可知strlen是计算一个不包括’\0’在内的字符串的长度的总合。
2. sizeof:sizeof操作符以字节形式给出了其操作数的存储大小。但sizeof我们要提到特殊的一点"当数组名单独放在sizeof中的时候,该数组名表示的是整个数组"例如:
int main() { int arr[] = { 1,3,5,6,7 }; int sz = sizeof(arr) / sizeof(arr[0]); printf("数组的长度是:%d\n", sz); return 0; }
运行结果:
数组的特殊情况
前面我们提到了sizeof与strlen的用法,下面我们还要学习一点关于数组的一些特殊情况。
①:当数组名单独放在sizeof里面的时候,该数组名表示整个数组
②:当&arr(数组名),此时也表示的是整个数组
③:在其除上面俩种特殊情况以外,数组名都表示的首元素地址
④:特别强调,二维数组的数组名表示第一行数组
来看看一些经典笔试题来帮助我们理解指针在strlen与sizeof上不同的含义。
指针和数组笔试题解析
sizeof与一维数组
//一维数组 int main() { 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)); return 0; }
题目分析:
printf("%d\n", sizeof(a));
前面我们提到过数组名单独放在sizeof中表示的是整个数组
因此我们可以知道,这个sizeof计算的是整个数组大小,因此为16个字节
printf("%d\n", sizeof(a + 0));
数组名没有单独放在sizeof内部因此,该数组名表示的是首元素地址
首元素地址加0还是首元素的地址,是地址就是4或8个字节
printf("%d\n", sizeof(*a));
同理,数组名没有单独放在sizeof内部因此,该数组名表示的是首元素地址,对数组名进行解引用表示的仍然是首元素,因此是4个字节
printf("%d\n", sizeof(a + 1));
同理,数组名没有单独放在sizeof内部因此,该数组名表示的是首元素地址,首元素地址加一跳过四个字节,表示第二个元素的地址,因此仍然是4个字节
printf("%d\n", sizeof(a[1]));
a[1]就是数组的第二个元素,这里计算的就是第二个元素的大小,故为4个字节
printf("%d\n", sizeof(&a));
&a表示的是数组的地址,是地址就是4个或者8个字节(取决于x86还是x64)
printf("%d\n", sizeof(*&a));
对数组指针解引用访问一个数组的大小
sizeof(*&a) – sizeof(a);
printf("%d\n", sizeof(&a + 1));
&a表示数组的地址,加一跳过整个数组,但仍是地址,是地址就是4或8个字节
printf("%d\n", sizeof(&a[0]));
&a[0]表示的是一个元素的地址,因此是4或8个字节
printf("%d\n", sizeof(&a[0] + 1));
取出来首元素地址,首元素地址加一就是第二个元素的地址,是地址就是4或者8个字节
运行结果:
sizeof与字符数组
//字符数组 int main() { 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)); return 0; }
题目分析:
printf("%d\n", sizeof(arr));
数组名单独放在sizeof中表示整个数组,因此该sizeof计算的是整个数组的大小,字节为6(char类型为一个字节)
printf("%d\n", sizeof(arr + 0));
数组名没有单独放在sizeof中因此数组名表示首元素地址,首元素地址加0还是首元素地址,是地址就是4/8个字节
printf("%d\n", sizeof(*arr));
数组名没有单独放在sizeof中,因此数组名表示首元素地址,对首元素地址进行解引用表示首元素,因此为1个字节
printf("%d\n", sizeof(arr[1]));
数组名没有单独放在sizeof中表示的首元素地址,因此arr[1]表示数组中第二个元素,故为1个字节
printf("%d\n", sizeof(&arr));
&arr表示数组的地址,是地址就是4或8个字节
printf("%d\n", sizeof(&arr + 1));
&arr表示整个数组的地址,&arr+1表示跳过整个数组,但仍然是地址,是地址就是4/8个字节
printf("%d\n", sizeof(&arr[0] + 1));
&arr[0]表示取数组中第一个元素的地址,&arr[0] + 1表示第一个元素的地址加1,表示第二个元素的地址,是地址就是4/8个字节
运行结果:
strlen与字符数组
//strlen求字符数组 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));
题目分析:
printf("%d\n", strlen(arr));
数组名表示首元素地址,因此计算的是该数组的长度,strlen是从所传的参数计算长度一直到’\0’为止,因为该字符数组没有’\0’故而该结果是一个随机值(随机值>=6)。
printf("%d\n", strlen(arr+0));
随机值>=6(没有/0)arr就是首元素地址,arr+0还是首元素地址。
printf("%d\n", strlen(*arr));
因*arr表示的是该数组首元素的值,因此传给strlen的就是首元素的ASCII值,故而造成了非法访问(strlen是用来结收地址),因此无法运行结果
printf("%d\n", strlen(arr[1]));
同理,arr[1]也是该数组首元素的值,传给strlen的也是该值的ASCII值,因此非法访问,导致无法运行出结果
printf("%d\n", strlen(&arr));
将数组的地址传给strlen,实际上把数组的地址转换成了该数组的首元素地址,又因为该数组没有’\0’,因此结果是一个随机值
printf("%d\n", strlen(&arr+1));
将整个数组的地址+1,故而跳过了整个数组,因此不知道后面什么时候会碰到’\0’,因此也是随机值
printf("%d\n", strlen(&arr[0]+1));
&arr[0]就是找到数组的首元素的地址,首元素的地址+1就是数组中第二个元素的地址,又因为数组中没有’\0’因此也是一个大于5的随机值
这个的运行结果要屏蔽掉第三个和第四个(无法访问):
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; }
题目分析:
printf("%d\n", sizeof(arr));
数组名单独放在sizeof里面表示整个数组,因此计算的就是整个数组的大小,一个字符是一个字节,因此为7个字节。
printf("%d\n", sizeof(arr + 0));
数组名没有单独放在sizeof内部,因此数组名表示首元素的地址,首元素地址+0还是首元素的地址,是地址就是4/8个字节
printf("%d\n", sizeof(*arr));
对数组名进行解引用,表示首元素,首元素是字符,因此为1个字节
printf("%d\n", sizeof(arr[1]));
arr[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));
&arr[0]表示字符串首字符的地址,首字符的地址加1仍然是地址,是地址就是4/8个字节
运行结果:
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; }
题目分析:
printf("%d\n", strlen(arr));
数组名表示首元素地址,在字符串的末尾有’\0’,一直找到’\0’,因此长度为6
printf("%d\n", strlen(arr + 0));
数组名表示首元素地址,首元素地址加0还是首元素地址,因此长度仍然为6
printf("%d\n", strlen(*arr));
对数组解引用,*arr表示首元素,在将首元素转换成了ASCII值传给了strlen,然而我们前面提到了,strlen只能接收地址,因此造成了非法访问,无法得到结果
printf("%d\n", strlen(arr[1]));
arr[1]同样表示字符串中的第二个元素,将它转换成了ASCII值传给了strlen,故而非法访问,无法得到结果
printf("%d\n", strlen(&arr));
&arr表示的是整个字符串的地址,然后&arr会被转换成首元素地址,因此从首元素一直找到’\0’一共有六个字符,因此长度为6
printf("%d\n", strlen(&arr + 1));
&arr表示的地址,因此&arr + 1跳过了整个字符串,从这个地址一直找到’\0’停止下来,但因为不知道什么时候会找到’\0’,因此结果是一个随机值
printf("%d\n", strlen(&arr[0] + 1));
找到数组的第一个字符的地址然后+1,即数组的第二个元素的地址往后找到’\0’为止,因此结果为5
运行结果:因第三个和第四个无法得到结果,这是省略了这俩个后的运行结果
sizeof与字符指针
int main() { //字符指针与sizeof 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; }
题目分析:
printf("%d\n", sizeof(p));
p表示指针变量,因此计算的是指针变量的大小,指针变量的大小只有4/8个字节
printf("%d\n", sizeof(p + 1));
p + 1表示字符串中第二个字符的地址,是地址就还是4/8个字节
printf("%d\n", sizeof(*p));
*p表示的是字符串中的首字符,字符的大小是1个字节,因此为1字节
printf("%d\n", sizeof(p[0]));
p[0]等价于 *(p + 0)表示的是字符串中第一个字符,因此为1字节
printf("%d\n", sizeof(&p));
取出来的是地址它也是4/8个字节
printf("%d\n", sizeof(&p + 1));
&p是首字符的地址,&p +1是第二个字符的地址,是地址就是4/8个字节
printf("%d\n", sizeof(&p[0] + 1));
&p[0]表示的是首字符的地址 ,+1是第二个字符的地址,是地址就是4/8个字节
运行结果:
strlen与字符指针
int main() { //字符指针与strlen 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)); return 0; }
题目分析:
printf("%d\n", strlen(p));
p表示首字符的地址,从首字符一直往后找直到碰到’\0’为止,故为6
printf("%d\n", strlen(p + 1));
p表示首字符的地址,p + 1表示第二个字符的地址,故而为5
printf("%d\n", strlen(*p));
*p表示首字符的值,然后转换成ASCII的值传给strlen,我们前面提到过strlen只能接收指针和地址,因此会非法访问无法得到结果
printf("%d\n", strlen(p[0]));
同理,p[0]表示首字符,然后转换成ASCII的值传给strlen,因此会非法访问无法得到结果
printf("%d\n", strlen(&p));
&p相当与一个存放了一级指针p的地址,因此从这个地址往后找’\0’,直到找到为止,因此为随机值
printf("%d\n", strlen(&p + 1));
同理&p相当与一个存放了一级指针p的地址,往后加一相当与&p往后的一个地址,因此从这个地址往后找’\0’,直到找到为止,因此为随机值
printf("%d\n", strlen(&p[0] + 1));
&p[0]相当与首字符的地址,然后 + 1相当于第二个字符的地址,一直往后找,找到’\0’为止,因此为5。
运行结果:因第三个和第四个无法得到结果,这是省略了这俩个后的运行结果
sizeof与二维数组
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; }
题目分析:
printf("%d\n", sizeof(a));
数组名单独放在sizeof中表示整个数组,因此计算的是整个数组的字节,数组中有12个整型因此为4个字节.
printf("%d\n", sizeof(a[0][0]));
a[0][0]相当与数组中的第一个元素,计算第一个元素的大小,因此为4个字节
printf("%d\n", sizeof(a[0]));
a[0]相当于第一行的数组名,第一行的数组名单独放在sizeof内部相当于计算这数组的第一行的大小,因此为16个字节
printf("%d\n", sizeof(a[0] + 1));
数组名并非单独放在sizeof内部,因次a[0]表示第一行第一个元素的地址,a[0] + 1 -->等价于 a[0][1]因此为4个字节
printf("%d\n", sizeof(*(a[0] + 1)));
(a[0] + 1)表示的是数组第一行第一个元素的地址,*(a[0] + 1)表示第一行第一个元素,因此为4个字节
printf("%d\n", sizeof(a + 1));
数组名并非单独放在sizeof内部,因此a表示第一行的数组地址,a + 1表示第二行数组的地址,因此计算的是整个第二行的数组的地址,是地址就是4/8个字节
printf("%d\n", sizeof(*(a + 1)));
a + 1表示第二行数组的地址,因此*(a + 1)表示的是整个第二行,故是16个字节
printf("%d\n", sizeof(&a[0] + 1));
&a[0]是数组第一行的地址,第一行的地址加一就是第二行的地址,是地址就是4/8个字节
printf("%d\n", sizeof(*(&a[0] + 1)));
&a[0]是数组第一行的地址,第一行的地址加一就是第二行的地址,*(&a[0] + 1)就是表示的是整个第二行,计算的就是第二行的大小,因此为16个字节
printf("%d\n", sizeof(*a));
*a表示数组名,也就算的是整个第一行的,因此为16个字节
printf("%d\n", sizeof(a[3]));
在我们看来a[3]是越界的情况,实际上sizeof并不会真的取运行这个,也就是说sizeof并不会真的取查看它是否越界,而是根据它的类型来直接访问它的值,也就是算的整个一行的,因此为16个字节
好了,今天的内容就讲到这里了,如果有讲的不好地方甚至错误的地方也 希望各位帮忙指出一下,作者都会虚心接受努力修改的!