目录
前言
一、sizeof和strlen的简单介绍
1.1 sizeof
1.2 strlen
二、一维数组
三、字符数组
3.1 字符数组结尾未加'\0'
3.2 字符数组结尾加'\0'
3.3 字符串常量
四、二维数组
总结
在前面我们已经介绍了指针初阶和进阶相关的知识,接下来这篇文章主要来做一些指针和数组相关的笔试题。
sizeof是C语言的一种单目操作符,如C语言的其他操作符++、--等。它并不是函数。sizeof操作符以字节形式给出了其操作数的存储大小。操作数可以是一个表达式或括在括号内的类型名。操作数的存储大小由操作数的类型决定。
用法1:sizeof用于数据类型
sizeof使用形式:sizeof(type),数据类型必须用括号括住。如sizeof(int)。
用法2:sizeof用于变量
sizeof使用形式:sizeof(var_name)或sizeof var_name。
变量名可以不用括号括住。如sizeof (var_name),sizeof var_name等都是正确形式。带括号的用法更普遍,可读性更好,大多数程序员采用这种形式。
注意:sizeof操作符不能用于函数类型,不完全类型或位字段。不完全类型指具有未知存储大小的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型等。
sizeof计算的结果:sizeof操作符的结果类型是size_t,它在头文件中typedef为unsigned int类型。sizeof计算出来的结果是变量或类型所占内存的大小,单位是字节。
C语言中的strlen函数是用于字符串的一个函数,用来使用统计字符串中'\0'之前出现的字符个数(不包含'\0'),对于strlen来说'\0'就是字符串的结束标志。使用strlen函数要包含头文件
。
#include
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;
}
输出:
接下来我们来分析一下这段代码:
在这段代码中,我们首先是创建了一个有4个整形数据的整型数组。一个整型数据的大小就是4个字节。
对于printf("%d\n", sizeof(a));sizeof内部放的是数组名,一般情况下数组名代表的是数组首元素的地址,但是在sizeof内部就是一个特例,代表的是整个数组,所以计算出来结果会是整个数组的大小。所以这里结果是16个字节.
对于printf("%d\n", sizeof(a+0));sizeof内部并不是单独放数组名,也没有&,所以数组名a就是数组首元素的地址,a+0还是数组首元素的地址,是地址大小就是 4/8 个字节。
对于printf("%d\n", sizeof(*a));a是数组名就是首元素的地址,对首元素地址解引用,拿到的就是首元素的值,首元素是整型,所以大小就是4个字节。
对于printf("%d\n", sizeof(a + 1));a是首元素地址,加上1就会向后偏移4个字节的大小,指向下一个元素,即第二个元素,所以a+1就是第二个元素的地址,是地址,大小就是4/8个字节。
对于printf("%d\n", sizeof(a[1]));a[1]表示的是第一个元素,就是一个整型变量,所以一个整型变量的大小就是4个字节。
对于printf("%d\n", sizeof(&a));这条语句:&a取出的是整个数组的地址,数组地址也是个地址,是地址大小就是4/8个字节。
对于printf("%d\n", sizeof(*&a));&a取出的是整个数组的地址,*&a是数组指针的解引用,访问一个数组的大小,所以大小是16个字节。
对于printf("%d\n", sizeof(&a + 1));&a取出的是整个数组,对整个数组进行加减整数的操作,加1会跳过整个数组。但是&a+1总归还是个地址,所以大小就是4/8个字节。
对于printf("%d\n", sizeof(&a[0]));a[0]是数组的首元素,&a[0]就是取出首元素的地址,地址的大小就是4/8个字节。
对于printf("%d\n", sizeof(&a[0] + 1));&a[0]就是取出第一个元素的地址,对第一个元素地址加1就会指向下一个元素,也就是第二个元素的地址,所以大小就是4/8个字节。
#include
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;
}
输出:
接下来我们来分析一下这段代码:
在这段代码中,我们首先是创建了一个有6个字符数据的字符数组。一个字符数据的大小是1个字节。
对于printf("%d\n", sizeof(arr));sizeof内部单独放数组名,计算的是整个数组的大小,所以就是6个字节。
对于printf("%d\n", sizeof(arr + 0));arr是数组首元素地址,arr+0还是首元素地址,是地址大小就是4/8个字节。
对于printf("%d\n", sizeof(*arr));arr是首元素地址,对首元素地址解引用得到的就是首元素,首元素的大小就是1个字节。
对于printf("%d\n", sizeof(arr[1]));arr[1]就是数组第二个元素,大小就是1个字节。
对于printf("%d\n", sizeof(&arr));&arr取出的是整个数组的地址,是地址大小就是4/8个字节。
对于printf("%d\n", sizeof(&arr + 1));&a取出的是整个数组,对整个数组进行加减整数的操作,加1会跳过整个数组。但是&a+1总归还是个地址,所以大小就是4/8个字节。
对于printf("%d\n", sizeof(&arr[0] + 1));&arr[0]取出的是首元素地址,加一会跳过一个元素,也就是指向第二个元素,就是第二个元素的地址,地址的大小就是4/8个字节。
#include
#include
int main()
{
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));//随机数
return 0;
}
输出结果:
接下来我们分析一下这段代码:
这段代码我们创建了一个有6个字符的char类型的数组,结尾未加'\0'。
对于printf("%d\n", strlen(arr));输出会是随机值,arr是数组名,但是没有放在sizeof内部,也没&,arr就是首元素的地址,strlen得到arr后,从arr数组首元素的地方开始计算字符串的长度,直到直到\0,但是arr数组中没有\0,arr内存的后边是否有\0,在什么位置是不确定的,所以\0之前出现了多少个字符是随机的。
对于printf("%d\n", strlen(arr + 0));输出会是随机数, arr是首元素地址,arr+0还是首元素地址。因为数组arr中'\0'的位置不确定,所以还是随机数。
对于printf("%d\n", strlen(*arr)); arr是首元素地址,*arr就是字符'a',字符a的ASCLL值是97,strlen就把97当作地址进行访问,会非法访问内存,所以这里代码是错误的。
对于printf("%d\n", strlen(arr[1]));arr[1]是数组的第二个元素就是b,b的ASCLL值是98,strlen就会把98当作地址进行访问,也会造成非法访问内存,所以这里代码是错误的。
对于printf("%d\n", strlen(&arr));输出会是随机值,&arr是数组的地址,数组的地址也是指向数组起始位置,因为'\0'位置不确定,所以输出会是随机数,和第一个案例一样。
对于printf("%d\n", strlen(&arr + 1));这里取出整个数组的地址并且加1,会跳过整个数组,但是因为'\0'的位置不确定,输出也会是个随机数,但是因为加1跳过了整个数组,所以这个随机数会比strlen(arr)的这个随机数小6。
对于printf("%d\n", strlen(&arr[0] + 1));这里取出首元素地址并且加1,会跳过1个元素,指向第二个元素,因为'\0'位置不确定,所以又会是个随机值,但是因为跳过了第一个元素,所以这个随机数会比 strlen(arr)的这个随机数小1。
#include
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;
}
运行结果:
接下来我们分析一下这段代码:
这段代码我们创建了一个有7个元素的字符数组,用双引号引起的字符串末尾会默认添加上'\0',所以这个字符数组会有7个元素。
对于printf("%d\n", sizeof(arr));数组名单独放在sizeof内部,这时候数组名代表整个数组,计算的是整个数组的大小,所以大小是7个字节。
对于printf("%d\n", sizeof(arr + 0));数组名不是单独放在sizeof内部,arr代表的就是首元素地址,arr+0代表的也是首元素地址,这是个地址,大小就是4/8个字节。
对于printf("%d\n", sizeof(*arr));arr是首元素地址,对首元素地址解引用得到的就是首元素,首元素的大小就是1个字节。
对于printf("%d\n", sizeof(arr[1]));arr[1]是首元素,首元素是char类型,大小就是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));这里取出的是首元素的地址,加1就会指向第二个元素,也就是第二个元素的地址,是地址大小就是4/8个字节。
#include
#include
int main()
{
char arr[] = "abcdef";
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;
}
运行结果 :
接下来我们分析一下这段代码:
这段代码我们创建了一个有7个元素的字符数组,用双引号引起的字符串末尾会默认添加上'\0',所以这个字符数组会有7个元素。而strlen函数是以'\0'为结束标志的,不包含'\0'。
对于printf("%d\n", strlen(arr));arr就是首元素的地址,strlen得到arr后,从arr数组首元素的地方开始计算字符串的长度,直到遇到\0,而用双引号初始化的字符串末尾默认会加上'\0',所以这里会输出6。
对于printf("%d\n", strlen(arr + 0));arr是首元素地址,arr+0还是首元素地址,strlen从首元素的位置开始算,直到遇到'\0',所以这里结果还是6.
对于printf("%d\n", strlen(*arr));arr是首元素地址,*arr就是首元素,也就是字符'a',a的ASCLL值是97,strlen就把97当作地址进行访问,会非法访问内存,所以这里代码是错误的。
对于printf("%d\n", strlen(arr[1]));arr[1]是数组的第二个元素就是b,b的ASCLL值是98,strlen就会把98当作地址进行访问,也会造成非法访问内存,所以这里代码是错误的。
对于printf("%d\n", strlen(&arr));&arr是数组的地址,数组的地址也是指向数组起始位置,从起始位置开始统计字符长度,所以这里字符串长度也是6.
对于printf("%d\n", strlen(&arr + 1));&arr会取出整个数组的地址,&arr+1会跳过整个数组,跳过整个数组后会从'\0'后面开始统计字符个数,但是这时我们并不知道内存中'\0'的位置,所以结果是个随机数。
对于printf("%d\n", strlen(&arr[0] + 1));&arr[0]会取出第一个元素的地址,&arr[0]+1会跳过一个元素,指向第二个元素,从第二个元素开始往后统计字符个数,所以结果是5。
#include
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
return 0;
}
运行结果:
接下来我们来分析一下这段代码:
这段代码我们创建了一个常量字符串,并且让char类型指针p指向字符串的首元素。
对于 printf("%d\n", sizeof(p));p是一个字符指针,也就是一个地址,地址的大小就是4/8个字节。
对于printf("%d\n", sizeof(p + 1));p指向字符串的首元素,p+1就是指向字符串的第二个元素,也就是字符串第二个元素的地址,是地址,大小就是4/8个字节。
对于printf("%d\n", sizeof(*p));p指向字符串第一个元素,*p拿到的就是第一个字符'a',a是char类型的,所以大小就是一个字节。
对于printf("%d\n", sizeof(p[0]));p[0]就相当于*(p+0)拿到的都是第一个元素,就是字符a,所以大小就是1个字节。
对于printf("%d\n", sizeof(&p));p是一个指针变量,&p取出的是p的地址,是地址,大小就是4/8个字节。
对于printf("%d\n", sizeof(&p + 1));&p就是取出p的地址,&p+1就是使p跳过4/8个字节,指向p的下一个地址,是地址大小就是4/8个字节。
对于printf("%d\n", sizeof(&p[0] + 1));&p[0]就是就是取出字符串第一个元素的地址,&p[0]+1就是第二个元素的地址,是地址大小就是4/8个字节。
#include
int main()
{
char* p = "abcdef";
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));//6
printf("%d\n", strlen(&p + 1));//随机数
printf("%d\n", strlen(&p[0] + 1));//5
return 0;
}
接下来我们分析一下这段代码:
这段代码我们创建了一个常量字符串,并且让char类型指针p指向字符串的首元素。
对于printf("%d\n", strlen(p));p是字符串常量的首地址,strlen得到p后,从p的位置开始计算字符串的长度,直到遇到\0,而用双引号初始化的字符串末尾默认会加上'\0',所以这里会输出6。
对于 printf("%d\n", strlen(p + 1));p+1会指向字符串的第二个元素,从第二个元素开始往后统计字符个数,字符个数就是5个。
对于printf("%d\n", strlen(*p));p是字符串首元素地址,*p就是首元素,也就是字符'a',a的ASCLL值是97,strlen就把97当作地址进行访问,会非法访问内存,所以这里代码是错误的。
对于printf("%d\n", strlen(p[0]));这条语句跟上一条是一样的,是错误代码。
对于printf("%d\n", strlen(&p + 1));&p+1会跳过整个字符串,从'\0'之后的位置开始统计字符个数,而我们并不知道内存中'\0'的位置,所以这里会是随机值。
对于printf("%d\n", strlen(&p[0] + 1));&p[0]是字符串首元素地址,&p[0]+1就会指向第二个字符,strlen就会从第二个元素开始向后统计字符个数,直到遇到'\0',所以这里结果会是5。
#include
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;
}
运行结果:
接下来我们来分析一下这段代码:
这段代码我们创建了一个三行四列的整型二维数组。
对于printf("%d\n", sizeof(a));数组名a单独放在sizeof内部,所以计算的是整个数组的大小,所以就是48个字节。
对于printf("%d\n", sizeof(a[0][0]));a[0][0]是数组首元素,数组是int型的,所以大小就是4个字节。
对于printf("%d\n", sizeof(a[0]))把二维数组的每一行看做一维数组的时候,a[0]是第一行的数组名,第一行的数组名单独放在sizeof内部,计算的是第一行的总大小,所以大小就是16个字节。
对于printf("%d\n", sizeof(a[0] + 1));a[0]虽然是第一行的数组名,但是并非单独放在sizeof内部,a[0]作为第一行的数组名并非表示整个第一行这个数组,a[0]就是第一行首元素的地址,a[0]--> &a[0][0] - int*,a[0]+1,跳过一个int,是a[0][1]的地址 4/8字节。
对于printf("%d\n", sizeof(*(a[0] + 1)));a[0]是第一行数组名,表示第一行首元素地址,a[0]+1就是第一行第二个元素的地址,对地址解引用,就能拿到地址指向的元素,即*(a[0]+1)就相当于a[0][1],就是数组的第二个元素,元素是整型,大小就是4个字节。
对于printf("%d\n", sizeof(a + 1));数组名a不是单独放在sizeof内部,所以a表示的就是数组首元素的地址,在二维数组中就是首行的地址,加1就跳过1行元素,指向第二行,a+1就是第二行元素的地址,是地址大小就是4/8个字节。
对于printf("%d\n", sizeof(*(a + 1)));由上一段分析可知,a+1指向的就是第二行元素的地址,解引用后就相当于拿到第二行元素,所以大小就是16个字节。
对于printf("%d\n", sizeof(&a[0] + 1));&a[0]就是取出第一行的地址,&a[0]+1就是拿到第二行的地址,是地址大小就是4/8个字节。
对于printf("%d\n", sizeof(*(&a[0] + 1)));由上一段代码分析可知:&a[0]+1就是拿到第二行的地址,解引用就是拿到第二行的元素,所以大小就是16个字节。
对于printf("%d\n", sizeof(*a));数组名a不是单独放在sizeof内部,所以数组名表示数组首元素地址,在二维数组中代表的就是第一行的地址,对第一行地址解引用就拿到第一行元素,所以大小就是16个字节。
对于printf("%d\n", sizeof(a[3]));a[3]是二维数组的第4行,虽然没有第四行,但是类型能够确定,大小就是确定的。大小就是一行的大小,所以就是16个字节。
数组名的意义:
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表示首元素的地址。