什么是指针?
指针 == 地址
什么是指针变量
指针变量 == 存放地址的变量
C语言中访问变量的方式
- 变量名(直接访问)
- 地址(间接访问:即将变量i的地址存放在另一变量中,然后通过该变量来找到变量i的地址,从而访问i变量。)
访问的方式可以与生活中类比,比如我们找一个餐馆,如果知道它的名字,可以上地图上去查。或者我们知道它在某个区某个街道某某号我们也可以找到这个餐馆。
&与*运算符
&的用法
取地址运算符
*的用法
- 取值运算符
- 作为标识符,在声明指针变量时,起标识作用
int main()
{
int a = 10;
int *p;//此处的*是一个标识符,表明p是一个指针变量
p = &a;
printf("a的地址:%p\n",&a);
printf("a的值:%d\n",*(&a));//此处的*表示取值运算符,把后面所跟地址中的数据取出来
printf("p的地址:%p\n",&p);
printf("p存放的地址(即a的地址):%p\n",p);
printf("p存放的地址中的内容(即a的内容):%d",*p);
}
既然指针变量存放的是地址,为什么要区分类型?
指针类型决定了指向空间的内存大小,也决定了增量
int main()
{
int a = 0x1234;
int *p = &a;
char *q = &a;
//p,q中存放的地址是相同的
printf("p = %p\n",p);
printf("q = %p\n\n",q);
//由于指针类型不同导致了p,q指向内存空间的大小不同
printf("*p = %x\n",*p);
printf("*q = %x\n\n",*q);
//由于指针类型不同p,q指向内存空间的增量不同
printf("++p = %p\n",++p);
printf("++q = %p",++q);
}
执行结果:
注意:p,q所占内存空间大小是相同的。
为什么要用指针?
封装一个函数,对两个数进行交换。
void changeData1(int data1,int data2){
//1.错误的,交换了两个局部变量的值
int temp;
temp = data1;
data1 = data2;
data2 = temp;
}
void changeData(int *data1,int *data2){
//2.正确的,直接通过指针修改值
int temp;
temp = *data1;
*data1 = *data2;
*data2 = temp;
//3.错误的 交换了两个局部变量的地址
/*int *temp;
temp = data1;
data1 = data2;
data2 = temp;*/
}
int main(){
int data1 = 10;
int data2 = 20;
changeData(&data1,&data2);
printf("data1 = %d\n",data1);
printf("data2 = %d\n",data2);
return 0;
}
通过指针引用数组
int main (){
int arr[3]= { 1,2,3};
int *p;
//数组的首地址就是首个元素的地址&arr[0];
//数组名就是数组的首地址
p = arr[0];
printf(""首元素是:%d\n" ,*p);
return o;
}
指针增量与数组的关系:
访问数组元素的方式
- 下标法
- 指针法
1. 先偏移
2. 然后取内容
问题一:我们可以使用数组名+i来进行偏移,也可以使用指针存储数组名的方式来偏移。那么
数组名和指针的区别在哪呢?
- 指针是存储地址的变量,而数组名可以理解为一个指针常量。也就是说数组名本身的值是不能改变的,数组名不能做自增,自减操作。
- 使用sizeof关键字时,数组名和存放了数组名地址的指针大小不同。
问题二:两种访问数组元素的方式谁的效率更高?
对于使用指针和数组下标的选择:
系统在使用数组下标对数组成员变量进行访问时,开销比较大,指针的访问效率是远远大于数组名的访问效率的。
但是只有在指针正确访问时,才成比下标法更有效率。
下标法更加容易理解,在可读性方面,也更加的具有优势,具体怎么选择,也没有一定的说法。
指针和二维数组
父子数组是二维数组中特有的说法。
二维数组本质上还是数组,与一维数组不同的是其数组元素是也是一个数组。
那么a,a[0],a[0][0],*a,*a[0]
数组指针
声明:
dataType (* pointerName)[Length];
(),[]运算符优先级都是1,并且结合方向是从左到右的,以使用运算符结合性优先级来理解,p先与()内的指针标识符*结合,故而其是一个指针。
#include <stdio.h>
int main()
{
int arr[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
//此处会出现warning 因为数据类型不同,p是int ,与arr增量的跨度不同
int *p = arr;
//能不能定义一个指针让其偏移量与arr相同?
//数组指针,定义一个指针指向一个数组
//数组指针才真正等同于二维数组名
int (*p1)[4] = arr;
int i,j;
for(i = 0;i < 3;i++){
for(j = 0;j < 4;j++){
//下列方式也可以成功输出,但这相当于一维数组的方式
//等价于3*4个元素的一维数组
//printf("%d\n",*p++);
printf("%d\n",*(*(p1+i)+j));
}
}
return 0;
}
函数指针
声明:
returnType (*pointerName)(param list);
函数地址:
如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配一段存储空间,这段存储空间的起始地址(又称入口地址)称为这个函数的指针。
与数组名类似,数组名是地址,函数名也是地址。
#include <stdio.h>
void demo1(){
printf("%d\n",10);
}
int demo2(int data){
return data;
}
int main()
{
void (*p)() = demo1;
int (*p1)(int) = demo2;
(*p)();
printf("%d",(*p1)(20));
return 0;
}
函数回调案例:
#include <stdio.h>
#include <stdlib.h>
int gMax(int data1,int data2){
return data1 > data2 ? data1 : data2;
}
int gMin(int data1,int data2){
return data1 > data2 ? data2 : data1;
}
int gSum(int data1,int data2){
return data1 + data2;
}
int dataHandler(int data1,int data2,int (*pfunc)(int,int)){
int ret;
ret = (*pfunc)(data1,data2);
return ret;
}
int main()
{
int data1 = 10;
int data2 = 20;
int cmd;
int ret;
int (*pfunc)(int,int);
puts("输入指令:1(取最大值),2(取最小值),3(求和)");
scanf("%d",&cmd);
switch(cmd){
case 1:
pfunc = gMax;
break;
case 2:
pfunc = gMin;
break;
case 3:
pfunc = gSum;
break;
default:
puts("输入错误!@1(取最大值),2(取最小值),3(求和)");
exit(-1);
}
printf("结果为:%d\n",dataHandler(data1,data2,pfunc));
return 0;
}
指针数组
定义:
dataType * pointerName[Length];
int * p[4];
由于[]比* 优先级高,因此p先与[4]结合,形成p[4]形式,这显然是数组形式,表示p数组有4个元素。然后再与p前面的“ * ”结合,“ * ”表示此数组是指针类型的,每个数组元素(相当于一个指针变量)都可指向一个整型变量。
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
int d = 40;
int size;
int *arr[] = {&a,&b,&c,&d};
int i;
size = sizeof(arr)/sizeof(int *);
for(i = 0;i < size;i++){
//内层的*:取出指针数组中的元素
//外层的*:取出元素中的值
printf("%d\n",*(*(arr+i)));
}
return 0;
}
注意指针数组与数组指针的区别。
指针函数
一个函数可以返回–个整型值、字符值、实型值等,也可以返回指针型的数据,即地址。其概念与以前类似,只是返回的值的类型是指针类型而已。
例如“int * a(int x,int y);”,a是函数名,调用它以后能得到一个int型(指向整型数据)的指针,即整型数据的地址。x和y是函数a的形参,为整型。
请注意在 a两侧没有括号,在a的两侧分别为* 运算符和()运算符。而()优先级高于* ,因此a先与()结合,显然这是函数形式。这个函数前面有一个* ,表示此函数是指针型函数(函数值是指针)。最前面的int表示返回的指针指向整型变量。
#include <stdio.h>
#include <stdlib.h>
#define row 3
#define column 4
//使用malloc申请空间的指针p
//对指针p进行赋值
//sizeof(p) 的值为一个指针实际所占字节数
//不是申请时的大小
/*
a个学生,b科成绩
找出有不及格课程的学生及其学生号
用指针函数实现
*/
void printPos(int *arr){
int size = 2;
int i;
for(i = 0;i < size;i++){
printf("%d ",*arr++);
}
}
int *findFailStuNum(int (*p)[column]){
int *posStu = (int *)malloc(sizeof(int) * row);
//int temp[row] = {};
int index = 0;
int i,j;
for(i = 0;i < row;i++){
for(j = 0;j < column;j++){
if(*(*(p + i)+j) < 60){
*(posStu + index++) = i;
break;
}
}
}
return posStu;
}
int main()
{
int arr[row][column] = {{11,22,33,44},{99,66,77,88},{33,44,55,100}};
int *posStu;
posStu = findFailStuNum(arr);
puts("不及格学生序号为:");
printPos(posStu);
free(posStu);
return 0;
}
二级(多级)指针
dataType ** pointerName;
int **p;
与一级指针几乎一致,不同的是二级指针保存的是一级指针的地址。
#include <stdio.h>
#include <stdlib.h>
/*
a个学生,b科成绩
输入学生序号,打印成绩
用二级指针实现
*/
void getPos(int pos,int (*p)[4],int **posStu)
{
*posStu = *(p+pos);
}
void printPos(int len,int *arr){
int i;
for(i = 0;i < len;i++){
printf("%d ",*(arr+i));
}
}
int main()
{
int arr[3][4] = {{11,22,33,44},{55,66,77,88},{33,44,55,100}};
int pos;
int row;
int column;
int *posStu;
row = sizeof(arr)/sizeof(*arr);
column = sizeof(*arr)/sizeof(int);
puts("输入学生序号(1,2,3):");
scanf("%d",&pos);
if(pos > row || pos <= 0){
printf("输入学生序号不存在\n");
exit(-1);
}
getPos(pos,arr,&posStu);
printPos(column,posStu);
return 0;
}
注意:二级指针和二维数组没有什么必然联系,二级指针不能直接指向二维数组。