种一棵树最好的时间是十年前,其次是现在。
指针与数组
一、指针的运算
指针可以进行三种运算:
1.指针加上整数:
如果指针p指向数组a[i],那么指针p + j 指向a[i + j](前提是a[i + j]存在!)
2.指针减去整数:
如果指针p指向数组a[i],那么指针p - j 指向a[i - j](前提 a[i - j] 存在!)
3.两个指针相加减(两指针必须指向同一变量!!!否则运算没有意义的!)
当两个指针相减时,结果是指针在内存上的距离,可以用数组元素的个数来度量,所以如果指针p指向a[ i ],q指向a[ j ],那么p - q就等于i - j.
指针比较:指针可以使用 > < = >= <= == != 等符号进行比较,(也是只有在指针指向同一数组时,使用关系运算符进行的指针比较才有意义!)
二、指针用于数组的处理
由于指针的算术运算,所以我们可以通过指针的自增自减来访问数组的元素。
#include<stdio.h>
int main()
{
int a[10] = {0,1,2,3,4,5,6,7,8,9};
int *p;
for(p = &a[0];*p < a[10];p++)
{
printf("%d\n",*p);
}
return 0;
}
使用数组下标可以写出不用指针的循环,有一种论调说使用指针可以节省时间,不过对于现在的编译器来说,实际上会自动对使用下标的循环进行优化,即依靠下标的循环会产生更好的代码(大佬说的 [^.^])
*和++组合使用:
#include<stdio.h>
int main()
{
int a[10] = {0,1,2,3,4,5,6,7,8,9};
int *p = &a[0];// *p = a;
while (*p < a[10])
{
printf("%d\n",*p++);
}
return 0;
}
当然还有*++p、(*p)++
三、用数组名作为指针
1.可以用数组名作为指向数组第一个元素的指针。例如int a[10] ; *a = 7 ;这样就是把a[0]元素给他赋值7。通常情况下a + i 就相当于&a[i] ,*(a + i) 就相当于a[i]。
2.数组变量本身表达地址,所以用数组对指针初始化不用&取地址符号,int a[10]; int *p = a; 。但是数组的每一个元素表达的是一个变量,需要取地址符&。
3.[ ]也可以对指针做,p[0] 相当于a[0] 。*也可以对数组做, *a 相当于a[0]。
注意:当数组变量是一个const类型的指针,就不能被赋值了!
4.数组作为函数参数
下面我们看一个有趣的东西( It's amazing!):
#include<stdio.h>
void test1(int a[]);
int main()
{
int a[5] = {0,1,2,3,3};
printf("main中sizeof(a[]) = %lu\n",sizeof(a));
test1(a);
return 0;
}
void test1(int a[])
{
printf("函数中sizeof(a[]) = %lu\n",sizeof(a));
}
运行结果如下:
在函数test1中sizeof传进去的数组我们发现他的字节大小是8!而不是数组本身的字节大小 20!有趣吧哈哈哈哈QAQ。这是因为数组作为参数传参的时候传进去的是一个指针!就是之前我们说了的数组变量是一个特殊的指针。数组名在传递给函数的时候总是被视为指针。
这对我们有很大的意义!:
(1).在给函数传递普通变量时,变量的值会被复制,任何对相应的形式参数的改变都不会影响到变量。而作为实际参数的数组我们可以改变(除非const了一下它)!
(2).给函数传递数组的时间与数组的大小半毛钱关系都没有!(编译器:“我根本就没对你数组进行复制,没想到吧!哈哈哈”)
(3).我们可以在需要的时候吧数组形式参数声明为指针。(编译器对数组和指针的声明完全看做是一样滴)
注意:对于形式参数而言,声明为数组跟声明为指针是一样的;但是对于变量而言声明为数组跟声明为指针是不同的!
5.用指针名当做数组名(如下代码)
#include<stdio.h>
int main()
{
int a[10],*p = a, i = 0;
for(i = 0;i < 10;i++)
{
*p++ = i;
printf("%d\n",a[i]);
}
return 0;
}
四、指针与多维数组
int a[4][5] = {{0,1,2,3,4},{10,11,12,13,14},{20,21,22,23,24},{30,31,32,33,34}};
可以把这个理解为包含四个行元素的数组:a[0]、a[1]、a[2]、a[3],可以把a[0]
还需要记住的是数组名为首元素的地址。有这样的说法a不是a[0][0]的地址,但是我试了一下,结果是它们地址相同,所以总结成一句话:数组名为首元素地址。
行指针:一个二维数组每一行视为一个一维数组,指向每一行的一维数组的指针。
处理多维数组的行:
#include<stdio.h>
int main()
{
int a[4][5] = {{0,1,2,3,4},{10,11,12,13,14},{20,21,22,23,24},{30,31,32,33,34}};
int *p,i = 0,j = 4;
i = 3;
for (p = a[i];p < a[i] + j;p++)//p = a[i]给p初始化.p < a[i] + j 中a[i]+j表示的是a[i][j]
{
*p = 0;
}
printf("%d\n",a[i][2]);//输出验证第i行第2列是否为0
return 0;
}
p++运行一次之后 就相当于 p +1 就相当于 a[i] +1 就相当于 a[i][1]
处理多维数组的列:
#include<stdio.h>
int main()
{
int a[4][5] = {{0,1,2,3,4},{10,11,12,13,14},{20,21,22,23,24},{30,31,32,33,34}};
int (*p)[5],i = 2;//(*p)的括号不可省略![5]这个表示的是一维数组的长度,不可省略!定义了一个行指针,其宽度为5(即二维数组的列数为5)
for ( p = &a[0]; p < &a[4];p++)//p=&a[0]是把第0行那个一行的一维数组的地址交给p这个行指针 &a[4]相当于a+4. p=&a[0]相当于p=a
{
(*p)[i] = 0;//(*p)这里代表着a的一整行
}
printf("%d\n",a[2][i]);//输出验证第2行第2列是否为0
return 0;
}