指针虽然是一个重要的概念,但是一点也不复杂。但他的确是C语言的精华。

正确而灵活的运用它,可以有效的表示复杂的数据结构;能动态的分配内存;能方便的使用字符串,有效而方便的使用数组。

为了说清楚什么是指针,必须弄清楚数据在内存中是如何存储的,又是如何读取的。

那就从一个美丽的故事开始吧。。。。

比如,昨天小A去图书馆借一本“我是美女”这本书的时候,在路上不小心碰到了一个帅哥,然后小A就非常勇敢的冲了上去一把掏出手机,文了该帅哥加的电话号码和地址,然后帅哥觉得小A实在长相一般不肯告诉他家的电话号码,就只是说了他家的地址,他是住在单身公寓的102号房,那这样小A就可以去找他了哈哈,因为小A已经知道了他家的地址了,那这就跟我们的内存挂钩了,我们知道内存有两个属性,第一个内存是一个线性的存储空间,有两个属性一个是地址,另一个是地址上边存放的数据。OK,内存是用来存放数据的,就像我们的地址,每一个门牌号码都有一个家,家里面就会放帅哥,就是一样的道理。我们通过地址才能够索引到某个我们要索引的内存,如果我们不知道他们的地址门牌号我们应该怎么找呢,不能一个个挨家敲门吧。所以,我们的内存都是有编好地址的,所以只要在固定的地址里面它会有对应的数据。我们存数据就是靠这个地址来索引的,那我就知道我的东西存在哪里了,也知道我的东西要去哪里拿。

内存区的每一个字节都有一个编号,这就是“地址”。如果在程序中定义了一个变量,在对程序进行编译时,系统就会自动的给这个变量分配内存单元。这叫做局部变量。

在C语言中,对变量的访问有两种方式,直接访问和间接访问。

举个例子就是:

为了开一个A抽屉,有两种方法:

(1)一种方法是将A钥匙带在身上,需要时直接找出该钥匙打开抽屉,取出所需的东西。-----直接访问

直接访问如:a=5;

系统在编译时,已经对变量分配了地址,例如若变量a分配的地址是2000,则该语句的作用就是把常数5保存到地址为2000的单元。(我们说内存中有两个属性嘛,一个是地址,然后和地址对应的空间存放的一个数据,那他地址就是2000存放的数据就是5,这个变量呢我们称之为a。)

(2)另外一种方法是:为安全起见,将A钥匙放到另外一个抽屉B中锁起来。如果需要打开A抽屉,就需要先找出B钥匙,打开B抽屉,取出A钥匙,再打开A抽屉,取出A抽屉中之物。-----间接访问

间接访问如:scanf("%d",&a);//这个函数的作用读取一个整形的变量,把他的值赋值给a

当我们调用这个函数的时候,把变量a的地址传递给函数scanf,函数首先把该地址保存到一个单元中,然后把从键盘接收的数据通过所存储的地址保存到变量a中。

1、初识指针

在C语言中指针是一种特殊的变量,它存放的是地址(一般变量存放的是值嘛,例如刚才a=5,变量a存放的是5;但是我们的指针他是一个特殊的变量它是存放地址的,叫做指针变量)。假设我们定义了一个指针变量,int *i_pointer用来存放整型变量i的地址。可以通过语句i_pointer=&i;(取得i的地址把他赋值给i_pointer这个指针变量)

如上图:假设变量i,变量j,变量k他们的地址分别是2000,2002,2004;它里边存放的数据分别为3、6、9;而我们定义的这个指针变量呢它里边存放的是一个地址,我们这里通过这一句i_pointer=&i,使得变量i的地址存放到了B处,所以B处存放的是i的地址,指针变量也是变量,数组变量也是变量,它跟变量是一样的道理,只是指针变量存放的是一个地址,而我们普通变量存放的是一个值。我们只需要把他灵活的应用就很NB了。

将i的地址(2000)存放到i_point中。这时,i_pointer的值就是(2000)即变量i所占用单元的起始地址。(因为不同类型在内存中占的字节数是不同的)

要存取变量i的值,可以采用间接方式:先找到存放“i的地址”的变量i_pointer,从中取出i的地址(2000),然后取出i的值3。见下图:直接的话,直接i=3;就取出来了。

*:叫做取值操作符;

&:叫做取地址操作符;(后边会详细分析这两个操作负要怎么用,目前只是基本使用方法)

int i=2000;//i的值为2000
int *pointer;//我们声明一个指针pointer//此处的*号并不是取值操作符,这个是申明它为指针的一个特征
pointer=&i;//在这里取出i的地址,比如说他的地址是1000,就把他的地址给了变量pointer//这个是取址
printf("%d\n",*pointer)//那我通过这个*号,取出指向这个地址的里边的值,也就是打印出来的值应该是2000//这个才是取值操作

知道了一个变量的地址,就可以通过这个地址来访问这个变量,因此又把变量的地址称为该变量的”指针“。指针指向的是一个地址。而指针变量呢,C语言中定义一类特殊的变量这些变量专门用来存放变量的地址,称为指针变量。(所以地址和变量是完全不同的两回事。)

注意:指针变量的值(即指针变量中存放的值是地址(即指针))要区分“指针(是一个地址)”和“指针变量(是一个变量)”这两个概念。

//定义一个指针变量
float *pointer_3;//pointer_3是指向float型变量的指针变量//pomiter_3是变量名而不是*pointer_3//*号只是起到表明它是指针
//*表示定义,*可以表示声明定义一个指针,如果不在声明的情况下,它是取值操作符

//下边可以使用赋值语句使一个指针变量得到另一个变量的地址,从而使他指向一个该变量。如:

首先我们的pointer_1里面存放的是i的地址,所以它指向的是i这个变量;pointer_2存放的是j的地址,所以指向的是j这个变量。那么如果有如下语句:

pointer_1=pointer_2;//就是将pointer_2这个变量里面的内容把他存放覆盖了pointer_1这个里面的内容。所以此时pointer_1里边存放的不再是i的地址了,而是j的地址了。而此时pointer_1指向了j。当然pointer_2没有发生改变还是指向的j;也就是说现在i被人抛弃了而两个都直奔j了。

指针变量的前面的*号表示改变量的类型是指针变量。即类型说明符 *变量名;

在定义指针变量时,必须指定基类型。特别注意的是:只有整型变量的地址才能放到指向整型变量的指针变量中。

请牢记,指针变量中只能存放地址(指针),不要将一个整数(或任何其他非地址类型的数据)赋给一个指针变量,否则编译器也会把该值当成一个地址来处理。

&变量名;//地址运算符&来表示变量的地址。

2、指针做函数参数

输入a,b两个整数,按大小顺序输出

#include<stdio.h>
//交换a,b的值
void swap(int *p1,int *p2);//行参定义的指向整数型的指针变量void main()
{int a,b;int *pointer_1, *pointer_2;scanf("%d %d",&a,&b);pointer_1=&a;//pointer_1该指针变量指向的是a。pointer_2=&b;if(a<b){swap(pointer_1,pointer_2);//所以实参需要传入的是????指针呢?还是指针变量。pointer_1叫做指针变量}printf("\n%d > %d\n",a,b);
}
void swap(int *p1,int *p2)
{int temp;printf("I'm swap....\n");temp=*p1;*p1=*p2;*p2=temp;
}

输入a,b,c三个整数,按大小顺序输出

#include<stdio.h>
//交换a,b的值
void exchange(int *q1,int *q2,int q3);//使得a>b>c。void main()
{int a,b,c;int *p1, *p2,*p3;scanf("%d %d %d",&a,&b,&c);p1=&a;//p1指针指向a的地址p2=&b;p3=&c;exchange(p1,p2,p3);//与上边定义相比,是做了int *q1=p1;这个操作。(行参和实参的一个交换就相当于一个赋值操作,他们是通过栈来完成的)printf("%d %d %d\n",a,b,c);
}
void exchange(int *q1,int *q2,int *q3)
{void swap(int *pt1,int *pt2);//用于交换if(*q1<*q2)//p1和q1都是指向a变量的//即如果a<b的话交换{ swap(q1,q2);}if(*q1<*q3)//a如果小于c的话c,交换{ swap(q1,q3);}if(*q2<*q3)//b如果小于c的话再交换{ swap(q2,q3);}}
void swap(int *pt1,int *pt2)
{int temp;temp=*pt1;*pt1=*pt2;*pt2=temp;
}

3、数组与指针

(因为数组和指针都是指向了一个地址,数组是比较稳定的,而指针是随时可变的;数组在定义的时候就占用了空间,而指针是一个变量,指针的空间随时可以消除,而数组不能)

一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元(而且他们是连续的),他们都有相应的地址。

指针变量(里边存放的是变量的地址)既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中)

所谓数组元素的指针就是数组元素的地址。

定义一个指向数组元素的指针变量的方法,与以前介绍的指向变量的指针变量相同。

int a[10];//定义a为包含10个整型数据的数组
int *p;//定义p为指向整型变量的指针变量
//应当注意,如果数组为int型,则指针变量的基类型也应为int型
p=&a[2];//指针指向数组的第三个元素;把a[2]元素的地址赋给指针变量p,也就是说把p指向a数组的第2号元素。
p++;//表示

引用数组元素,可以用下标法也可以用指针法:

(1)下标法:a[i]的形式;

(2)指针法:*(a+1)或者*(p+i);//因为a是第一个元素的地址,其中的加i就是指向的是第i个元素,不是说地址加i,因为要看什么类型。

其中a是数组名,p是指向数组元素的指针变量,其初值p=a即p=&a[0];

注意:数组名(在编译器是编译为一个地址的)即“编译成数组的第一个元素的地址。”(这就是上边说的相似的地方)

用数组名做函数的参数--------

如:

void f(int arr[],int n)
{......
}
void main()
{int arr[10];... ...f(arr,10);//数组名相当于该数组的首地址
}

上述中的,f(int arr[],int n),在编译时是将arr按照指针变量处理的,相当于将函数f的首部写成f(int *arr,int n)所以以上这两种写法是等价的。

需要说明的是:C语言调用函数时虚实结合的方法,都是采用值传递方式,当用变量名作为函数参数时,传递的是变量的值;当用数组名作为函数参数时,由于数组名代表的是数组首元素地址,因此传递的值是地址,所以要求形参为指针变量。

例子:将数组a中n个整数按相反顺序存放。

#include<stdio.h>
void reverse(int x[],int n)//形参x是数组名
void main()
{int i, a[10]={3,5,7,8,1,0,2,6,9,4};printf(" The original array:\n");for(i=0,i<10,i++){printf("%d",a[i]);}printf("\n");reverse(a,10);printf("The Array has been inverted:\n");
}
void reverse(int x[],int n)
{int temp,i,j,m;m=(n-1)/2;for(i=0;i<=m;i++){j=n-1-i;temp=x[i];x[i]=x[j];x[j]=temp;}
}

改变一下用指针做函数的参数:

#include<stdio.h>
void reverse(int *x,int n)//形参x为指针变量
void main()
{int i, a[10]={3,5,7,8,1,0,2,6,9,4};printf(" The original array:\n");for(i=0,i<10,i++){printf("%d",a[i]);}printf("\n");reverse(a,10);printf("The Array has been inverted:\n");
}
void reverse(int *x,int n)//形参x为指针变量
{int *p,temp,*i,*j,m;m=(n-1)/2;//中间序号i=x;//前边的序号//i指向数组的第一个元素j=x-1+n;//后边的序号//j指向数组的最后一个元素p=x+m;//指向中间配对for(;i<=p;i++,j--){temp=*i;*i=*j;*j=temp;}
}

用指针做参数------

例子:从10个数中找出最大值和最小值。

#include<stdio.h>
int max,min;//全局变量
void max_min_values(int array[],int n);
void main()
{int i,number[10];printf("Enter 10 integer numbers:\n");for(i=0;i<10;i++){scanf("%d",&number[i]);}max_min_values(number,10);printf("\nmax=%d,min=%d\n,"max,min);
}
void max_min_values(int array[],int n)
{int *p,*array_end;array_end=array+n;max=min=*array;for(p=array+1;p<array_end;p++){if(*p>max){max=*p;}else if(*p<min){min=*p;}}}

总结:如果有一个数组,想在函数中(子程序)改变此数组中的元素值,实参与形参的对应关系有以下四种:

(1)形参和实参都用数组名:

void main()
{int a[10];f(a,10)
}
void f(int [],int n)
{......
}

(2)实参采用数组名,形参用指针变量。如:

void main()
{int a[10];f(a,10);
}
void f(int *a,int n)
{......
}

(3)实参和行参都用指针变量。如:

void main()
{int a[10];int *p=a;//指针变量p指向数组的首地址f(p,10);//传指针相当于传a数组的地址
}
void f(int *x,int n)//int *x指针相当于指向a的地址,相当于传值的时侯x=p,p指向a那么x也指向a
{......
}

(4)实参为指针变量,行参为数组名。如:

void main()
{int a[10];int *p=a;f(p,10);//1这里把数组的首地址当作实参传过去,
}
void f(int x[],int n)//2定义数组来接收首地址,说明位置起始点一样指向了同一组数组。
{......
}

例子:对一个数组中的元素按照从大到小的顺序排列一下。

#include<stdio.h>
int max,min;//全局变量
void sort(int array[],int n);
void main()
{int *p,i,a[10]={3,7,4,9,2,0,5,8,1,6};printf("The origial array:\n");for(i=0;i<10;i++){  printf("%d",a[i]);}printf("\n");p=a;sort(p,10);printf("The result is:\n");for(p=a;i=0;i<10;i++){printf("%d",*p);p++;}printf("\n");
}
void sort(int x[],int n)
{int i,j,k,t;for(i=0;i<n-1;i++){k=i;//假设第一个是最大的,如果接着的那个比他大那就把他调过来for(j=i+1;j<n;j++){if(x[j]>x[k]){t=x[j];x[j]=x[k];x[k]=t;}}}}

C语言:随笔5--指针1相关推荐

  1. C语言随笔小算法:创建双向链表

    C语言随笔小算法:创建双向链表 双向链表两个指针域!head定住,tail移动! 代码: #include "stdlib.h" #include "stdio.h&qu ...

  2. C语言随笔小算法:char字节流与结构体变量相互转换

    C语言随笔小算法:char字节流与结构体变量相互转换 代码: /* **数据域 */ typedef struct {kal_uint8 bt_dpacket_data_total_num; //(可 ...

  3. C语言随笔小算法:单向链表

    C语言随笔小算法:单向链表 参考链接: 代码参考:https://blog.csdn.net/go_sann/article/details/80508284 原理参考:https://blog.cs ...

  4. c语言中程序偏离,C语言中的指针加减偏移量

    首先看一段程序: #include int main() { int a[5] = {1, 2, 3, 4, 5}; int* p = (int*)(&a + 1); printf(" ...

  5. 各种语言中对指针的叫法

    如果没有指针,就无法构造正统的数据结构, 因此,比较成熟的.正统的编程语言,必定会存在指针3. 3以前, FORTRAN . COBOL 和BASIC 中 都没有指针,但是在 fortran90.Vi ...

  6. Swift3.0语言教程使用指针创建和初始化字符串

    Swift3.0语言教程使用指针创建和初始化字符串 Swift3.0语言教程使用指针创建和初始化字符串苹果的Swift团队花了不少功夫来支持C的一些基础特性.C语言中为我们提供了指针,Swift也不例 ...

  7. 【C 语言】数组 ( 指针数组用法 | 自我结束能力 )

    文章目录 一.指针数组用法 ( 自我结束能力 ) 二.完整代码示例 一.指针数组用法 ( 自我结束能力 ) 在上一篇博客 [C 语言]数组 ( 指针数组用法 | 命令行参数处理 ) 中的主函数中的 c ...

  8. 【C 语言】二级指针案例 ( 字符串切割 | 返回 自定义二级指针 作为结果 | 每个 一级指针 指向不同大小内存 | 精准分配每个 一级指针 指向的内存大小 )

    文章目录 一.二级指针案例 ( 返回自定义二级指针 | 精准控制内存大小 ) 二.完整代码示例 一.二级指针案例 ( 返回自定义二级指针 | 精准控制内存大小 ) 博客 [C 语言]二级指针案例 ( ...

  9. 【C 语言】二级指针案例 ( 字符串切割 | 返回 自定义二级指针 作为结果 )

    文章目录 一.二级指针案例 ( 返回自定义二级指针 ) 二.完整代码示例 一.二级指针案例 ( 返回自定义二级指针 ) 上一篇博客 [C 语言]二级指针案例 ( 字符串切割 | 返回 二维数组 作为结 ...

  10. 【C 语言】二级指针作为输入 ( 自定义二级指针内存 | 二级指针排序 | 通过 交换指针指向的内存数据 方式进行排序 )

    文章目录 一.二维指针 排序 ( 通过 交换指针指向的内存数据 方式进行排序 ) 二.完整代码示例 一.二维指针 排序 ( 通过 交换指针指向的内存数据 方式进行排序 ) 在上一篇博客 [C 语言]二 ...

最新文章

  1. 由Node.js事件驱动模型引发的思考
  2. 用国内镜像源pip加速安装模块
  3. epoll的使用实例
  4. linux下网口监控软件_超赞的!Aibaba技术官分享高性能Linux服务器解读笔记
  5. google账号解除游戏绑定_成长守护平台解除实名认证 公众号解绑操作流程
  6. CCF201403-4 无线网络(100分)
  7. AWS ec2 安装手记
  8. 如何应对阿里、美团、Oracle等大厂的面试刁难?
  9. vue单向数据绑定和双向数据绑定
  10. 闲聊机器人实例四:python实现小姜机器人,BERT检索模型,新增一个余弦相似度Cosine层, 解决BERT句向量编码传输耗时问题。部署tf-serving
  11. Conda / Anaconda : UnavailableInvalidChannel The channel is not accessible or is invalid.
  12. 居中小圆点 html,居中小圆点怎么打出来
  13. mysql简单数据库定期备份
  14. puts()和gets()
  15. Jackknife,Bootstrap, bagging, boosting, AdaBoost, Rand forest 和 gradient boosting的区别
  16. 使用Leaflet绘制上海地铁地图
  17. 免费css布局和模板集合
  18. 一元线性回归及Excel回归分析
  19. Multi-Head Self-Attention里投影矩阵WQ/WK/WV是否共享的问题
  20. 计算机毕业设计Node.js+Vue安卓电影院售票管理APP论文(程序+源码+LW+部署)

热门文章

  1. [C] [最短路] 只有5行的算法:Floyd-Warshall
  2. java equal hashcode_Java(二)equal 和 hashcode使用
  3. Android Intent setAction的使用注意
  4. androidx FloatingActionButton 中间加载的图片显示黑色
  5. Only the original thread that created a view hierarchy can touch its views
  6. Android 侧滑栏 (DrawerLayout)
  7. 电子表单系列谈之表单数据处理
  8. 关于acm的新手一些问题
  9. Ubuntu PyCharm cv2 无代码提示解决方法
  10. Go 学习笔记(4)— Go 标识符、数据类型之间转换、布尔型、整型、浮点型、interface 类型