要想熟练的面对数组与指针方面的笔试题。首先我们先大致了解一下,关于数组与指针。
一,数组
数组的构成是 数据类型+数组名[常量表达式],数组无法直接引用其全部,只能逐一的引用数组元素,数组的表达形式是数组名[下标]。
对于数组用来传参,数组名一般代表数组首元素地址。
二,指针
首先指针就是地址,定义形式为 指针类型+变量名称,这个变量里的数值被认定为内存里的地址。对于指针,我们要详细的了解其指针的类型,指针指向的类型。
int *a;//指针的类型是int *型,指针指向的类型是int型 int **a;//指针的类型是int **型,指针指向的类型是int *型 int(*a)[3];//指针的类型是int (*)[]型,指针指向的类型是 int()[]型
数组与指针
指针->地址,指针变量->大小4/8字节
数组,大部分情况数组名就是数组首元素地址 但有两个例外
sizeof(数组名)
&数组名-数组名表示整个数组-取出的是数组地址
这两个数组名代表的是整个数组的地址
一般的,指针与数组可以相对的表示 *a->&a[o]->a[0]
这里的考察主要通过 操作符sizeof与函数strlen两个对数组与指针的考察,但在此前,先了解一下
操作符sizeof()
sizeof()作为一个单目运算符,是用来计算对应操作数的所占空间大小,它里面的参数可以是,数组,指针,函数,类型等。不管是是什么,都是计算对应数所占空间大小,单位是字节。
对于sizeof(),sizeof表达式不会真的去访问它,只看类型.
/* * int a=5; * short s=11; * printf("%d\n",sizeof(s=a+2));//2 short类型,sizeof只看类型,不计算 * printf("%d\n",s);// 实际为7 但不计算,还是11 * return 0 * * 对于表达式a+3有两种类型: * 值属性 * 类型属性 //sizeof()只关注类型属性,不关注值属性 */
函数strlen()
strlen()函数是用来求字符串长度。
strlen只针对字符串,求的是字符串的长度。本质上是“\0”前的字符长度,其参数是指针才可以,否则会造成非法访问,若无“\0”,则长度为随机值。
例题展示
现在我们来深入sizeof与strlen,举例一个整型数组,此时用sizeof 计算
int a[] = { 1,2,3,4 }; printf("%d\n", sizeof(a));//16 //szieof)(a)就是数组名单独放在sizeof内部,计算数组总大小,单位是字节 printf("%d\n", sizeof(a + 0));//4/8个字节 //a+0其实是数组首元素地址 printf("%d\n", sizeof(*a));//4 //数组首元素 //*a->&a[o]->a[0] printf("%d\n", sizeof(a + 1));//4/8个字节 //a是数组首元素,a+1是第二个元素的地址 printf("%d\n", sizeof(a[1]));//4 printf("%d\n", sizeof(&a));//4或者8 //&a-取出的是整个数组的地址。是一个地址 //int (*pa)[4]=&a;int (*)[4] printf("%d\n", sizeof(*&a));//16 //sizeof(a); //int (*)a; printf("%d\n", sizeof(&a + 1));//4/8 //&a+1跳过一个数组,在该数组地址末尾 printf("%d\n", sizeof(&a[0]));//4/8 printf("%d\n", sizeof(&a[0] + 1));//4/8 //第二个元素的地址
在上面中,若a单独放进sizeof中,就是正常作用,此时a代表整个数组,计算总空间大小为4*4个字节,若没有单独放,如a+0;那么a代表首元素地址,加零还是首元素地址,地址的大小为4或8个字节。其次sizeof(&a)这里取出的地址是整个数组的地址,int (*pa)[4]=&a,数组与指针的相互表示。若&a+1则表示跳过这个数组这一块空间,
之后我们再以arr的字符型数组为例,用sizeof()来计算一次
char arr[] = { 'a','b','c','d','e','f'}; printf("%d\n", sizeof(arr));//6 printf("%d\n", sizeof(arr + 0));//4/8 //数组首元素的地址 printf("%d\n", sizeof(*arr));//1 //表示首元素地址,单独放进来的*,是首元素。 printf("%d\n", sizeof(arr[1]));//1 printf("%d\n", sizeof(&arr));//4/8 //取出的是全部地址,但也是一个地址 printf("%d\n", sizeof(&arr + 1));//4/8 //末尾地址 printf("%d\n", sizeof(&arr[0] + 1));//4/8 //第二个元素地址
上面的数组类型不一样,计算的空间大小不一样,但本质都是看对于 arr arr +0 *arr *arr+ 1 &arr &arr+1 a[0] 等的本质理解它是代表什么。
我们在举例关于strlen的计算,
char arr[] = { 'a','b','c','d','e','f' }; printf("%d\n", strlen(arr));//随机值 //arr数组名,首元素地址,但不知道什么时候遇到‘\0’ printf("%d\n", strlen(arr + 0));//随机值 //+0还是首元素地址 printf("%d\n", strlen(*arr));//非法访问 //放的不是地址,*arr=a[0]是‘a' printf("%d\n", strlen(arr[1]));//无非访问 //与上同理 printf("%d\n", strlen(&arr));//随机值 //无法确定“\0”的位置 printf("%d\n", strlen(&arr + 1));//随机值-6 //末尾地址,找不到’\0‘ printf("%d\n", strlen(&arr[0] + 1));//随机值-1 //第二个地址往后,找不到’\0‘
以上字符数组例子都是在数组中无法找到“\0”的,所以在计算时会出现随机值。
若改变数组再利用sizeof 与strlen 各计算一遍
char arr[] = "abcdef";//[a,b.c.d.e.f,\0] printf("%d\n", sizeof(arr));//7 //计算数组大小,7个字节 printf("%d\n", sizeof(arr + 0));//4/8 printf("%d\n", sizeof(*arr));// 1 //数组首元素。*arr= (*arr+0)= arr[0] printf("%d\n", sizeof(arr[1]));//1 printf("%d\n", sizeof(&arr));//4/8 printf("%d\n", sizeof(&arr + 1));//4/8 printf("%d\n", sizeof(&arr[0] + 1));//4/8 printf("%d\n", strlen(arr));//6 printf("%d\n", strlen(arr+0));//6 //从首元素开始计算 printf("%d\n", strlen(*arr));//无法访问 printf("%d\n", strlen(&arr));//6 //取出的类型不一样,但不影响 printf("%d\n", strlen(&arr+1));//随机值 //在末尾地址 printf("%d\n", strlen(arr[0]+1));//5
对于arr在取地址操作时,他取出的是整个数组的地址。
此外对于一个变量指针存放数组再计算.,比较sizeof与strlen的区别。
int main() { char* p = "abcdef"; printf("%d\0", sizeof(p));//4/8 //指针的大小 printf("%d\0", sizeof(p + 1));//4/8 //指向第二个元素的地址 printf("%d\0", sizeof(*p));//1 //表示首字符 printf("%d\0", sizeof(p[0]));//==*(p+0) 为1 //可以理解为一个数组 printf("%d\0", sizeof(&p));//4/8 //取出的是地址,开辟p时的地址,与abcdef无关 printf("%d\0", sizeof(&p + 1));//4/8 //跳过一个p //char *p;char* *p=&p; printf("%d\0", sizeof(&p[0]+1));//4/8 //b的地址 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));//随机值 //在地址里找'\0’ //不知哪里有”\0“ printf("%d\n", strlen(&p + 1));//随机值 printf("%d\n", strlen(p[0] + 1));//5 }
这里用指针表示数组,效果一样,我们可以直接理解为数组放进去。除了数组名单独放,它都是一个表示首元素地址的效果(&p,代表取出整个地址),才是也是无法找到“\0”。
这里我们再用二维数组举例,再看sizeof与strlen在这里的情况。
int main()//二维数组数组名没单独放。则是第一行地址 { int a[3][4] = { 0 }; printf("%d\n", sizeof(a));//a的数组名单独放在sizeof里 代表所有大小 48 printf("%d\n", sizeof(a[0][0]));//第一行第一个元素 4 printf("%d\n", sizeof(a[0]));//16 第一行的数组名,数组名单独放在sizeof内部。 printf("%d\n", sizeof(a[0]+1));//4 不是单独放在sizeof里 a[0]代表首元素地址 是第一行第二个元素地址 printf("%d\n", sizeof(*(a[0]+1)));// a[0][1]大小 4个字节 printf("%d\n", sizeof(a+1));//a代表第一行的地址类型 加一为 第二行的地址 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 四个0 }
对于二维数组,sizeof(a)
我们知道二维数组是连续储存的,那么它的结构应该是这样。
,这里的a[0]单独放表示第一行整个的所有元素,a不单独放代表的是第一行的整个地址。
且在最后一个例子当中我们发现sizeof并没有说越界访问,这是因为sizeof只看数据类型,不看值类型,在sizeof中参数表达式不会被计算。
对于sizeof(*arr+1) ,a若单独放*a=arr[0],是代表第一行的元素。 不是单独放,则为首元素地址所以这里是第二个元素。
看完以上这个,举个笔试题。
int main() { int a[5] = { 1,2,3,4,5 }; int* ptr = (int*)(&a + 1); printf("%d %d", *(a + 1), *(ptr - 1));//2 5 //a是首元素地址在&a+1中。a是全部地址在ptr中 return 0; }
这里我们可以看到&a是去除的是整个该数组的地址,且a表示首元素地址。
以上便是今天学习的内容了。
大家一起加油o