C语言学习
指针基础
友情链接:C语言专栏
前言:
经过前面指针基础、指针进阶1、指针进阶2的学习,咱们对指针已经有了深入的理解,那咱们就来看看指针和数组相关的一些笔试题!
指针和数组笔试题解析
一维数组
看一下下面代码输出都是什么?
#include<stdio.h>
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 + 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;
}
咱们可以自己先试着来分析分析。
结果:
解释:
#include<stdio.h>
int main()
{
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a)); //数组名遇见sizeof就是数组元素总大小,即为16(字节)
printf("%d\n", sizeof(a + 0));//看起来与上一个一样,但是,两者还是有区别:
//单是数组名遇见sizeof的话,就是数组元素总大小;但是这里数组名已经和0进行的算术运算
//即为首元素地址+0,为首元素地址,故输出为4(32位机器)。
printf("%d\n", sizeof(*a)); //数组名为首元素地址,*解引用操作后为4(int型数据),输出为4
printf("%d\n", sizeof(a + 1));//这个和第二个有点像,首元素地址+1;为第二个元素地址,故大小为4;
printf("%d\n", sizeof(a[1])); //第二个元素大小,即为4;
printf("%d\n", sizeof(*(a + 1)));//a + 1数组第二个元素的地址;*(a + 1)数组第二个元素;
//那看起来与a[1]的意思是一样的,其实,a[1]与*(a + 1)是等效的,a[1]是*(a + 1)的简写,
// 故sizeof(*(a + 1))也为4;
printf("%d\n", sizeof(&a)); //&a取出数组的地址,地址即为4(32位);
printf("%d\n", sizeof(*&a)); //*&抵消,和第一个一样,输出为16;
//或者这样思考:&a取出数组的地址,*&a解引用找到数组,sizeof(*&a)计算数组大小;即为16;
printf("%d\n", sizeof(&a + 1));//&a取出数组地址,&a + 1地址跳过这个数组(对于这个数组跳16个字节)
//但它还是地址,即sizeof(&a + 1)为4(32位);
printf("%d\n", sizeof(&a[0])); //a[0]为数组首元素,&a[0]首元素地址,sizeof(&a[0])即为地址大小,4(32位)
printf("%d\n", sizeof(&a[0] + 1));//&a[0]首元素地址,&a[0] + 1首元素地址+1,还是地址,即大小为4(32位机器)
return 0;
}
//主要体会什么叫数组的地址,弄清楚它与首元素地址的区别;
字符数组1
看一下下面代码输出都是什么?
#include<stdio.h>
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;
}
输出:
解释:
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr)); //数组名遇见sizeof就为整个数组大小,故为6
printf("%d\n", sizeof(arr + 0));//同一维数组,数组名在进行算术运算的时候为首元素地址,
//地址为4(32位机器)
printf("%d\n", sizeof(*arr)); //*arr首元素地址*解引用,即为首元素,为1字节
printf("%d\n", sizeof(arr[1])); //数组第二个元素,大小为1;
printf("%d\n", sizeof(&arr)); //数组地址,为4(32位机器)
printf("%d\n", sizeof(&arr + 1));//数组地址+1,还是地址,为4(32位机器)
printf("%d\n", sizeof(&arr[0] + 1));//首元素的地址+1,还是地址,为4(32位机器)
return 0;
}
字符数组2
看一下下面代码输出都是什么?
#include<stdio.h>
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;
}
介于这两串代码会出现访存错误(不属于你的程序),无法输出,咱们就看可以输出的
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
输出:
解释:
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
//对strlen函数简介:
// 返回类型为字符串大小;
// 参数为字符串地址
// 是用来计算字符串大小的函数,遇到'\0'停止,
printf("%d\n", strlen(arr)); //对于数组arr没有'\0',咱们不知道从首元素地址起哪里有'\0',
//故不知道输出为多少,
printf("%d\n", strlen(arr + 0)); //解释同上;
printf("%d\n", strlen(*arr)); //*arr对首元素地址*解引用,即为首元素,
//它会把'a'(97)当作内存地址去访问,会有意想不到的错误
printf("%d\n", strlen(arr[1])); //解释同上;
printf("%d\n", strlen(&arr)); //&arr为数组地址,虽然实参类型与形参类型不同,
//但是形参会将实参的类型转化为与自己相同的类型后再在接受,即隐式类型转化;
//故输出与strlen(arr)相同
printf("%d\n", strlen(&arr + 1)); //&arr + 1跳过整个数组之后的地址,之后道理同上虽然传过去的是数组地址,
//但是会发生隐式类型转化,因为跳过了6个元素,故它的输出比上一个少6;
printf("%d\n", strlen(&arr[0] + 1));//首元素地址+1,故输出比strlen(arr)少1
return 0;
}
字符数组3
看一下下面代码输出都是什么?
#include<stdio.h>
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));
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;
}
与字符数组2中类似的就不再解释了:
int main()
{
char arr[] = "abcdef";
printf("%d\n", sizeof(arr)); //数组名遇见sizeof就为整个数组大小,但是咱们这里要注意
//字符串后面还有一个隐藏的\0呢,所以应该为7,
printf("%d\n", sizeof(arr + 0));//4
printf("%d\n", sizeof(*arr)); //1
printf("%d\n", sizeof(arr[1])); //1
printf("%d\n", sizeof(&arr)); //4
printf("%d\n", sizeof(&arr + 1));//4
printf("%d\n", sizeof(&arr[0] + 1));//4
printf("%d\n", strlen(arr)); //注意:
//strlen遇见\0就停止,即为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));//随机,直到遇到\0
printf("%d\n", strlen(&arr[0] + 1));//5
return 0;
}
字符数组4
int main()
{
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));
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;
}
首先,咱们先对char* p = "abcdef"
解释一下:
p作为一个字符指针,它需要接收字符的地址
所以这个代码的意思是:将字符串的第一个元素的地址给p
解释:
int main()
{
char* p = "abcdef";
printf("%d\n", sizeof(p)); //p首元素地址,大小为4(32位机器)
printf("%d\n", sizeof(p + 1));//p + 1第二个元素地址,大小为4(32位机器)
printf("%d\n", sizeof(*p)); //*p首元素地址解引用即为首元素,大小为1
printf("%d\n", sizeof(p[0])); //p[0]前面说过,相当于*p+0;即与前一个一样,为1;
printf("%d\n", sizeof(&p)); //p为首元素地址,即字符指针,&p则为二级指针,二级指针也是指针呀,大小为4(32位机器)
printf("%d\n", sizeof(&p + 1));//&p二级指针,&p + 1二级指针跳过一个一级指针(32位机器为4字节),但是它还是地址
//所以为4(32位机器)
printf("%d\n", sizeof(&p[0] + 1));//p[0]即为a,&p[0]取出首元素地址,&p[0] + 1首元素地址+1,第二个元素地址,大小为4(32位机器)
printf("%d\n", strlen(p)); //传入首元素地址,即求的是字符串长度,为6;
printf("%d\n", strlen(p + 1)); //传入第二个元素地址,即求的是从第二个元素起的字符串长度,为5;
//printf("%d\n", strlen(*p)); //传入首元素,等效于strlen('a')->strlen(98);会导致内存访问错误;
//printf("%d\n", strlen(p[0])); //同上,内存访问错误
printf("%d\n", strlen(&p)); //&p,二级指针,结果未知
printf("%d\n", strlen(&p + 1));//同上,结果未知,且两者无联系;
printf("%d\n", strlen(&p[0] + 1));//&p[0]为首元素地址,&p[0] + 1为第二个元素的地址,
//即求的是从第二个元素起的字符串长度,为5
return 0;
}
输出(内存访问错误无输出):
二维数组
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;
}
输出
解析:
int main()
{
int a[3][4] = { 0 };//未完全初始化,默认其他为0;
printf("%d\n", sizeof(a)); //同上,整个数组大小,即为48;
printf("%d\n", sizeof(a[0][0])); //第0行第0列元素大小,即为4(int);
printf("%d\n", sizeof(a[0])); //a[0]代表第0行元素,即一维数组,故大小为4*4(int),为16;
printf("%d\n", sizeof(a[0] + 1)); //a[0]进行算数运算时,和一维数组一样,代表第0行数组(一维数组)的首元素(第0个元素)地址,
//a[0] + 1即为第0行第一个元素地址,故大小为4(32位机器);
printf("%d\n", sizeof(*(a[0] + 1)));//a[0] + 1为第0行第一个元素地址,*解引用即为第0行第一个元素,故大小为4;
printf("%d\n", sizeof(a + 1)); //二维数组数组名为第0行地址(一维数组地址),数组名a + 1即为第1行地址(一维数组地址),
//地址大小为4(32位机器)
printf("%d\n", sizeof(*(a + 1))); //a + 1为第1行地址,*解引用即为第1行元素,故大小为16;
printf("%d\n", sizeof(&a[0] + 1)); //a[0]表示第0行(即一维数组),&a[0]表示第0行地址,&a[0] + 1即为第1行地址,大小为4(32位机器);
printf("%d\n", sizeof(*(&a[0] + 1)));//&a[0] + 1为第1行地址,*解引用则为第1行元素,大小为16;
printf("%d\n", sizeof(*a)); //a为首行元素地址(一维数组地址),*解引用即为首行元素,大小为16;
printf("%d\n", sizeof(a[3])); //很明显,已经越界了,但是sizeof不计算表达式,仅分析类型,因此即使越界也不会报错。
//所以咱们还是来看看,a[3]->*(a+3),同上,还是16;
return 0;
}
所以咱们这里可以再进行一次总结:
数组名的意义:
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
- & 数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
- 除此之外所有的数组名都表示首元素的地址。