C语言之指针
最近打算学数据结构,所以又重新复习了一遍c语言,我用的谭浩强的第四版,里面的内容也都是来自这本书。
1.指针的定义
一个变量的地址被称为该变量的指针。如果有一个变量专门用来存放另一个变量的地址(即指针),则称它为指针变量。
定义指针变量:
int a,b;
int *pointer_1=&a,*pointer_2=&b;
//等价
int *pointr_3;
pointer_3=&a;
int *p中,int表示基类型,表示该指针变量p所指向的存贮单元的数据类型是int。星号表示该变量是指针变量,而指针变量的名字为p。
2.使用指针变量
int a,b;
int *p1=&a,*p2=&b;
scanf("%d,%d",p1,p2);
printf("%d,%d",*p1,*p2);
scanf后面的参数需要参数的地址,printf后面的参数需要参数的值。另外,scanf尽量不要用带有“,”的格式,容易输入错误(血的教训!)。
3.指针变量作为函数参数
例8.3(书226)
需要对两个整数按大小输出,用函数处理,指针类型的数据作为函数参数。
#include<stdio.h>
int main()
{
void swap(int *p1,int *p2);
int a,b;
int *pointer_1,*pointer_2;
scanf("%d %d",&a,&b);
pointer_1=&a;
pointer_2=&b;
if(a<b)
swap(pointer_1,pointer_2);
printf("max=%d,min=%d",a,b);
}
void swap(int *p1,int *p2 )
{
int temp;
temp=*p1;
*p1=*p2;
*p2=temp;
}
以上是正确方法,下面一种是错误方法,只改变swap(),且在main函数中调用swap()函数,参数是int,swap(a,b)。
void swap(int x,int y)
{
int temp;
temp=x;
x=y;
y=temp;
错误原因:
理解形参和实参的概念,在调用函数的时候,采用单向传递的方式。即在调用函数的期间,会创造一个在存储单元中真实存在的变量(形参),然后将实参的值复制给形参。但二者是相互独立的,互不干扰(在不使用指针的前提下),在函数调用完毕后形参就会被系统消除。打个比方,有一只羊a,你可以根据羊a克隆出所有特性一模一样的羊b,但是二者都是真实存在的,而且无论你对羊b做了什么,都不会影响羊a。
因此,为了使在函数中改变了的量能够被主函数main()所接收,就不能把要改变的变量作为调用函数的参数,而应该把要改变的量的指针变量作为参数。还是打个比方:有一间屋子a,如果你按照屋子a一模一样造了一间屋子b,那么无论你对屋子b做什么都不会影响到屋子a。但是,如果你把屋子a的地址给了一群坏人,那么这群坏人就能通过地址找到屋子a,从而真正对屋子a进行破坏。
所以,以指针变量作为函数参数的本质就是通过把实参的地址传给所要调用的函数,从而通过调用函数真正找到那个实参,从而对实参进行操作。
下面是一个错误例子,题目还是上题,即把两个整数按照大小输出(书228)
#include<stdio.h>
int main()
{
void swap(int *p1,int *p2);
int a,b;
int *pointer_1=&a,*pointer_2=&b;
scanf("%d %d",&a,&b);
if(a<b)
swap(pointer_1,pointer_2);
printf("max=%d,min=%d",*pointer_1,*pointer_2);
}
void swap(int *p1,int *p2)
{
int *p;
p=p1;
p1=p2;
p2=p;
}
错误原因:
不能通过改变指针形参的值而使指针实参的值改变,本质上还是前面所说的错误。
总结(片面):
如果你想在调用的函数中改变main()中变量a的值,你需要在main()中定义一个指向变量a的指针变量p,然后将p作为参数传入到调用的函数中,通过操作p来改变a。如果你把a作为调用函数的参数,想通过操作形参a来改变实参a,错误。如果你把p作为调用函数的参数,通过操作p来改变p,还是错误。
4. 指针引用数组
int a[10]={1,2,3,4}
int *p=a;
//等价
int *p=&a[0];
数组名就表示数组首元素的地址,因此上面把a数组的首元素a【0】的地址赋给了指针变量p。
- 如果p已指向数组中的一个元素,那么p+1指向下一个元素,而不是连续的下一个地址。
- 如果p的初值就是&a[0],那么p+i和a+i就是元素a[i]的地址,星(a+i)和星(p+i)就是a[i]。
一个例子:
//数组与指针
#include<stdio.h>
int main()
{
void swap(int *pointer);
int a[]={1,2,3,4,5};
int *p=a;
swap(p);
printf("%d",p[1]);
}
/* 方法2
void swap(int *pointer)
{
int temp;
temp=*(pointer+1);
*(pointer+1)=*(pointer+2);
*(pointer+2)=temp;
}
*/
/*方法2
void swap(int *pointer)
{
int temp;
temp=pointer[1];
pointer[1]=pointer[2];
pointer[2]=pointer[1];
}
*/
例8.6(p233)
有一个整型数组a,有十个元素,要求输出全部元素。
(1)下标法
#include<stdio.h>
int main()
{
int a[10];
int i;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
for(i=0;i<10;i++)
printf("%d",a[i]);
}
(2)数组名
#include<stdio.h>
int main()
{
int a[10];
int i;
for(i=0;i<10;i++)
scanf("%d",a+i);
for(i=0;i<10;i++)
printf("%d",*(a+i));
}
(3)指针法
在这#include<stdio.h>
int main()
{
int a[10];
int *p,i;
for(i=0;i<10;i++)
scanf("%d",a+i);
for(p=a;p<(a+10);p++)
printf("%d",*p);
}
各种用法总结:
#include<stdio.h>
int main()
{
int a[]={1,10,100,1000,10000};
int *p=a;
printf("%d\n",*p);
printf("%d\n",*p++);//先执行*p的运算,再使p+1,输出为1
printf("%d\n",*p);//由于上一条语句的影响,输出为10
printf("%d\n",*(p++));//等价于p++,输出为10
printf("%d\n",*p);//由于上一条语句的影响,输出为100
printf("%d\n",*(++p)); //先使p+1,在执行*p,输出为1000
printf("%d\n",++(*p));//使p所指向的元素的值加1,输出为1001
}
5.数组名,指针作为函数参数
(1)数组名作为函数参数
int main()
{
void fun(int arr[],int n);//对fun函数的声明
int array[10];//定义array数组
...
fun(array,10);//用数组名作为函数的参数,调用函数
}
void fun(int arr[],int n)
{
...
}
(2)指针变量作为函数参数
int main()
{
void fun(int *arr,int n);
int array[10];
...
fun(array,10);
}
void fun(int *arr,int n)
{
...
}
实参,形参,数组名,指针变量名共四种组合用法见书p239——p243
例8.10(P244)
用选择法,对10个数按由大到小进行排序
(1)
#include<stdio.h>
int main()
{
void sort(int x[],int n);
int i,*p,a[10];
p=a;
for(i=0;i<10;i++,p++)
scanf("%d",p);
p=a; //使p重新指向a[0]
sort(p,10);
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])
k=j;
if(k!=i)
{
t=x[i];
x[i]=x[k];
x[k]=t;
}
}
}
(2)
#include<stdio.h>
int main()
{
void sort(int x[],int n);
int i,*p,a[10];
p=a;
for(i=0;i<10;i++)
scanf("%d",p++);
p=a;
sort(p,10);
for(p=a,i=0;i<10;i++)
{
printf("%d",*p);
p++;
}
}
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])
k=j;
if(k!=i)
{
t=x[i];
x[i]=x[k];
x[k]=t;
}
}
}
6.指针与二维数组
如书:
个人总结!!!
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
如上,定义了一个二维数组a,三行四列。可以把它看做特殊的一维数组,共包含了三个(行)元素:a[0],a[1],a[2],但这三个行元素本身就是一维数组,各自包含四个元素,如:a[0]包含元素a[0][0],a[0][1],a[0][2],a[0][3]。
- a[i]与*(a+i)等价,表示的是第i行(从0开始)的第0列的元素的地址,如:a[1]的值是&a[1][0]
- a+i表示的是第i行的首地址,如:a+1的值是&a[1]。
- 因此,a[i]+j和*(a+i)+j表示的是第i行第j个元素的地址。所以*(a[i]+j)和*(*(a+i)+j)表示的是a[i][j]。
指向一维数组的指针变量
例8.13(书p251)
#include<stdio.h>
int main()
{
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
int (*p)[4],i,j;
p=a;
scanf("%d %d",&i,&j);
printf("%d\n",*(*(p+i)+j));
}
如上,int (*p)[4]定义一个指针变量p,它指向包含4个整形元素的一维数组。p是指向一个包含4个元素的一维数组,而不是指向一维数组中的某一元素。p的值就是该一维数组的起始地址。
#include<stdio.h>
int main()
{
int a[4]={1,2,3,4};
int(*p)[4];
p=&a;
printf("%d",(*p)[3]);
如上,p=&a表示p指向的是一维数组(行),正确;
而如果写成p=a,则p的值就是&a[0],指向的是第一个元素a[0]。
7.指向数组的指针作为函数参数
两种方法:
1.指向变量的指针变量
2.指向一维数组的指针变量
例8.14
3个学生,各有4门成绩,计算总平均分数以及第n个学生的分数。
#include<stdio.h>
int main()
{
void average(float *p,int n); //标注1
void search(float(*p)[4],int n); //标注2
float score[3][4]={{65,67,78,67},{56,45,45,78},{3,5,66,78}};
average(*score,12); //求12个分数的平均分 标注1
search(score,2); //求序号为2的学生的成绩 标注2
}
void average(float *p,int n)
{
float *p_end;
float sum=0,aver;
p_end=p+n-1;
for(;p<=p_end;p++)
sum=sum+(*p);
aver=sum/n;
printf("%5.2f\n",aver);
}
void search(float(*p)[4],int n)
{
int i;
for(i=0;i<4;i++)
printf("%6.1f",*(*(p+n)+i));
}
标注1:
(score)表示的是第0个元素score[0][0]的地址,因此(score)传给定义的函数void average(float *p,int n)的形参p。*p就表示元素a[0][0]。
标注2
score表示的是第0行的地址,因此score传给定义的函数void search(float(*p)[4],int n)的形参p。
例8.15
在上题的基础上,查找有课程不及格的学生,并输出他们的全部成绩。
#include<stdio.h>
int main()
{
void search(float(*p)[4],int n);
float score[3][4]={{56,70,67,89},{45,34,89,100},{100,100,99,100}};
search(score,3);
}
void search(float (*p)[4],int n)
{
int i,j,flag;
for(j=0;j<n;j++)
{
flag=0;
for(i=0;i<4;i++)
if(*(*(p+j)+i)<60)
flag=1;
if(flag==1)
{
printf("NO.%d,his scores are:\n",j+1);
for(i=0;i<4;i++)
printf("%5.0f",*(*(p+j)+i));
printf("\n");
}
}
}
8.通过指针引用字符串
(1)用字符数组存放一个字符串,可以通过数组名和下标引用字符串中的一个字符,也可以通过数组名和“%s”输出该字符串。
char string[]="I love 吕宇凌";
printf("%s\n",string);
printf("%c",string[7]);
(2)用字符指针变量引用字符串
char *string="i love 吕宇凌";
printf("%s",string);
经典错误:
char *a;
scanf("%S",a);
因为没有给指针变量a赋值,a中所存放的地址是不确定的,即a所指向的对象是不确定的,非常危险。
改正
char *a;
char str[10];
a=str;
scanf("%s",a);
指针变量的值是可以改变的,而数组名是固定的不能改变。
正确:
char *a="i love 吕宇凌";
a=a+7;
printf("%s",a); //输出改为从a+7指向的字符串开始
错误:
char str[]="i love 吕宇凌";
str=str+7;
printf("%S",str);
正确:
char *a="i love 吕宇凌";
printf("%c",a[5]);
9.指向函数的指针
函数的起始地址被称为这个函数的指针。可以定义一个指针变量,用来存放函数的起始地址,即该指针指向该函数。
格式:
int (*p)(int int)
p指向函数的返回值类型为整型且有两个整型参数的函数。p的类型用int(*)(int,int)表示。
例8.22(p267)
求a和b大者
#include<stdio.h>
int main()
{
int max(int,int);
int(*p)(int,int);
int a,b,c;
p=max; //使p指向max函数
printf("please enter a and b:\n");
scanf("%d %d",&a,&b);
c=(*p)(a,b);
printf("max=%d",c);
}
int max(int x,int y)
{
int z;
if(x>y)
z=x;
else
z=y;
}
调用函数时,只需用*p代替函数名即可。如:
p=max c=(*p)(a,b)
指向函数的指针变量的一个重要用途就是把函数的地址作为参数传递到其他函数
例8.24
有两个整数a,b,由用户输入1,2,3,输入1,给出最大值;输入2,给出最小值;输入3,求和
#include<stdio.h>
int main()
{
void fun(int x,int y,int (*p)(int,int));
int max(int,int);
int min(int,int);
int add(int,int);
int a=1,b=2,n;
printf("please choose 1,2,3:\n");
scanf("%d",&n);
if(n==1) fun(a,b,max);
else if(n==2) fun(a,b,min);
else if(n==3) fun(a,b,add);
}
int fun(int x,int y,int(*p)(int,int))
{
int result;
result=(*p)(x,y);
printf("%d",result);
}
int max(int x,int y)
{
int z;
z=x>y?x:y;
printf("max=");
return(z);
}
int min(int x,int y)
{
int z;
z=x<y?x:y;
printf("min=");
return(z);
}
int add(int x,int y)
{
int z;
z=x+y;
printf("sum=");
return(z);
}
9.返回指针值的函数
定义:
一个函数的返回值类型是指针类型。
格式
类型名 函数名(参数列表)
如:inta(int x,int y)
int *表示该函数的返回值是指针类型。
偷懒,手累了,故copy
10.指针数组
copy
例8.27
#include<stdio.h>
#include<string.h>
int main()
{
void sort(char *name[],int n);
void print(char *name[],int n);
char *name[]={"lyl","a love lyl","b really lyl","c really love lyl","d with lyl"};
int n=5;
sort(name,n);
print(name,n);
}
void sort(char *name[],int n)
{
char *temp;
int i,j,k;
for(i=0;i<n-1;i++)
{
k=i;
for(j=i+1;j<n;j++)
if(strcmp(name[k],name[j])>0) k=j;
if(k!=i)
{
temp=name[i];
name[i]=name[k];
name[k]=temp;
}
}
}
void print(char *name[],int n)
{
int i;
for(i=0;i<n;i++)
printf("%s\n",name[i]);
}
多重指针
跑步去了,故copy
总结:
指针是c语言的灵魂,有了指针才使c语言变得简洁,高效和优雅,但同时也令它变得比其他语言危险和难以捉摸
上一篇: 裂缝检测标记
下一篇: 为什么特征相关性非常的重要?