指针篇
指针就是地址,地址就是指针 指针变量就是存放地址的变量
*号只有定义的时候表示定义指针变量,其他表示从地址里面取内容
通过指针的方法使main函数中的data1和data2发生数据交换。
#include <stdio.h>
void chang_data(int *data1,int *data2)
{
int tmp;
tmp = *data1;
*data1 = *data2;
*data2 = tmp;
}
int main()
{
int data1 = 10;
int data2 = 20;
chang_data(&data1,&data2);
printf("data1:%d data2:%d\n",data1,data2);
return 0;
}
============================================================================
指针指向固定的区域
volatile :防止编译器进行编译优化,导致原来的值发生变化
#include <stdio.h>
int main()
{
volatile unsigned int *p = (volatile unsigned int *)0x0000000001234567;
printf("p:%p\n",p);
return 0;
}
============================================================================
输入三个数,要求不管怎么输入,在输出的时候要由大到小输出,用函数封装的方法
#include <stdio.h>
int func(int *data1,int *data2,int *data3)
{
int tmp;
if(*data1 < *data2){
tmp = *data1;
*data1 = *data2;
*data2 = tmp;
}
if(*data1 < *data3){
tmp = *data1;
*data1 = *data3;
*data3 = tmp;
}
if(*data2 < *data3){
tmp = *data2;
*data2 = *data3;
*data3 = tmp;
}
}
int main()
{
int data1;
int data2;
int data3;
scanf("%d %d %d",&data1,&data2,&data3);
func(&data1,&data2,&data3);
printf("大到小:%d %d %d \n",data1,data2,data3);
return 0;
}
============================================================================
1.定义一个指针变量指向数组
在C语言当中,数组名(不包括形参数组名)代表数组中首元素的地址,所以p = &a[0];//或者p =a;这两个等价的
2. 指针的偏移,偏移多少根据指针的类型,int偏移四个字节,char就偏移一个字节,使用的指针偏移遍历数组
3.指针的访问效率是远远大于数组下标的访问效率的
int main()
{
int i = 0;
int a[] = {1,2,3};
int *p;
p = &a[0];//或者p = a;
for(i=0;i<sizeof(a)/sizeof(a[0]);i++){
printf("a[%d] = %d \n",i,*p++);
}
p = a;//让指针重新指向数组的首元素地址
return 0;
}
============================================================================
64位操作系统,一个指针占8个字节,所以八个字节表示一个地址
struct demo{
int a;
char c;
};
int main()
{
struct demo *test;
printf("sizeof int *:%d\n",sizeof(int *));
printf("sizeof char *:%d\n",sizeof(char *));
printf("sizeof double *:%d\n",sizeof(double *));
printf("sizeof struct *:%d\n",sizeof(test));
return 0;
}
============================================================================
- 函数封装数组初始化和遍历
- 作为形参的指针,在调用函数时也会分配自己的一个地址,作为指针,指向传过来的地址(下面传过来的main函数arr的首地址),后续就是对传过来的地址进行操作
ps:下面输出遍历的时候为什么不用把parr重新指向arr的首地址,因为这是两个函数,每个传过来的就是parr就是arr的首地址
#include <stdio.h>
void Init_arr(int *parr,int len)
{
int i= 0;
for(i=0;i<len;i++){
*parr = i;
parr++;
}
}
void Printf_arr(int *parr,int len)
{
int *tmp2;
for(int i = 0;i<len;i++){
printf("arr:%d\n",*parr);
parr++;
}
}
int main()
{
int arr[5];
int len = sizeof(arr)/sizeof(arr[0]);
Init_arr(arr,len);
Printf_arr(arr,len);
return 0;
}
============================================================================
练习:数组翻转
#include <stdio.h>
void Init_arr(int *parr,int len)
{
int i= 0;
for(i=0;i<len;i++){
*parr = i;
parr++;
}
}
void Overturn_arr(int *arr,int len)
{
int i,j;
int tmp;
for(i=0;i<(len/2);i++){
j = len-1-i;
tmp = *(arr+i);
*(arr+i) = *(arr+j);
*(arr+j) = tmp;
}
}
void Printf_arr(int *parr,int len)
{
for(int i = 0;i<len;i++){
printf("arr:%d\n",*parr);
parr++;
}
}
int main()
{
int arr[5];
int len = sizeof(arr)/sizeof(arr[0]);
Init_arr(arr,len);
Overturn_arr(arr,len);
Printf_arr(arr,len);
return 0;
}
============================================================================
二维数组的认知 现在有一个二维数组,我们可以把二维数组理解为父子数组,还有数组名就是地址
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
那么父数组的名字就是a,而字数组的名字就是a[0],a[1],a[2],那么我们可以看出a的地址和a[0]的地址是一样的,但是要注意他们的偏移量是不一样的,a+1偏移的是一整个子数组的大小,a[0]+1是偏移一个数组元素的大小。
a[0] 等价于 &a[0][0] a[1]等价于 = &a[1][0] a[2]等价于 = &a[2][0]
那么a表示父数组的地址,a[0]表示的字数组的首地址
(a)等价与a[0]的首地址(因为a不可能取到整个子数组的值),*(a)+1就是(a[0])+1
#include <stdio.h>
int main()
{
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
printf("a父数组地址 IP:%p %d\n",a,*(a));
printf("a[0]子数组地址 IP:%p %d\n",a[0],*(a[0]));
printf("a+1父数组地址偏移 IP:%p %d\n",(a+1),*(a+1));
printf("a[0]+1字数组地址偏移 IP:%p %d\n",(a[0]+1),*(a[0]+1));
//*(a)等价与a[0]的首地址,*(a)+1就是(a[0])+1
printf("*(a)字数组地址偏移 IP:%p *(a)+1的地址: %p\n",*(a),*(a)+1);
return 0;
}
总结:
============================================================================
数组指针:就是数组的指针,其实就是一个指向数组的指针。 数组指针才是等同于二维数组名
#include <stdio.h>
int main()
{
int arr[2][3] = {{1,2,3},{4,5,6}};
int (*p)[3];//指向有3个元素数组的指针
p = arr;
int i,j;
printf("p=%p\n",p);
printf("++p:%p\n",++p);//这里看出地址偏移是12字节,就是一个子数组的大小
p = arr;//这里让p重新指向二维数组名,再使用p遍历数组
for(i=0;i<2;i++){
for(j=0;j<3;j++){
//printf("arr:%d\n",arr[i][j]);
printf("arr:%d\n",*(*(p+i)+j));
}
}
return 0;
}
============================================================================
数组指针的和二维数组的配合使用
题目:用数组指针方法,输出二维数组任意行列的数
#include <stdio.h>
void InputNum(int *hang,int *lie)
{
printf("输入行列号:\n");
scanf("%d %d",hang,lie);
}
int SearchNum(int (*p)[4],int hang,int lie)
{
return (*(*(p+hang)+lie));
}
int main()
{
int a[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}};
int hang,lie;
int data;
//提示输出
InputNum(&hang,&lie);
//找到对于的数字
data = SearchNum(a,hang,lie);
//打印出来
printf("第%d行第%d列的数字为:%d\n",hang,lie,data);
return 0;
}
============================================================================
函数指针,就是一个指向函数入口地址的指针。
1.函数指针:如果程序中定义了一个函数,在编译的时候,编译系统会为函数代码分配一段存储空间,这段存储空间的起始地址(又成为入口地址)称为这个函数的指针。
2.函数名就是地址
3.如何定义一个函数指针变量:int (*p)(int a,int b)
4.定义函数指针的时候要和指向的函数的类型保持一致
#include <stdio.h>
void printfWeclme()
{
printf("hello world\n");
}
int DataAdd(int data)
{
return ++data;
}
int main()
{
void (*p)(); //函数指针的定义
int (*p2)(int data);
p = printfWeclme;//指向printfWeclme这个函数
p2 = DataAdd;
(*p)();//利用函数指针调用这个函数
printf("++data:%d\n",(*p2)(10));
return 0;
}
练习:用函数指针实现两个整数ab,输入1 2 或者3,1:输出ab中的大者,2:输出ab中的小者,3:输出ab之和
#include <stdio.h>
#include <stdlib.h>
int GetMax(int a,int b)
{
int Max;
if(a>b){
Max = a;
}else{
Max = b;
}
return Max;
}
int GetMin(int a,int b)
{
int Min;
if(a<b){
Min = a;
}else{
Min = b;
}
return Min;
}
float GetAvg(int a,int b)
{
return ((float)a+(float)b)/2;
}
int dataHandler(int a,int b,int (*func1)(int,int))
{
int ret;
ret = (*func1)(a,b);
return ret;
}
float dataHandler2(int a,int b,float (*func2)(int,int))
{
float ret;
ret = (*func2)(a,b);
return ret;
}
int main()
{
int a = 10;
int b = 20;
int mark;
int ret;
float ret2;
int (*func1)(int,int);
float (*func2)(int,int);
printf("Input:");
scanf("%d",&mark);
switch(mark){
case 1:
func1 = GetMax;
break;
case 2:
func1 = GetMin;
break;
case 3:
func2 = GetAvg;
break;
default:
printf("Input error\n");
exit(-1);
}
if(mark != 3){
ret = dataHandler(a,b,func1);
printf("ret:%d\n",ret);
}else{
ret2 = dataHandler2(a,b,func2);
printf("ret2:%0.2f\n",ret2);
}
return 0;
}
============================================================================
数组指针:
理解::指针数组就是存放指针的数组,数组的每一项都是指针变量
int main()
{
int a = 1;
int b = 2;
int c = 3;
int* arr[3] = {&a,&b,&c};
for(int i=0;i<3;i++){
printf("%d ",*(arr[i]));
}
return 0;
}
定义一个函数指针数组,然后进行函数调用:
#include <stdio.h>
#include <stdlib.h>
int GetMax(int a,int b)
{
int Max;
if(a>b){
Max = a;
}else{
Max = b;
}
printf("Max:%d\n",Max);
return Max;
}
int GetMin(int a,int b)
{
int Min;
if(a>b){
Min = b;
}else{
Min = a;
}
printf("Min:%d\n",Min);
return Min;
}
int main()
{
int a = 10;
int b = 20;
int (*pfun[2])(int,int) = {GetMax,GetMin};//定义了一个指向函数入口的函数指针数组,每一项都指向一个函数入口地址
for(int i=0;i<2;i++){
(*pfun[i])(a,b);//进行调用
}
return 0;
}
============================================================================
指针函数:返回指针值的函数
有3个学生,每个学生对于四门成绩,要求用用户输入学生序号后,输出该学生的全部成绩。用函数指针实现
#include <stdio.h>
int *getPosPenson(int pos,int (*pstu)[4])//函数指针
{
int *p;
p = (int *)(pstu+pos);
return p;
}
int main()
{
int arr[3][4] = {
{1,2,3,4},
{5,6,7,8},
{45,52,63,54}
};
int *ppos;
int pos;//学生0 1 2
do{
printf("输入学生012:");
scanf("%d",&pos);
}while(pos!=0 && pos!=1 && pos!=2 );
ppos = getPosPenson(pos,arr);
for(int i=0;i<4;i++){
printf("%d ",*(ppos++));
}
return 0;
}
============================================================================
二级指针:保存指针地址的指针
#include <stdio.h>
int main()
{
int data = 10;
int *p;
p = &data;
int **p2;
printf("data的地址:%p\n",&data);
printf("p存放的地址(data的地址):%p\n",p);
printf("p本身的地址:%p\n",&p);
p2 = &p;
printf("p2保存的地址(p本身的地址):%p\n",p2);
printf("*p2是:%p\n",*p2);//这里取到的应该是data的地址
printf("**p2是:%d\n",**p2);//这里就取到data的值了
return 0;
}
============================================================================
下面对于ppos的理解
#include <stdio.h>
void getPosPenson(int pos,int (*pstu)[4],int **ppos)//函数指针
{
*ppos = (int *)(pstu+pos);//*ppos取到的就是main函数一级指针ppos的地址,这个指针指向了pstu这个指针的偏移地址,所以main函数中的ppos就再*(取一次内容)就访问到里面的值了
}
int main()
{
int arr[3][4] = {
{1,2,3,4},
{5,6,7,8},
{45,52,63,54}
};
int *ppos;
int pos;//学生0 1 2
do{
printf("输入学生012:");
scanf("%d",&pos);
}while(pos!=0 && pos!=1 && pos!=2 );
getPosPenson(pos,arr,&ppos);//这里传过去的时ppos的地址(指针的地址需要二级指针来承接)
for(int i=0;i<4;i++){
printf("%d ",*(ppos++));
}
return 0;
}
============================================================================
二级指针不能简单粗暴指向二维数组
============================================================================
搞懂下面这张图
================================================================================================================================================================