目录
一、初步认识指针(一级)
二、数组指针
1.一维数组与指针
2.二维数组与指针
三、函数指针
四、指针数组
2.函数指针数组
五、指针函数
六、二级(多级)指针
七、指针定义的归纳
一、初步认识指针(一级)
1.指针变量: 指针变量是一个特殊的变量,这个变量存储的是一个地址,也可以说指针就是一个地址。
2.取值运算符: *(&a)取值运算符,他把后面跟的内存地址中的数据”取出来“
3.说明符:int a = *P, 说明符,说明这个p是一个存放地址的整型指针变量
定义格式:类型 *(说明符) 指针名 例如 int *a、char *b
#include<stdio.h>
int main()
{
//指针变量 存放指针 地址的变量
//指针变量==存放地址的变量
int a = 10;
int *P;//(*标识符)标识作用声明与定义 整型的指针变量 保存别人地址
p = &a;//p存放是地址,&a是a的地址
printf("%d\n",a);
printf("0x%p\n",&a);//0x 地址十六进制
printf("%d\n",*(&a));//取值(运算符)运算作用,他把后面跟的内存地址中的数据”取出来“
printf("%d\n",*p);//取值运算符
return 0;
}
4.指针类型:既然存放的是地址那为什么要有类型呢?难道地址有类型?当然不是,不管什么类型,地址都是4个字节。取值运算符会根据指针变量类型,访问不同大小的空间。
例如:int *P访问4个字节、char *p访问1个字节如果char *p存放int 类型的变量的地址,那么char类型指针只能访问int类型变量(int类型 四个字节)的一个字节,从而丢失了三个字节。
#include <stdio.h>
int main()
{
int a = 1234;
int *p = &a;
char *c = &a;//char类型指针 存放int类型变量
printf("p = %p\n",p);//输出地址
printf("c = %p\n",c);
printf("a = %d\n",*p);//类型相同,输出正确
printf("a = %d\n",*c);//变量类型与存放地址指针类型不同,空间大小不匹配,输出错误数据
printf("++p = %p\n",++p);//自加1 类型所以含有的空间大小为一(4个字节)
printf("++c = %p\n",++c);//(一个字节)
return 0;
}
5.为什么需要指针:指针的使用使得不同区域的代码可以轻易的共享内存数据,因为操作的是同一个地址上的数据。
#including<stdio.h>
void chageData(int *pdata, int *pdata2)
//用指针 给到的是与main相同的地址 所以交换的数值是在同一个地址上的数据
//不用指针 操作的是在新的空间地址上
{
int tmp;
tmp = *pdata;
*pdata = *pdata2;
*pdata2 = tmp;
}
int main()
{
int data = 10;
int data2 = 20;
printf("why point\n");
printf("交换前:data=%d,data2=%d\n",data,data2);
chageData(&data,&data2);//操作的是同一个地址上的数据
printf("交换后:data=%d,data2=%d\n",data,data2);
}
二、数组指针
是一个指针,指向数组的指针。
1.一维数组与指针
1.指针的增量与数组的关系
#include <stdio.h>
int main()
{
int arr[3] = {1,2,3};
int *p;
//数组名就是数组的首地址,数组的首地址就是首个元素的地址相当于p = &arr[0];
p = arr;
for(int i = 0; i<3; i++){// 第一种输出各个指针
//printf("address:0x%p,%d \n",(p+i),*(p+i));//输出地址与数值
printf("%d \n",*p);
p++;//指针偏移加1,指向下一个地址(下一个数组元素)
}
p = arr;//防止越界 p+4越界出错
for(int i = 0; i<3; i++){//第二种输出各个指针
//printf("address:0x%p,%d \n",(p+i),*(p+i));//输出地址与数值
printf("%d ",*p++);
}
/*
printf("0元素是:%d\n",*(p+0));// +i 地址偏移一个类型大小
printf("1元素是:%d\n",*(p+1));
printf("2元素是:%d\n",*(p+2));
*/
return 0;
}
运行结果:
2.指针与数组名的奇怪用法
#include <stdio.h>
int main()
{
int arr[3] = {1,2,3};//数组
int *p = arr;//指针变量
//sizeof计算大小,单位字节
printf("sizeof arr is %d\n",sizeof(arr));//3*4=12
printf("sizeof arr is %d\n",sizeof(p));// 指针都用8个字节表示一个地址
printf("sizeof int is %d\n",sizeof(int));//4个字节
printf("sizeof pointer is %d\n",sizeof(int *));// 用8个字节表示一个地址
printf("sizeof pointer is %d\n",sizeof(char *));// 用8个字节表示一个地址
//printf("%d ",p[2]);
printf("%d \n",*arr);//取首地址的元素
for(int i = 0; i<3; i++){
printf("%d ",p[i]);//(2)指针当作数组名用
}
putchar('\n');
for(int i = 0; i<3; i++){//(3)数组拿来加
printf("%d ",*(arr+i));
}
putchar('\n');
/*
for(int i = 0; i<3; i++){
printf("%d ",*arr++);//编译不过,指针常量 数组为固定大小空间数值无法改变与指针不同
}*/
putchar('\n');
return 0;
}
运行结果:
3.指针代替数组函数分装
#include <stdio.h>
void initArray(int *parr, int size)//传递的数组用指针代替 parr为地址
//指针间接的在main空间里面操作
{
int i;
for(i=0;i<size;i++){
printf("请输入第%i个元素的数据:\n",i+1);
scanf("%d",parr++);//输完之后加(地址)
}
}
void printArray(int *parr, int size)
{
int i;
for(i=0;i<size;i++){
printf("%d ",*parr++);//取地址的值
}
}
int main()
{
int arry[5];
int size = sizeof(arry)/sizeof(arry[0]);
initArray(arry,size);//实际参数,数组的首地址: 名,首个元素的地址
printArray(&arry[0],size);
return 0;
}
运行结果:
4.例:将输入数据按倒序输出
想法:数字是数组下标
#include <stdio.h>
//name , params, returnValue
void initArray(int *parr, int size)
{
int i;
for(i=0;i<size;i++){
printf("请输入第%i个元素的数据:\n",i+1);
scanf("%d",parr++);
}
}
void revangeArry(int *parr, int size)
{
int i,j;
int tmp;
for(i=0;i<size/2;i++){//将数组元素按上图交换
j = size-1-i;
tmp = parr[i];
parr[i] = parr[j];
parr[j] = tmp;
}
}
void printArray(int *parr, int size)
{
int i;
for(i=0;i<size;i++){
printf("%d ",*parr++);
}
putchar('\n');
}
int main()
{
int arry[5];
int size = sizeof(arry)/sizeof(arry[0]);
initArray(arry,size);//实际参数,数组的首地址: 名,首个元素的地址
printArray(&arry[0],size);//输出数组
revangeArry(arry,size);//把数组倒序
printArray(&arry[0],size);//输出倒序数组
return 0;
}
运行结果:
2.二维数组与指针
1.说明介绍
二维数组本质还是数组,不同点是二维数组元素还是个数组(子数组)
a表示父数组的地址,a[0],*a表示子数组的地址
各种表示形式的含义
2.数组名与偏移与地址
#include <stdio.h>
int main()
{
int arr[3][4] = {{11,22,33,44},{12,13,15,16},{22,66,77,88}};
int arr1[4] = {11,22,33,44};
//二维
printf("1arr是父亲地址:%p,偏移1后是%p\n",arr, arr+1);//偏移4列*4字节=16
printf("2arr[0]是子数组地址;%p,偏移1后是%p\n",arr[0],arr[0]+1);//4字节
//对二维数组名取内容=数组的首地址
printf("3arr[0]是子数组地址;%p,偏移1后是%p\n",*(arr+0),*(arr+0)+1);//4字节
printf("4arr[0]是子数组地址;%P,偏移1后是%p\n",*arr,*(arr+1));
printf("5arr[0]是子数组地址;%d,偏移1后是%d\n",*arr,*(arr+1));//不可取值
//一维
printf("6arr[0]是子数组地址;%p,偏移1后是%p\n",arr1[0],(arr1[0]+1));//错
printf("7arr[0]是子数组地址;%p,偏移1后是%p\n",*(arr1+0),*((arr1+0)+1));
printf("8arr[0]是子数组地址;%P,偏移1后是%P\n",*arr1,*(arr1+1));
printf("9arr[0]是子数组地址;%d,偏移1后是%d\n",*arr1,*(arr1+1));//可取值
return 0;
}
运行结果:
3.输出数组地址数值
#include <stdio.h>
//arr,arr[0]
int main()
{
int arr[3][2] = {{11,22},{32,44},{55,66}};
int i;
int j;
for(i=0;i<3;i++){
for(j=0;j<4;j++){
printf("add:0x%p,data:%d \n",&arr[i][j],arr[i][j]);//输出方式一
printf("add:0x%p,data:%d \n",arr[i]+j,*(arr[i]+j));//方式二
printf("add:0x%p,data:%d \n",*(arr+i)+j,*(*(arr+i)+j));//方式三 arr[i]地址==*(arr+i)地址
printf("===========================================\n");
}
putchar('\n');
}
}
运行结果:
4. 数组指针
数组指针定义:int (*p2)[i];
int 类型,p2是数组指针名,i是二维数组列数
#include <stdio.h>
//arr,arr[0]
int main()
{
int arr[3][4] = {{11,22,33,44},{12,13,15,16},{22,66,77,88}};
int i,j;
//int *p;
//p++不等arr++
//p = &arr[0][0];
//p = arr;
/*能不能定义一个指针,让指针偏移的时候,也偏移对应大小的数组?
数组指针,定义一个指针,指向一个数组!
数组指针才是真正等同于二维数组名 arr++=p2++*/
int (*p2)[4];//数组指针
p2 = arr;
//printf("p2=%p\n",p2);
//printf("++p2=%p\n",++p2);//先加 偏移16 arr++=p2++
for(i=0;i<3;i++){
for(j=0;j<4;j++){
printf("%d\n",*(*(p2+i)+j));//*(*(arr+i)+j)=*(arr[i]+j)
}
}
}
运行结果:
5.例:输入行列用指针取数组值
#include <stdio.h>
int getTheData(int (*p)[4],int hang,int lie)//了解二维数组对应的数组指针的用法
{
int data;
data = *(*(p+hang-1)+lie-1);
return data;
//return p[hang-1][lie-1];//把p当作数组名用
}
void tipsInputHangLie(int *pm, int *pn)
{
printf("输入行列值:\n");
scanf("%d%d",pm,pn);
puts("done!");
}
//arr,arr[0]
int main()
{
int arr[3][4] = {{11,22,33,44},{12,13,15,16},{22,66,77,88}};//arr+
int ihang,ilie;
int data;
tipsInputHangLie(&ihang,&ilie);//输入行列值
data = getTheData(arr,ihang,ilie);//找出对应行列值的那个数
printf("%d行%d列的值是%d\n",ihang,ilie,data);//输出这个数
}
运行结果:
三、函数指针
指向函数的指针,函数名就是地址。
函数指针定义:int (*p2)(x);
int返回值类型,p2函数指针名,x是传递参数的类型与名(例如int data)
1.函数指针定义与调用
#include<stdio.h>
int invdata(int data)
{
return ++data;
}
void printwelcome()
{
puts("欢迎来到地球");
}
int main()
{
int a = 10;
int *pa;
pa = &a;
printf("%d\n",*pa);
int (*p2)(int data);//定义一个函数指针变量
p2 = invdata;
(*p2)(10);//调用传递实参
printf("%d\n",(*p2)(10));
void (*P)();//定义一个函数指针变量
P = printwelcome;//对指针给地址
//void (*P)(printwelcome);//定义
printwelcome();//调用
(*P)();//调用
return 0;
}
运行结果:
四、指针数组
是一个数组 ,数组里每一个元素都是一个地址(相当于由一群指针变量组成的数组)
1.指针数组的定义及使用
#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
int d = 40;
int *p[4] = {&a,&b,&c,&d};//定义指针数组
for(int i=0;i<4;i++)
{
printf("%d ",*(p[i]));
}
return 0;
}
运行结果:
2.函数指针数组
由函数指针组成的数组。
函数指针数组定义:int (*pfunc[ i ])(x)
int是类型,pfunc函数指针数组名称,i是元素个数,x是传递参数的类型与名
#include <stdio.h>
#include <stdlib.h>
int getMax(int data1, int data2)//返回大值
{
return data1>data2 ? data1:data2;
}
int getMin(int data1, int data2)//返回小值
{
return data1<data2 ? data1:data2;
}
int getSum(int data1, int data2)//返回和值
{
return data1+data2;
}
int main()
{
int a = 10;
int b = 20;
int ret;
int (*pfunc[3])(int , int )={getMin,getMax,getSum};//定义函数指针数组
for(int i=0;i<3;i++){//调用3个函数
ret = (*pfunc[i])(a,b);
printf("ret = %d\n",ret);
}
return 0;
}
运行结果:
五、指针函数
返回值为指针的函数。
1.例子
#include <stdio.h>
int *getPosPerson(int pos,int (*pstu)[4])//指针函数 返回一个指针
{
int *p;
p = (int *)(pos + pstu);//(int * )转成整型指针(ins)整型
return p;//返回子数组的地址
}
int main()
{
int scores[3][4]={
{55,66,77,88},
{66,55,99,100},
{11,22,33,59},
};
int *ppos;
int pos;
printf("请输入你需要看的学生号数:0,1,2\n");
scanf("%d",&pos);
ppos = getPosPerson(pos, scores);
for(int i=0;i<4;i++){//输出子数组数据
printf("%d ",*ppos++);
}
return 0;
}
运行结果:
六、二级(多级)指针
二级指针内存存放的是下一个指针的地址,而下一个指针内存存放数据的地址。
如同俄罗斯套娃下一个指针存放上一个指针的地址
1.二级(多级)指针的定义及使用
#include <stdio.h>
int main()
{
int data = 100;
int *p = &data;//一层
printf("data的地址是:%p\n",&data);
printf("p保存data的地址:%p,内容是%d\n",p,*p);
printf("p的地址是:%p\n",&p);
/*可以用一级指针变量存放指针变量的地址,但是使用有缺陷,无法获得最终地址内容
printf("p的地址是:%p\n",&p);
int *pp = &p;
printf("pp保存p的地址:%p\n",pp);
printf("*pp是%p\n",*pp);*///得到的是dara的地址不是data的值 *p存放的地址
printf("\n");
int **p2;//定义二级指针
p2 = &p;//二层,二级指针存放一级指针的地址
printf("p2保存p的地址:%p\n",p2);
printf("*p2是%p\n",*p2);
printf("**p2来访问data:%d\n",**p2);
printf("p2的地址是:%p\n",&p2);
printf("\n");
int ***p3;//定义三级指针
p3 = &p2;//三层,三级指针存放二级指针的地址
printf("p3保存p2的地址:%p\n",p3);
printf("*p3是%p2\n",*p3);
printf("***p3来访问data:%d\n",***p3);
return 0;
}
运行结果:
2.例子
#include <stdio.h>
void getPosPerson(int pos, int (*pstu)[4],int **ppos)//二级指针
{
*ppos = (int *)(pstu+pos);//把地址存放在一级指针的内存里
}
int main()
{
int scores[3][4]={
{55,66,77,88},
{66,55,99,100},
{11,22,33,59},
};
int *ppos;//野指针 空指针 不指向任何地址
int pos;
printf("请输入你需要看的学生号数:0,1,2\n");
scanf("%d",&pos);
getPosPerson(pos,scores,&ppos);
for(int i=0;i<4;i++){
printf("%d ",*ppos++);
}
return 0;
}
运行结果:
七、指针定义的归纳
1.各种指针的定义
2.各种指针变量的含义