10.数组和指针试题讲解
数组
整型数组
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:6328)
#include <stdio.h>
int main()
{
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));//sizeof(数组名):计算的是整个数组的大小 ———— 16
printf("%d\n", sizeof(a+0));//a+0表示a是首元素的地址,所以大小为 ———— 4/8(32位平台(x86)是4字节,64位平台(x64)是8字节)
printf("%d\n", sizeof(*a));//a是首元素地址,*a就是首元素,整型元素大小为 ———— 4
printf("%d\n", sizeof(a+1));//a+1是第二个元素的地址 ———— 4/8
printf("%d\n", sizeof(a[1]));//第二个元素的大小 ———— 4
printf("%d\n", sizeof(&a));//&a取的是整个数组的地址,但也是地址,是地址就是 ———— 4/8
printf("%d\n", sizeof(*&a));//对整个数组的地址解引用,还可以理解为*和&抵消了 ———— 16
printf("%d\n", sizeof(&a+1));//&a+1表示地址跳过一整个数组,但仍是地址 ———— 4/8
printf("%d\n", sizeof(&a[0]));//取出首元素的地址 ———— 4/8
printf("%d\n", sizeof(&a[0]+1));//第二个元素的地址 ———— 4/8
return 0;
}
运行结果:①
16
8
4
8
4
8
16
8
8
8
-- -- -- -- -- -- -- -- -- --
字符数组
例一
#include <stdio.h>
#pragma warning(disable:6328)
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));//sizeof(数组名):计算的是整个数组的大小 ———— 6
printf("%d\n", sizeof(arr+0));//a+0表示a是首元素的地址,所以大小为 ———— 4/8
printf("%d\n", sizeof(*arr));//a是首元素地址,*a就是首元素,字符类型元素大小为 ———— 1
printf("%d\n", sizeof(arr[1]));//第二个元素的大小 ———— 1
printf("%d\n", sizeof(&arr));//&a取的是整个数组的地址,但也是地址,是地址就是 ———— 4/8
printf("%d\n", sizeof(&arr+1));//&a+1表示地址跳过一整个数组,但仍是地址 ———— 4/8
printf("%d\n", sizeof(&arr[0]+1));//第二个元素的地址 ———— 4/8
return 0;
}
运行结果:
6
8
1
1
8
8
8
-- -- -- -- -- -- --
例二
#include <stdio.h>
#include <string.h>
#pragma warning(disable:4477)
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr));//无'\0',结果是 ———— 随机值
printf("%d\n", strlen(arr + 0));//首元素地址+0,但仍然没有'\0',结果是 ———— 随机值(和上面一样)
printf("%d\n", strlen(*arr));//strlen函数的参数部分要的是地址,而*arr等于a,a的ACSCII码值是97
//strlen会从97这个地址向后去找'\0',属于非法访问,直接报错 ———— error
printf("%d\n", strlen(arr[1]));//传了一个b(即98)给strlen函数 ———— error
printf("%d\n", strlen(&arr));//整个数组的地址在值上等于字符'a'的地址,所有也是从开头去找'\0', ———— 随机值
printf("%d\n", strlen(&arr+1));//地址跳过一个数组后,在想后面找'\0',随机值小6 ———— 上面的随机值-6
printf("%d\n", strlen(&arr[0]+1));//从第二个元素向后面找'\0',随机值小1 ———— 随机值-1
return 0;
}
可能有的编译器会说strlen的需要参数和传过来的不一样,直接报错,这个会在下面的例四中一起回答这个问题
-- -- -- -- -- -- --
例三
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#pragma warning(disable:4477)
int main()
{
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//f后面还有'\0',共7个char类型的元素 ———— 7
printf("%d\n", sizeof(arr + 0));//首元素地址+0还是地址 ———— 4/8
printf("%d\n", sizeof(*arr));//首元素大小 ———— 1
printf("%d\n", sizeof(arr[1]));//第2个元素大小 ———— 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
return 0;
}
运行结果:
7
8
1
1
8
8
8
-- -- -- -- -- -- --
例四
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#pragma warning(disable:4477)
int main()
{
char arr[] = "abcdef";
printf("%d\n", strlen(arr));//'\0'前共有6个字符 ———— 6
printf("%d\n", strlen(arr + 0));//arr+0仍是首元素地址,与上一行相同 ———— 6
printf("%d\n", strlen(*arr));//传参传了一个'a'(即97),非法访问 ———— error
printf("%d\n", strlen(arr[1]));//非法访问 ———— error
printf("%d\n", strlen(&arr));//整个数组的地址在值上和首元素地址相同,结果还是 ———— 6
//但是会报警告(报错也可能)。因为&arr的类型是数组指针char(*)[7],但是strlen函数的参数是const char*
printf("%d\n", strlen(&arr + 1));//跳过一个数组的地址。因此,连数组末尾的'\0'都跳过了,不知道下一个'\0'会在哪 ———— 随机值
printf("%d\n", strlen(&arr[0] + 1));//从'b'开始向后面开始找'\0' ———— 5
return 0;
}
-- -- -- -- -- -- --
例五
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#pragma warning(disable:4477)
int main()
{
const char* p = "abcdef";//有点编译器不支持这样写:char* p = "abcdef" ,你在前面加一个const就好了,加完也更加严谨
printf("%d\n", sizeof(p));//p是指针 ———— 4/8
printf("%d\n", sizeof(p + 1));//p+1是地址 ———— 4/8
printf("%d\n", sizeof(*p));//*p就是字符a ———— 1
printf("%d\n", sizeof(p[0]));//数组中arr[0]就等于*(arr+0),这里p==arr,它们都是首元素地址,p[0]==*(p+0).所有p[0]其实就是a ———— 1
printf("%d\n", sizeof(&p));//p是一级指针,&p可以放在一个二级指针里 ———— 4/8
printf("%d\n", sizeof(&p + 1));//p的地址+1 ———— 4/8
printf("%d\n", sizeof(&p[0] + 1));//p[0]是a,&p[0]就是a的地址,加1是b的地址 ———— 4/8
return 0;
}
运行结果:
8
8
1
1
8
8
8
-- -- -- -- -- -- --
例六
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#pragma warning(disable:4477)
int main()
{
const char* p = "abcdef";
printf("%d\n", strlen(p));//p中是首元素地址,这个字符串常量有六个字符 ———— 6
printf("%d\n", strlen(p + 1));//strlen从b开始向后找'\0' ———— 5
printf("%d\n", strlen(*p));//非法访问 ———— error
printf("%d\n", strlen(p[0]));//非法访问 ———— error
printf("%d\n", strlen(&p));//从p的地址向后找'\0' ———— 随机值
printf("%d\n", strlen(&p + 1));//p的地址的下一个地址 ———— 随机值
printf("%d\n", strlen(&p[0] + 1));//从b的地址向后找'\0' ———— 5
return 0;
}
-- -- -- -- -- -- -- -- -- --
二维数组
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#pragma warning(disable:4477)
int main()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));//求数组大小,12*4 ———— 48
printf("%d\n", sizeof(a[0][0]));//第一行第一列那个元素的大小 ———— 4
printf("%d\n", sizeof(a[0]));//把二维数组看成一维数组,每个元素都是一维数组,a[0]即为第一行的数组名,sizeof求第一行四个元素的大小,4*4 ———— 16
printf("%d\n", sizeof(a[0] + 1));//a[0]+1中,数组名表示首元素地址,+1则是第一行第二列的元素地址,而不是第二行的地址 ———— 4/8
printf("%d\n", sizeof(*(a[0] + 1)));//第一行第二列的元素 ———— 4
printf("%d\n", sizeof(a + 1));//a是二维数组的数组名,所以a就是首元素(第一行)地址,a+1就是第二行地址 ———— 4/8
printf("%d\n", sizeof(*(a + 1)));//第二行的大小,等于arr[1] ———— 16
printf("%d\n", sizeof(&a[0] + 1));//&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]));//sizeof不会去访问数组的地址行,它是不参与真实运算的,只是根据类型来计算大小。
//所以a[3]和a[0]是一样的,是一个有4个整型元素的一维数组 ———— 16
return 0;
}
运行结果:
48
4
16
8
4
8
16
8
16
16
16
-- -- -- -- -- -- -- -- -- -- -- -- -- -- ---- -- -- -- -- -- -- -- -- -- -- -- -- -- --
附
①作者当前使用x64平台,所以指针大小为8