在计算机科学和数据结构中,数组是一种基本且至关重要的数据结构。数组是一种线性数据结构,用于存储具有相同数据类型的元素集合。这些元素在内存中连续存储,并可以通过索引(通常是整数)快速访问。数组的概念不仅广泛应用于编程中的各个领域,如排序、搜索、动态规划等算法实现,也是理解更复杂数据结构(如链表、栈、队列和矩阵)的基础。
然而,数组的理解并不仅仅局限于其定义和基本操作。在实际应用中,数组的高效使用和优化往往涉及对存储空间的优化、时间复杂度的分析以及对特定编程语言中数组特性的深入理解。因此,辨析数组的不同类型(如静态数组、动态数组)、掌握数组的常用操作(如插入、删除、遍历)以及了解数组在不同场景下的应用,对于提高编程能力和解决复杂问题的能力至关重要。
在接下来的内容中,我们将深入探讨数组的基本概念、特性、操作以及在实际编程中的应用,旨在帮助读者更好地理解和掌握这一基础而强大的数据结构。
注:代码注释皆为辨析解释
指针和数组笔试题解析
一维数组
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));
//4*4=16
printf("%d\n", sizeof(a + 0));
//a+0是数组首元素的地址,是地址就是4/8字节
printf("%d\n", sizeof(*a));
//*a是数组首元素,计算的是数组首元素的大小,单位是字节,4
printf("%d\n", sizeof(a + 1));
//数组第二个元素的地址
printf("%d\n", sizeof(a[1]));
//数组第二个元素的地址
printf("%d\n", sizeof(&a));
//&a是整个数组的地址,整个数组的地址也是地址,地址大小为4/8字节
//&a->类型:int(*)[4]数组指针
printf("%d\n", sizeof(*&a));
//&a是整个数组的地址,*&a就是拿到了数组,sizeof(*&a)==sizeof(a),16
printf("%d\n", sizeof(&a + 1));
//&a是整个数组的地址,&a+1跳过整个数组,指向数组后面的地址,是一个地址,4/8字节
printf("%d\n", sizeof(&a[0]));
//&a[0]首元素的地址,计算的是首元素地址的大小,4/8字节
printf("%d\n", sizeof(&a[0] + 1));
//&a[0]+1是数组第二个元素的地址,地址大小为4/8字节
//字符数组
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));//arr单独放在sizeof内部,计算的是整个数组的大小,单位是字节,6
printf("%d\n", sizeof(arr + 0));//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));//&arr+1是跳过整个数组,指向数组后面的地址,地址大小为4/8字节
printf("%d\n", sizeof(&arr[0] + 1));//&arr[0] + 1是数组第二个元素的地址
printf("%d\n", strlen(arr));//随机值(至少为6),strlen只有遇到\0才会停止
printf("%d\n", strlen(arr + 0));//随机值,同上
printf("%d\n", strlen(*arr));//strlen('a')->strlen(97)非法访问error
printf("%d\n", strlen(arr[1]));//非法访问,同上
printf("%d\n", strlen(&arr));//随机值,取出整个数组及其后面的地址
printf("%d\n", strlen(&arr + 1));//随机值,&arr是整个数组的地址,&a+1是跳过了整个数组的地址
printf("%d\n", strlen(&arr[0] + 1));//随机值,&arr[0] + 1是第二个元素的地址
return 0;
注:
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));//3*4*4=48
printf("%d\n", sizeof(a[0][0]));//4
printf("%d\n", sizeof(a[0]));//a[0]是第一行的数组名,数组名单独放在sizeof内部,计算的是第一行的大小,16字节
printf("%d\n", sizeof(a[0] + 1));//a[0]作为第一行的数组名,没有单独放在sizeof内部,没有取地址,表示的是数组首元素的地址
//那就是a[0][1]的地址,a[0]+1就是第一行第二个元素的地址,是地址大小就是4/8字节
printf("%d\n", sizeof(*(a[0] + 1)));//*(a[0] + 1)是第一行第二个元素,计算的是元素的大小---4个字节
printf("%d\n", sizeof(a + 1));//a是二维数组的数组名,数组名表示首元素的地址,就是第二行的地址,a + 1就是第二行的地址
//第二行的地址也是地址,是地址就是4/8字节 a+1类型为--->int(*)[4]
printf("%d\n", sizeof(*(a + 1)));//*(a + 1)就是第二行的全部元素,16字节
printf("%d\n", sizeof(&a[0] + 1));//&a[0] + 1就是第二行的地址,是地址,地址大小为4/8字节
printf("%d\n", sizeof(*(&a[0] + 1)));//*(&a[0] + 1)是对第二行地址解引用,得到第二行的数组,计算第二行的大小
printf("%d\n", sizeof(*a));//a是首元素的地址,就是第一行的地址,*a就是计算第一行的大小,16字节
//*a---*(a+0)--a[0]
printf("%d\n", sizeof(a[3]));
//如果数组存在第四行,a[3]就是第四行的数组名,数组名单独放在sizeof内部,计算的是第四行的大小,16字节
//放在sizeof内部的表达式,不会进行运算操作
在编程世界的浩瀚宇宙中,指针如同一把双刃剑,既锋利无比,又需谨慎握持。作为C、C++等编程语言的核心特性之一,指针提供了一种直接操作内存地址的能力,使得程序能够以更高效、更灵活的方式管理数据和资源。指针不仅是对内存单元的抽象表示,更是连接程序与计算机底层硬件的桥梁,为开发者提供了前所未有的控制力。
通过这段旅程,期望能够帮助读者建立起对指针的深刻认识,不仅学会如何“舞剑”,更懂得如何“避锋”,从而在编程的征途中更加游刃有余。
//1.
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };//&a--->int(*)[5]
int* ptr = (int*)(&a + 1);//强制类型转换
printf("%d,%d", *(a + 1), *(ptr - 1));//2 5
return 0;
}
//程序的结果是什么?
2.
//结构体的大小是20个字节
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
p=(struct Test*)0x100000;
printf("%p\n", p + 0x1);
//0x00100014
printf("%p\n", (unsigned long)p + 0x1);
//0x00100001
printf("%p\n", (unsigned int*)p + 0x1);
//0x00100004
return 0;
}
//3.小端,x86环境下
int main()
{
int a[4] = { 1, 2, 3, 4 };
int* ptr1 = (int*)(&a + 1);
int* ptr2 = (int*)((int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2);//1,2
return 0;
}
//4.
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };//注意逗号表达式结果为最后一个数的值
//数组实际存储为1 3 5,其余为未初始化的0
int* p;
p = a[0];
printf("%d", p[0]);//结果为1
return 0;
}
//5.
int main()
{
int a[5][5];
int(*p)[4];
p = a;
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
//结果为:fffffffc,-4
return 0;
}