数组名的理解
数组名:数组名就是地址,是数组首元素的地址。
int main()
{
int a[]={1,2,3,4};
return 0;
}
调试观察a
与&a[0]
的值,会发现它俩的值是一样的。
看到这,可能有的读者会思考“既然数组名是数组首元素的地址,那这段代码又该怎么理解? siezeof(a)”
首先我们回顾一下"sizeof"的知识:
sizeof计算的是操作对象在内存中所占的字节大小
上面,我们讲到“数组名是首元素地址
”,既然是地址,那sizeof(a)输出的值因该是4/8
。但实际运行起来输出的值却不是。看下面的图。
为什么会输出16呢?不是说“数组名就是首元素地址”吗?难道是编译器出错了?
其实不是的,数组名就是首元素地址,但是有两个例外:
- sizeof(数组名),这里,数组名代表的是整个数组,计算的是数组在内存中所占的字节大小。
- &数组名,这里数组名代表整个数组,取出的是数组的地址。
关于“&数组名”,我举个例子,就明白了。
int main()
{
int a[]={1,2,3,4};
printf("%p\n",a);
printf("%p\n",&a[0]);
printf("%p\n",&a);
printf("%p\n",a+1);
printf("%p\n",&a[0]+1);
printf("%p\n",&a+1);
return 0;
}
观察输出的值,当我们输出a与a+1时,跳过的是四个字节 (一个整型),输出&a[0]和&a[0]+1,跳过的是四个字节(一个整型),而输出&a与&a+1时,跳过的是16字节(一个数组)。
这是因为&a取出来的是数组的地址,当&a+1时,跳过的是一个数组的大小,而其余两个取出来的都是数组首元素的地址,+1跳过的只是数组首元素。这下就应该明白&数组名
的含义了把。
使用指针访问数组
在学习数组的时候,我们访问数组是通过数组下标来访问,像这样:
int main()
{
int a[10]={0};
int sz=sizeof(a)/sizeof(a[0]);
for(int i=0;i<sz;i++)//通过数组下标,向数组输入值
{
scanf("%d",&a[i]);
}
for(int i=0;i<sz;i++)//通过数组下标,输出数组的值
{
printf("%d",a[i]);
}
return 0;
}
但是,既然我们学了指针,那能不能用指针来访问数组呢?那是当然可以的:
int main()
{
int a[10]={0};
int *p=a;
int sz=sizeof(a)/sizeof(a[0]);
for(int i=0;i<sz;i++)//通过数组下标,向数组输入值
{
scanf("%d",p+i);//这里夜可以写成a+i
}
for(int i=0;i<sz;i++)//通过数组下标,输出数组的值
{
printf("%d",*(p+i));//也可以写成*(a+i)
}
return 0;
}
因为数组在内存中是连续存放的
我们让p指向数组首元素的地址
代码中p+i
就表示让p从数组首元素开始,后移i个单位(这个后移并不会实际改变p的指向,只是让p后移访问地址,结束后p依然指向数组首元素的地址)
当p+0时,指针变量p访问的就是数组首元素的地址,当p+1时,指针访问的就是数组下标为1的元素。
后面p+2、p+4、p+5…都是如此,当输入完数据后,进行输出。如果想要得到指针变量p指向的地址下的值,就需要对指针进行解引用。
for(int i=0;i<sz;i++)//通过数组下标,输出数组的值
{
printf("%d",*(p+i));//也可以写成*(a+i),p[i]
}
不管我们写成*(p+i)还是p[i],它都能正常输出,是因为他俩本质上是等价的。
那么*(a+i)与a[i]也是如此。数组与那苏的访问,在编译处理的时候,也是转换成数组首元素的地址+偏移量,然后解引用来访问数组的
。
一维数组传参的本质
一维数组的传参,实际上传递的就是数组首元素的地址
看下这段代码:
//一维数组传参,形参的部分可以写成指针的形式,也可以写成数组的形式
void test(int a[])//参数是数组的形式,本质上还是指针
{
printf("%zd\n",sizeof(a));
}
void test1(int *a)
{
printf("%zd\n",sizeof(a));
}
int main()
{
int a[]={1,2,3,4,5,6,7,8,9,10};
test(a);
test1(a);
return 0;
}
既然一维数组传参的本质传递的就是数组,那么两个函数计算出来的值应该是4/8,运行得出结果:
所以,当我们把一个一维数组首元素地址传递给一个函数来计算数组的长度时,是得不出我们想要的结果的:
void test(int a[])
{
int sz=sizeof(a)/sizeof(a[0]);
printf("%d",sz);
}
int main()
{
int a[]={1,2,3,4,5,6,7,8,9,10};
test(a);
return 0;
}
- 在x86环境下,输出2
- 在x64的环境下,输出1
冒泡排序
void bubble_sort(int a[],int num)//传入数组的地址和数组的元素个数
{
int i = 0;
for (i=0;i<num-1;i++)//比较几趟,最坏的情况是9趟。
{
int flag = 1;
int j = 0;
for (j=0;j<num-1-i;j++)//第一趟比较9个数,第二趟比较8个数...以此类推
{
if (a[j] > a[j + 1])
{
flag = 0;
int tmp = a[j];
a[j] = a[j + 1];
a[j + 1] = tmp;
}
}
if (flag == 1)//如果为1,说明没有进行交换,数值已经排好序
{
break;
}
}
}
int main()
{
int a[] = {3,1,5,8,4,10,9,7,6,5};
int sz = sizeof(a) / sizeof(a[0]);
bubble_sort(a,sz);
for (int i=0;i<sz;i++)
{
printf("%d ",a[i]);
}
return 0;
}
二级指针
二级指针:存放指针变量地址的指针变量
指针变量也是变量,是变量就会有地址,二级指针
就是用来存放指针变量的地址的。
int main()
{
int a=10;
int *p=&a;
int **pa=&p;
return 0;
}
对于二级指针的运算:
int a=10;
int *p=&a;
int **pa=&p;
return 0;
******************
*pa得到p....pa->&p,*pa->p
**pa得到10....*pa->p,**pa==*p->a==10
指针数组
指针数组:是一个数组,数组中存放的元素是指针。
int b=1;
int c=2;
int d=3;
int*a[3]={&b,&c,&d};
指针数组模拟二维数组
#include <stdio.h>
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
int* parr[3] = { arr1, arr2, arr3 };
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 5; j++)
{
printf("%d ", parr[i][j]);
}
printf("\n");
}
return 0;
}