数组和指针
数组
数组:一系列相同类型元素的集合
数组声明
//一些数组声明
#include<stdio.h>
int main()
{
int a[10]; //包含从a[0]到a[9]10个int类型的元素,不包含a[10]
float f[20]; //20个float类型的元素
double d[20]; //20个double类型的元素
return 0;
}
数组的初始化
int a[10]={0,1,2,3,4,5,6,7,8,9};//每个元素之间以逗号隔开
把0赋给a数组的首个元素a[0],1赋给a[1]以此类推把9赋给a[9];
int a[]={0,1,2,3,4,5,6,7,8,9};
在定义初始化中也可不在方括号中加入数字,直接在后面赋值 计算机根据后面元素个数自动匹配数组大小。
部分初始化
int a[10]={0,1,2,3,4,5};
这句只初始化了a[0]到a[5]六个元素,后面剩余的元素全部会被初始化成为0
在C里也可指定要初始化的元素
int a[10]={[5]=11};
这样就把a[5]初始化成了11;
多维数组
二维数组
int year[2][12]={ {1,2,3,4,5,6,7,8,9,10,11,12} , {1,2,3,4,5,6,7,8,9,10,11,12} };
一年有12个月,两年有24个月
主数组有两个数组元素,每个数组元素又内含12个元素
一维数组相当于写了一行字
二维数组相当于写了几行字
三维数组就相当于写了好几张字,a[2][5][10]这个相当于写了两张,然后每一张写了5行,每一行又写了10个字。
以此类推还有四维数组,五维…
数组和指针
数组名是数组首个元素的地址
如果a是一个数组,那么 a==&a[0]
数组作为函数形参
数组作为函数形参传入到函数中相当于传入了一个地址
函数参数表中的数组 a[] 实际上是一个指针,将a[]替换为*a后程序仍然可以正常运行
在函数参数表中 int a[]实际上是声明了a是一个int类型的指针,并且a还是一个int型的数组元素。
数组名是数组首个元素的地址
我们给函数传入一个 x 就相当于传入了一个数组首个元素x[0]的地址
还可以改一下程序,让我们给函数直接传入一个地址
程序依旧正常运行,这样是不是就证明了int a[]这个形参就是让我们给函数传入一个地址(指针,a[]==*a)。
//test
#include<stdio.h>
void test(int a[]);
int main()
{
int x[10] = { 0,1,2,3,4,5,6,7,8,9 };
printf("sizeof(x)=%d\n", sizeof(x));
printf("&x=%p\n", &x);
printf("x=%p", x);
test(&x[0]);
printf("x[0]=%d\n", x[0]);
return 0;
}
void test(int a[])
{
printf("sizeof(a)=%d\n", sizeof(a));
a[0] = 112;
printf("%p\n", &a[0]);
}
下面的四种函数原型都是等价的
int test(int *a);
int test(int a[]);
因为函数原型可以省略参数名,所以我们还可以写成这样
int test(int *);
int test(int []);
但是在函数定义中必须要加上参数名,不可省略,不然的话传入的值让谁保存嘞!
所以函数定义我们就得使用下面这两个:
int test(int *a);
int test(int a[]);
指针
指针是一个地址
int a;
int *p; //指针的声明
p=&a; //初始化 运算符 & 是用来取地址的
*p=12 指针的间接引用 *p==a *p=12 => a=12
指针的间接引用又称解引用,* 运算符给出指针所指向地址上所存储的值
####野指针
没有有效的地址空间的指针,也就是未初始化的指针
int *p;//未初始化的指针
*p=10;//错误 p是一个随机值,我们不知道p将会指向哪里,可能不会出错,但也可能指向一个特殊的地方使得程序崩溃。
####空指针
为了避免使用未初始化的指针,我们定义了一个 NULL (NULL==0)使得指针在声明时就指向一个无效的访问区域。
int *p=NULL;
p 所指向的存储空间一定是一个无效的访问区域。
####指针作为函数形参
#include<stdio.h>
int sum(int *x,int y);
int main()
{
int a=10;
int b=2;
sum(*a,b);
printf("%d",a);
return 0;
}
int sum(int *x,int y)
{
return *x=*x+y;
}
就是给函数传入了一个地址。
####指针的运算
**指针加上一个整数n **
指针加上一个整数n相当于指针加上了一个 n*sizeof(类型名)
递增指针 p++ 、++p 加上了一个 sizeof(类型名)
指针减去一个整数n
指针减去一个整数n相当于指针减去了一个 n*sizeof(类型名)
递减指针 p-- 、–p 减去了一个 sizeof(类型名)
指针相减
在同一个数组里,两个指针指向不同的元素,指针作差可以用来计算两个元素之间的距离。
指针和const
指针是const
指针指向的地址不可修改,只能是第一次得到的地址,但是指针所指向的变量可以修改。
所指向的变量是const
指针指向的地址可以修改,但是不能通过指针去修改变量的值,指针指向的变量并不会成为const(除非变量本身就是const),只是无法通过指针去修改变量,const的作用对象一直是指针。
数组和指针
数组变量是特殊的指针
数组变量本身表达地址
a==&a[0]
[]运算符可以对数组做,也可以对指针做。
例如:
*运算符可以对指针做,也可以对数组做。
例如:
#####指针和二维数组
//二维数组相当于是一个数组的数组
int a[4][2] = { { 7, 11}, { 12, 31},{ 4, 3},{ 2, 1} };
int (*p)[2]=a; //数组指针(表示二维数组)
p 二维数组的首个数组元素的地址 => &a[0]
p+2 二维数组的第3个数组元素的地址 => &a[2]
*(p+2) 二维数组的第3个数组元素的首个元素的地址 => &a[2][0]=4
*(p+2)+1 二维数组的第3个数组元素的第二个元素的地址 => &a[1]
*(*(p+2)+1) 二维数组的第3个数组元素的第二个元素的值 => a[2][1]=3