//本文以固筑自己的知识为主,写的很可能不好。
//参考资料:C Primer Plus 和深入理解C语言指针_ZackSock的博客-CSDN博客_深入理解c指针
一,了解指针
指针,就是一种导航工具,可以正确且迅速带你到达目标所处地点。
写一段简单的代码:
int main(){
int a=1;
double b=2;
int *pa=&a;
double *pb=&b;
return 0;
}
变量pa和pb分别就是a和b的指针, 注意指针的变量的定义方式是 数据类型 * 变量名 ,*不属于变量名。
我将&a赋给了pa,那么&是什么意思?
1.&取地址符 以及 *指针运算符
&与*可以理解为对应关系,*是通过地址获取这个地址储存的变量,而&是通过变量获取这个变量所在的地址。
&为单目运算符(按位与运算符&为双目运算符)。
&与*都是右结合,即从右到左计算,如*&a,就是对a先取地址,然后获取该地址中的变量。
注意:对于常量表达式、寄存器变量不能取地址(因为它们存储在存储器中,没有地址)。
(我写这文章之前居然还不知道寄存器变量,乐,我太菜了)
如:常量表达式:const int a=1,register 寄存器变量:egister int quick...
2.深刻了解指针和变量的关系:
通常的说法就是利用房子和钥匙解释,我认为这个很形象:
eg:int a=1;
当你每创建一个变量,程序就会开辟出一块空间,用于储存你所创建的变量(出生就保分配),如创建一个变量a,
就相当于开辟了一块空间,将值放了进去,然后将变量指向了这块空间,而指针存储了这个变量的地址。
用房子理论就是说,你为一个人开了一间房(空间),地址就是房子的位置,而指针就是这个房子的钥匙,你可以根据指针来找到这个房子,进入这个房子。
注意:指针与变量必须是相同类型。
3. 指针的定义
(1)指针在创建时最好初始化。
我们定义了一个指针,比如 int* a,没有初始化时,a的指向是不确定的。若它指向非法地址,这时可能会报异常;若它指向一个合法地址,这将非常危险,因为这样可能更改一个正在使用的数值。
(2)指针可以初始化为0或者NULL;
指针的0并非是数据0,而是NULL,表示不指向任何地方。
(3) 如果你知道储存某个数的地址,如*0x21332=2;表示0x21332这个地址储存了2这个数值,但这种写法是非法的,因为他不是一个合法的左值,应该将他强制转换为指针,*(int*)0x21332=2。
4.指针的运算:
指针也由数值一样,可以进行“+,-,*,/”的运算,也可进行自增或者自减。
int main(){
int num[3]={1,3,5};//数组的地址是连续的,这里假设num[0]的地址是1000
int *p1=&num[0];
int *p2=&num[1];
int *p3=&num[2];
printf("%d %d %d\n",*p1,*p2,*p3);//1,3,5
printf("%d\n",*(p1+1));//3,输出p1增加一个int类型的长度后的结果,即p2
*(p1++);//等于*p++,因为*和++的优先级是一样的,且遵循从右向左的规则
//这里p1进行了一次指针递增,p1增加了一个int类型的长度(1000->1004)
//(*P1)++是将p1指向的值+1。
printf("%d\n",*p1);//3
}
5..多级指针
int a=1;
int *p1=&a;
int **p2=&p1;
这是一个简单的二级指针,p1得到了a的地址,p2得到了p1所指向的地址。
由于二级指针不是本文重点,故此跳过。
二,数组
1.定义
数组是由数据类型相同的一系列元素组成,在声明数组时,必须要告诉编译器数组有多少个元素,是什么类型,然后编译器就可以正确创建数组,分配合适大小空间。
使用数组就是变相的使用指针,下文第三点会详细说。
注意:分配空间时,数组不会初始化,该空间储存的是内存对应位置上的现有的值。
2.初始化
如果知道要储存的数据,那么数组一开始就初始化比较好。
int main(){
int num[10]={1,2,3,4,5,6,7,8,9,10};
}
可以看出上面代码数组中的数是以“,”隔开的,用“{}”括起来。可以在逗号和值之间使用空格,上面代码就是将1赋给num[0]这个数组的首元素,以此类推 。
3.多维数组
int main(){
int num[3][3]={
{1,2,3},{4,5,6},{7,8,9}};
}
这是一个简单的二维数组。
二维数组可以认为是多个一位数组的数组,num【3】【3】由一维数组num[0][3],num[1][3],num[2][3]组成(num[0],num[1],num[2]可以认为是数组名)。
三.指针与数组的关系
1.在数组中,数组名即为该数组的首地址。
int num[3];
int *p=num;(== int *p=&num[0])
int main(){
int num[3]={1,3,5};
int *p=num;
printf("%d",*p);//1
}
2.根据指针的运算,我们可以用指针输出数组 (因为数组的地址是连续的):
int main(){
int num[3]={1,3,5};
int *p=num;
for(int i=0;i<3;i++){
printf("%d ",*(p+i));//1,3,5
}
}
3.一维数组的地址:
int num[3]={1,2,3};
int *p1=num;
for(int i=0;i<3;i++){
printf("ptr=%p\n",(p1+i));
}
//输出结果:
//ptr=00000000007AFE00
//ptr=00000000007AFE04
//ptr=00000000007AFE08
}
4.对于多维数组:
既然数组名即为该数组的首地址,那么二维数组中是否也能这么用:
int main(){
int num[3][3]={
{1,2,3},{4,5,6},{7,8,9}};
int *p1=num[0];//num[0]相当于一个一维数组,获取num[0][0]的地址
int *p2=num[1];
int *p3=num[2];
printf("*p1=%d *p2=%d *p3=%d\n",*p1,*p2,*p3);//1,4,7
}
可见,多维数组也可,那么多维数组的地址是怎样的呢?
int main(){
int num[3][3]={
{1,2,3},{4,5,6},{7,8,9}};
int *p1=num[0];
int *p2=num[1];
int *p3=num[2];
for(int i=0;i<3;i++){
printf("p1=%p p2=%p p3=%p\n",(p1+i),(p2+i),(p3+i));
}
}
多维数组的地址也是连续的,每一个上一维数组最后一位和下一个上一维数组第一位差一个对应数据类型的长度。
5.指针数组:
指针数组就是一个由指针组成的数组。,首先,这个变量是一个数组,其次,”指针”修饰这个数组,意思是说这个数组的所有元素都是指针类型。
//()与[ ] 优先级相同,根据结合律,就从左向右运算。如果分不清优先级,可以用括号:如果目的是一个数组,那就把num1[3]括起来,如果是一个指针,就把*num1括起来,这里是一个数组。
int main(){
int num[3]={1,2,3};
int *num1[3]={&num[0],&num[1],&num[2]};
//num1就是一个指针数组,它有三个元素,每个元素是一个int *类型的指针。
for(int i=0;i<3;i++){
printf("%d ",*num1[i]);
}
}
6.数组指针
说了指针数组就肯定要说数组指针了。
数组指针就是一个数组的指针,首先,这个变量是一个指针,其次,”数组”修饰这个指针,意思是说这个指针存放着一个数组的首地址。
int main()
{
int num[5] = {1, 2, 3, 4, 5};
int (*p)[5] = #
int i;
for(i = 0; i < 5; i++)
{
printf("%d\n", *(*p + i));//1,2,3,4,5
//或者 printf("%d\n", (*p)[i]);
}
return 0;
}