1.数组名的理解
int arr[10]={1,2,3,4,5,6,7};
int *p=&arr[0];
这里&arr[0]取得的是第一个元素的地址。但其实数组名名本来就是地址,而且是首元素的地址。下面就验证一下。
int main()
{
int arr[10] = {1,2,3,4,5,6};
printf("%p\n",arr);
printf("%p\n", &arr[0]);
return 0;
}
我们可以发现数组名和首元素地址相同,但也有例外
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d",sizeof(arr));
return 0;
}
如果arr首元素的地址的话那么应该是4/8个字节,但显然不是。那这是怎么回事呢?
其实数组名就是数组⾸元素(第⼀个元素)的地址是对的,但是有两个例外:
• sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩,单位是字节
• &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素的地址是有区别的)除此之外,任何地⽅使⽤数组名,数组名都表⽰⾸元素的地址。
下面就用代码那看看
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%p\n",arr);
printf("%p\n", &arr[0]);
printf("%p\n", &arr);
return 0;
}
从这里看出来地址首元素地址和数组的地址又有什么区别呢?
我们从下面的代码来看看吧。
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf(" &arr[0]== %p\n", &arr[0]);
printf(" &arr[0]+1== %p\n", &arr[0]+1);
printf(" arr== %p\n", arr);
printf(" arr+1== %p\n", arr+1);
printf(" &arr== %p\n", &arr);
printf(" &arr+1== %p\n", &arr+1);
return 0;
}
由此可以看出对于&arr[0]和arr加一都是变化4个字节,即1个字节,但是对于&arr则是变化40个字节,即10个整形。因此对于&arr[0]和arr加一都是加一个元素,而对于&arr则是变化了一个数组的大小。
2.使用指针访问数组
int main()
{
int arr[10] = {0};
int i = 0;
int* p= arr;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
scanf("%d",p+i);
//也可以写成scanf("%d",arr+i); 因为p与arr都代表首元素地址
//这里是可以灵活变化的
}
for (i = 0; i < sz; i++)
{
printf("%d ",p[i]);
//p[i]也可以用下面代替
//*(p+i)
//*(arr+1)
//arr[i]
//p[i]
}
return 0;
}
3.一维数组传参的本质
#include <stdio.h>
void test(int arr[])
{
int sz2 = sizeof(arr) / sizeof(arr[0]);
printf("sz2 = %d\n", sz2);
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz1 = sizeof(arr) / sizeof(arr[0]);
printf("sz1 = %d\n", sz1);
test(arr);
return 0;
}
从此看出在函数内部没有算出正确的大小。
这就要学习数组传参的本质了,上个⼩节我们学习了:数组名是数组⾸元素的地址;那么在数组传参的时候,传递的是数组名,也就是说本质上数组传参本质上传递的是数组⾸元素的地址。所以算不出来正确的元素个数。
4,冒泡排序
#define _CRT_SECURE_NO_WARNINGS 1
//本函数是将乱序的数排列成正序
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void sort(int arr[], int sz)
{
//确定冒泡排序的趟数
int i = 0;
for (i = 0; i < sz - 1; i++) //趟数等于元素个数减1,所以i<sz-1
{
//一趟冒泡排序
int flag = 1;//假设数组是有序的 flag的出现是为了优化程序,使得当已经满足要求时不再进行交换
int j = 0;
for (j = 0; j < sz - 1 - i; j++) //j<sz-1-i 的原因:例如有1,2,3 要排序,按照冒泡排序是将1与2交换再与3交换得到2,3,1,进行了2次比较
{ //这就是进行了一趟冒泡排序,再下一次将2与3交换,这里2就不要再与1比较了,所以进行了1次比较,得到3,2,1
//因此每经过一趟冒泡排序就少一次比较次数,而i从0开始所以sz在减去i后要再减去1
if (arr[j] > arr[j + 1])
{
//交换
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = 0;//不是有序
}
}
if (flag == 1)
{
break;
}
}
}
void print(int* arr, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]); //在main函数中将数组大小算出来
sort(arr, sz);
print(arr, sz);
return 0;
}
5.二级指针
指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪⾥?
下面就是一个二级指针的示意图:
对于⼆级指针的运算有:
*ppa 通过对ppa中的地址进⾏解引⽤,这样找到的是 pa , *ppa 其实访问的就是 pa
**ppa 先通过 *ppa 找到 pa ,然后对 pa 进⾏解引⽤操作: *pa ,那找到的是 a
6.指针数组
指针数组是指针还是数组?
我们类⽐⼀下,整型数组,是存放整型的数组,字符数组是存放字符的数组。
那指针数组呢?是存放指针的数组。
7.指针模拟二维数组
#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数组中
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;
}
parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型⼀维数组,parr[i][j]就是整型⼀维数组中的元素。
上述的代码模拟出⼆维数组的效果,实际上并⾮完全是⼆维数组,因为每⼀⾏并⾮是连续的.