指针在c语言中有着举足轻重的地位,是C语言的精髓,是C语言的灵魂。这样说一点也不为过,那么指针究竟是什么呢?
指针就是地址!上课的时候老师一直在强调这句话要外化于形,内化于心,说的再通俗一点就是,如果把目标访问数据当做我们的宝藏,那么指针就好比是藏宝图。
定义指针变量时,与一般变量最大的不同就是前面会有目标访问符" ∗ * ∗",在函数的说明语句板块你会看到不同的定义方式,如下面列举的几种
int *p1
double *p2
char *p3
....
研究指针一定要清楚的就是内存的概念,也就是存储的形式和所占用的空间。
特别注意
1,指针变量在内存中所占用的字节数一般为4或8个字节,(这个由编译决定,大家可以使用函数sizef()来查看地址变量的字节数)
口说无凭,上代码:(我的占了8个字节)
#include
int main()
{
int *p,a,n;
a=10;
p=&a; //&取地址的操作符
n=sizeof(p);
printf("%d",n);
return 0;
}
结果显示:
2,C语言中的地址信息中既包含位置信息,也包含它所指向的数据的类型信息。
前面定义的 i n t , d o u b l e , c h a r int , double,char int,double,char是“通知”编译器我们所定义的指针变量所指向的数据类型,他们所占的字节数还是原来字节数, i n t int int占4个, d o u b l e double double占8个, c h a r char char占1个……而指针变量对应的字节数是不会改变的。
容易理解错的是,误以为前面的数据类型会影响指针变量所占的字节数。
3,I have a problem.指针到底是什么类型呢,嘻嘻,就是指针类型!
试图用简单的操作来说明一些问题:交换两个变量的值
#include
int main()
{ void swap(int *plt1,int *plt2);
int *p1,*p2,a,b;
a=4,b=8;
printf("\n交换前:a=4,b=8\n");
p1=&a;
p2=&b;
swap(p1,p2);
printf("交换后:a=%d,b=%d\n",*p1,*p2); //*p1<=>a
return 0;
}
void swap(int *plt1,int *plt2)
{
int m;
m=*plt1;
*plt1=*plt2;
*plt2=m;
}
代码说明:
swap()函数通过对变量a,b所对应的存储区的数据进行交换,从而达到了交换a,b数值的功能实现。虽然在调用函数的时候新开辟的存储空间中会在调用结束后释放,但指针的强大之处就在于,能通过指针这个媒介对main函数里的数据进行修改。
结果展示:
错误展示:
void swap(int *p1,int *p2)
{
int m;
m=p1;
p1=p2;
p2=m;
}
void swap(int a,int b)
{
int m;
m=a;
a=b;
b=m;
}
这两个函数放在一起看,二者都是对接受的变量进行了交换,而没有对mian函数中的变量进行作用,我们知道在函数调用结束后,内存会进行释放,所以该函数模块就“操作了个寂寞”,什么也没干。
我们知道C中函数不能通过 r e t u r n return return去返回一批数据,两个也不行。
数组是一批具有相同数据类型的变量集合,在内存中占用连续的存储空间,以同一个名字命名,用下标加以区分。其实数组的访问本质上就是指针的偏移。那我们为什么还要引入指针的概念来处理数组呢?这里只是对指针的一个简单应用,指针的魅力远大于此,比如文件指针等。
——例题1,将数组a中的n个整数按相反顺序存放,如图所示操作:
解题思路,通过相应位置值的交换达到逆序输出的目的,实现过程中会涉及到的很多干货,请爱学习的你耐心看下去吧。
先看代码吧,
解题一:
#include
int main()
{ void swap(int arr[],int m);
int arr[10]={1,2,3,4,5,6,7,8,9,10},i;
swap(arr,10);
for(i=0;i<10;i++)
{
printf(" %d",arr[i]);
}
return 0;
}
void swap(int arr[],int m)
{
int zh,i,j,n=(m-1)/2;
for(i=0;i<=n;i++)
{
j=m-1-i;
zh=arr[i];arr[i]=arr[j];arr[j]=zh;
}
}
解题二:
#include
int main()
{ void swap(int arr[],int m);
int arr[10]={1,2,3,4,5,6,7,8,9,10},i;
swap(arr,10);
for(i=0;i<10;i++)
{
printf(" %d",arr[i]);
}
return 0;
}
void swap(int *p,int m)
{
//相当于p=arr,把数组的地址给了p
int *h,*i,*j,n=(m-1)/2,zh;
i=p;j=i+m-1;h=p+n;
for(;i<=h;i++,j--)
{
zh=*i;*i=*j;*j=zh;
}
return;
}
两者的操作结果是一样一样的,其实原理也一样。
干货number one:
指针的具体指向问题。
干货number two
指针的偏移问题
指针的偏移加减操作,并不是一个字节一个字节地去移动,而是根据你所定义的指针所指向的类型数据去移动该数据类型的数据所占字节数。 而不是简单的进行一个字节的偏移。
简单点举个例子:
干货number three
数组引用时形如 [ ] [ ] []的叫做变址运算符,即将a[i]按a+i计算地址,然后找出地址单元中的值,这就不难理解为什么 a [ i ] ⟺ ∗ ( a + i ) a[i]\Longleftrightarrow *(a+i) a[i]⟺∗(a+i)了。这里也会设计后面多维数组的指针引用章节的学习理解,大家应该注意。
这里先讲一个比较重要的概念,一个关于C程序员的内功提升的点。
我们知道计算机对于一维数据的存储是占用连续的存储空间的,那对于多维的数据又是怎样存储的呢?
(以二维数据为例)像我们平时所见到的二维平面的存储形式:这样???
NO NO NO!!! 在内存里不存在的,操作系统只会提供连续的存储空间,尽管你在函数说明语句就已经“通知”它你要存二维数组了,可他是真没能力去给你开垦耕地。
接下来引入“降维”的概念:
这里简单点说就是,一维套一维来实现多维。我们可以这样理解,
比如有一个二维数组arr[3][4],在存储时就为一个有三个元素的一维数组,并且三个元素是包含四个数据的一维数组。在内存中仍占用连续的存储空间。
这一点是要先声明的,我们接下来讨论的指针引用二维数组可能会误导大家认为存储的真实形式,切记占用连续的存储空间。
提笔还真不知道从哪写起……
还是先从最经典的这张图谈起吧!
我们看到这个二维数组与一维数组引用最大的不同是引入了行列索引的指向,也就是上面横线箭头与竖向箭头。
下面放上几个地址的表示,帮助大家更好地理解
表示形式 | 含义 | 值 |
---|---|---|
a | 二维数组名,0行起始地址 | 2000 |
a[0] | $12 | |
导管 | $1 |