目录
一、指针的概念
一个变量的地址称为该变量的“指针”;
二、指针变量
1、指针变量的定义
专门存放变量地址的变量就是指针变量;
2、指针变量的赋值
①将a的地址赋给指针p:
int a,*p;
p=&a
②指针变量的初始化:
int a,*p=&a;
③通过其他指针变量赋值:
int a,*p1,*p2;
p1=&a;
p2=p1;
④用NULL给指针变量赋空值:
int *p=null;
3、指针的运算
①指向运算符
指向运算符:作用在指针上,代表该指针所指向的存储单元,实现间接访问,因此又叫做“间接访问运算符”。
例如:
int a=3,*p;
p = &a;
printf("%d",*p)
//输出结果为3
指向运算符*为单目运算符,根据运算符的作用,指向运算符*和地址运算符&互逆;
*(&a)==a,&(*p)==p
注意:定义指针变量时,“*”作用是定义指针变量,在执行部分的表达式中,“*”作用是充当指向运算符,作用在指针上,代表指针所指向的存储单元,实现间接访问。
在执行过程中,对*p进行修改等于直接对p指向的变量进行修改:
#include <stdio.h>
int main()
{
int a=123,*p;
p = &a;
printf("%d,%d\n",a,*p);//结果123,123
*p = 456;
printf("%d,%d\n",a,*p); //结果:456,456
return 0;
}
②指针的算数运算
一个指针可以加减一个整数n,但其结果不是指针值直接加减n,而是与其所指向的变量的数据类型有关,指针变量的值应增加或减少n*sizeof(指针类型)
例如:int a=3,*p=&a;设a的起始地址为1000,执行p=p+后,
p的值变化三个整型(每个整形占2byte)的位置,p的值应该是1000+3*sizeof(int)=1000+6=1006;而不是10003;
p=p+n,p=p-n,p++,p--,++p,--p 的理解类似...
③指针的关系运算
与基本类型变量一样,指针可以关系运算,在关系表达式中允许对两个指针进行所有的关系运算。在指针进行关系运算前,指针必须指向确定的变量,即指针必须有初始值。另外只有相同类型的指针才能进行比较。
4、多级指针
①多级指针变量均用基类型定义,定义m级指针变量,变量名前l放m个*
②各指针变量都得取低一级的指针变量的地址后才能应用
③引用m级指针变量访问最终的普通变量时,变量名前需要使用m个指针运算符*
eg:
#include "stdio.h"
int main()
{
int a = 5;
int *p1,**p2,***p3;
p1 = &a; p2 = &p1; p3 = &p2;
printf("%d\n",***p3);
return 0;
}
三、指针与数组
1、指针与一维数组
C语言规定,数组的首地址即数组名是第一个地址常量,是不能改变的,a++是非法的;
若首地址是a,且指针变量p指向该数组的首地址(p=a;)则:
数组的第0个元素a[0]的地址是a(等价于p)
数组的第1个元素a[1]的地址是a+1(等价于p+1)
数组的第2个元素a[2]的地址是a+2(等价于p+2)
数组的第i个元素a[i]的地址是a+i(等价于p+i)但是注意a是常量,p是变量;
eg:利用指针法实现数组中元素的输入和输出:
#include "stdio.h"
int main()
{
int a[10],*p,i;
p = a;
printf("请输入10个整数:");
for(i=0;i<10;i++)
scanf("%d",p+i);
printf("输出10个整数:");
for(i=0;i<10;i++)
printf("%3d",*(p+i));
return 0;
}
感想:
①实际上scanf输入值的时候,就是在将值赋给对应的地址!所以才有用地址运算符&;
②a[i]等于*(a+i) ; &a[i]等于a+1 ;
2、用数组名作为函数参数
当函数之间需要传递数组时,可通过传递数组的首地址,完成存取主调函数中的数组元素的操作。
如果实际参数是某个数组元素,那么因为数组元素是一个变量,因此传递方法和普通变量是一样的,采用传值的方式进行,函数中的形式参数的编号不会影响对应实参的数组元素。
如果实际参数是数组名,由于数组名代表数组首地址,当他作为实参进行函数调用时,是把数组首地址传给形参,实参和形参共用一段内存区域。当在函数中对形参进行操作时,实际上是在实参数组中进行的,在函数调用后,实参的元素值可能发生变化。
eg:将数组a中n个整数反序存放(算法1):
#include <stdio.h>
void reverse(int x[],int n){
int temp,i;
for(i=0;i<=(n-1)/2;i++)
{temp=x[i];x[i]=x[n-1-i];x[n-1-i]=temp;}
}
int main(){
int a[9];
printf("请输入10个数存入数组:");
for(int i=0;i<10;i++)
scanf("%d",a+i);
reverse(a,10);
for(int i=0;i<10;i++)
printf("%d",a[i]);
return 0;
}
eg:将数组a中n个整数反序存放(算法2:定义两个指针实现交换):
#include <stdio.h>
void reverse(int x[],int n){
int *p,*q,temp;
p = x;
q = x+n-1;
while(p<q){
temp = *p; //注意要用指向运算符取值来交换
*p = *q;
*q = temp;
p++,q--;
}
}
int main(){
int a[9];
printf("请输入10个数存入数组:");
for(int i=0;i<10;i++)
scanf("%d",a+i);
reverse(a,10);
for(int i=0;i<10;i++)
printf("%d",a[i]);
return 0;
}
3、指针与二维数组
(1)二维数组的地址:
用指针可以指向一维数组,也可指向二维数组是具有行列结构的数据,二维数组元素地址于一维数组元素地址表示不同,二维数组的首地址称为二维数组的指针,存放这个指针变量称为指向二维数组的指针变量。
a[i]从形式上看是a数组中序号为i的元素,如果a是一维数组名,则a[i]代表a数组第i号元素所占内存单元的内容,a[i]是有物理地址的(a+i),是占内存单元的。
如果a是二维数组,则a[i]是一个地址,并不代表某个元素的值。
a是指向行的,a[i]是指向列元素的;
数组名a常称为行指针,a[i]常称为列指针(行中的某列),
=========================================================================
看以下举例容易懂:
若有如下定义:
int a[3][4],i,j;
当0<=i<3、0<=j<4时,a数组元素可以用以下五种表达式表示:
①a[i][j]
②*(a[i]+j) //a[i]=*(a+i),所以内部先指向行;然后外部整体*(*(a+i)+j)后指向列,
------>!!!即先指向第(a+i)行,再指向该行的第(a[i]+j)列;!!!<------
③*(*(a+i)+j) //同上
④(*(a+i))[j] //每次指向a+1行后,再通过[j]指向列
⑤*(&a[0][0]+4*i+j) //4*i代表 前行第一列和后行第一列隔4个存储单位
=========================================================================
eg:用地址表示法输出二维数组各元素大的值:
#include <stdio.h>
int main(){
int a[2][3]={{1,2,3},{4,5,6}} ;
int i,j,p,*x;
printf("a数组为:\n");
for(i=0;i<2;i++){
for(j=0;j<3;j++)
printf("%3d",*(a[i]+j));
printf("\n");
}
}
(1)指向二维数组的指针变量
①指向数组元素的指针变量:
eg:用指针变量输出二维数组的值:
#include <stdio.h>
int main(){
int a[2][3]={{1,3,5},{7,9,11}};
int *p;
for(p=a[0];p<a[0]+6;p++)
printf("%3d",*p);
return 0;
}
②指向一维数组的指针变量或行指针:
=========================================================================
行指针的说明形式如下:
类型符 (*指针变量名)[每行的元素个数]
例如:int (*p)[4] , a[3][4]; //注意不要掉*p的括号别掉了
含义:定义了一个指针p,指向了一个具有4个元素的一维数组(二维数组的行数组),即用p来定义二维数组中的行地址。引用了行指针p后,p++表示指向下一行地址,p的值应该以一行占用存储字节数为单位进行调整。
=========================================================================
eg:用指向一维数组的行指针,输出二维数组,输出一行后换行:
#include <stdio.h>
int main(){
int i,j,x;
int a[3][4]={{1,3,4,9},{23,17,36,34},{73,88,33,12}};
int (*p)[4];
p=a;
for(i=0;i<3;i++){
for(j=0;j<4;j++)
printf("%3d",*(*p+j));
p++;
printf("\n");
}
}
eg:用指向一维数组的行指针,输出二维数组,并求数组中的最大元素及其行列号:
#include <stdio.h>
int main(){
int i,j,s,t,max;
int a[3][4]={{1,3,4,9},{23,17,36,34},{73,88,33,12}};
int (*p)[4];
p = a;
max = **p;s=0;t=0;
for(i=0;i<3;i++){
for(j=0;j<4;j++)
if(*(*p+j)>max)
{max=*(*p+j);s=i;t=j;}
p++;
}
printf("最大值=%d,行号=%d,列号=%d\n",max,s,t);
return 0;
}
四、指针与字符串
1、字符串的表示形式
字符串是特殊常量,一般被存储在一维的字符数组中并以‘\0’结束。字符串与指针也有着密切的关系。再C语言程序中,可用两种方法来访问一个字符串:一种方式使用字符数组,另一种是使用字符指针。在字符串处理中,使用字符指针往往比使用字符数组更为方便。
补充:字符数组的定义,字符指针的定义:
字符数组的定义:char str[]="Welcome to Wuhan!";
字符指针的定义:char *p;
=========================================================================
eg:输出该字符串。
①字符数组实现:定义一个字符数组,对其初始化后,输出该字符串。
#include <stdio.h>
int main(){
char str[]="Welcome to Wuhan!";
int i;
for (i=0;str[i]!='\0';i++)
printf("%c",str[i]);//使用字符数组输出
printf("\n");
printf("%s\n",str); //使用str这个首地址输出
}
②字符指针实现:定义一个字符数组,对其初始化后,输出该字符串。
#include <stdio.h>
int main(){
char str[]="Welcome to Wuhan!",*p;
p = str;
printf("%s\n",p); //先用 字符指针p 复制 字符串的首地址str,然后通过指针输出即可
=========================================================================
eg:利用字符指针变量的方法,完成字符串的复制。
#include <stdio.h>
int main(){
char str1[]="Welcome to Wuhan!",str2[80];
char *p1,*p2;
p1=str1;
p2=str2;
for(;*p1 != '\0';p1++,p2++)
*p2=*p1;
*p2 = '\0';
printf("%s\n",str1);
printf("%s\n",str1);
}
=========================================================================
eg:输出两个字符串,比较是否相等,相等输出Yes!,不相等输出No!
#include <stdio.h>
int main(){
char str1[100],str2[100],*p1,*p2;
printf("请输入第一个字符串:");
gets(str1);
printf("请输入第二个字符串:");
gets(str2);
int flag=1;
p1=str1,p2=str2;
while(*p1!='\0' || *p2!='\0'){
if(*p1!=*p2)
{flag=0;break;}
p1++,p2++;
}
if(flag==1) printf("Yes!");
else printf("No!");
}
2、字符指针作为函数参数
将一个字符串从一个函数传递给另一个函数,可使用字符数组名做参数,也可使用指向字符串的指针变量做参数,在被调函数中改变字符串的内容,在主调函数中可得到改变了的字符串。
eg:将输入的字符串中的小写字母改为大写字母后,输出字符串。
#include <stdio.h>
void fun(char *p){
while(*p){
if('a'<=*p&&*p<='z') *p-=32;
p++;
}
}
int main(){
char str1[80];
printf("请输入需要改为大写的字母组:");
gets(str1);
fun(str1);
printf("修改为大写后:%s",str1);
}
eg:用字符指针变量将两个字符串首尾连接起来。
#include <stdio.h>
void fun(char str1[],char str2[]){
char *p1,*p2;
p1=str1;
p2=str2;
for(;*p1!='\0';p1++); //将p1指向str1的尾端
do{
*p1=*p2; //将str2的首端 连入str1的尾端
p1++; //p1指针后移
p2++; //p2指针后移
}while(*p2!='\0'); //当检测到p2指到了str2的尾端的‘\0’时停止所有指针的移动
*p1 = '\0'; //给str1的尾端加上‘\0’
}
int main(){
char str1[80],str2[20];
printf("请输入第一个需要合并的字符串:");
gets(str1);
printf("请输入第二个需要合并的字符串:");
gets(str2);
fun(str1,str2);
printf("合并后的字符串为:%s\n",str1);
return 0;
}
五、指向函数的指针
1、指向函数的指针变量
定义:指向函数的指针变量的一般形式:
类型标识符 (*指针变量名)(标识符 ,标识符 );//其中“标识符”数量与指向的函数有关
eg:int (*p)(int x,int y );
方法:为函数指针赋值的格式:
p = 函数名;
方法:通过函数指针调用函数,调用格式如下:
s = (*p)(实参)
eg:指向函数的指针变量示例。
#include <stdio.h>
int max(int a,int b){
if(a>b) return a;
else return b;
}
int main(){
int (*p)(int,int);
int x,y,z;
scanf("%d%d",&x,&y);
p = max;
z=(*p)(x,y);
printf("max=%d\n",z);
}
2、指向函数的指针变量作为函数参数(▲)
变量、数组名、指向数组的指针变量都可作为函数的参数,同样,指向函数的指针变量也可作为函数参数。当函数指针每次指向不同的函数时,可完成不同的功能。
函数名表示该函数在内存区域的入口地址,因此,函数名可作为实参出现在函数调用的参数表中。
eg:编写一个函数,每次在调用它时实现不同的功能。输入两个整数,利用前面编写的函数求出他们的和、差、积。
#include <stdio.h>
int add(int a,int b){
return a+b;}
int minus(int a,int b){
return a-b;}
int multiply(int a,int b){
return a*b;}
void process(int x,int y,int (*p)(int,int)){ //定义一个函数指针变量作为参数,可指向不同函数,实现不同功能
int result;
result = (*p)(x,y ); //通过函数指针,调用其指向的函数;
printf("%d\n",result);}
int main(){
int a,b;
printf("请键入a,b的值:");
scanf("%d%d",&a,&b);
process(1,2,add);
process(1,2,minus);
process(1,2,multiply);
}
六、返回指针的函数
1、返回指针型函数的定义形式
在C语言中,允许一个函数的返回值是一个指针。有时把返回指针值的函数称为称为指针型函数。
返回指针型函数的一般定义形式为:
类型说明符 *函数名(形参表)
{
函数体
}
// 其中,函数名前加了*表示这个是一个指针型函数,返回值是一个指针。类型说明符表示了返回的指针值所指向的数据类型。
eg:int *fun(int x,int y);
其中fun是函数名,指向函数后返回的是一个指向整型数据的指针,由于*的优先级低于( ),所以fun首先于( )结合称为函数形式,然后于*结合,说明此函数是指针型函数,函数的返回值是一个指针(即是一个地址)。说行说明符int表示返回的指针值所指向的数据类型为整型。
2、返回指针的函数的应用
对于返回指针的函数,在通过函数调用后必须把它的返回值赋给指针类型的变量。
eg:通过指针型函数,输入一个1~7之间的整数,输出对应的星期名。
#include <stdio.h>
char *day_name(int i){
static char *name[]={"Illegal day","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"};
if(i<1||i>7)
return(name[0]);
else return(name[i]);
}
int main(){
int n;
char *p;
printf("Please input a number of day:");
scanf("%d",&n);
p = day_name(n);
printf("It is %s\n",p);
return 0;
}
七、指针数组
1、指针数组的概念
一个数组的若干元素均为指针型数据类型,称为指针数组。即每个元素都是指针类型的数组。
指针数组的定义形式为:
类型名 *数组名[数组长度];
例如 int*p[10];
p是数组名,这个数组包括6个元素,即p[0]~p[9],每个元素都是指向整型数据的指针。p可以用于保存6个整型数据的地址。
注意区别 int *p[10]和int (*p)[10],两者意义不同,前者由于[]的优先级高于*,因此p先与[10]结合,表明p是数组,数组中有10个元素,再与*结合,表明该数组是指针类型的。后者p先与*结合,再与[10]结合,表明p是指向一维数组的指针变量。
引用指针数组可用来处理一组字符,比较适合于指向若干长度不等的字符串,使字符串处理更加方便灵活,而且节省空间。
eg:设计一个程序,将若干字符串按字母顺序从大到小输出,要求用字符指针数组实现。
#include <stdio.h>
#include <string.h>
void sort(char *str[],int n){
int i,j,k;
char *s;
for(i=0;i<n-1;i++){
k=i;
for(j=i+1;j<n;j++)
if(strcmp(str[j],str[k])>0) k=j;
if(k!=i)
{s=str[k];str[k]=str[i];str[i]=s;}
}
}
void print(char *str[],int n){
int i;
for(i=0;i<n;i++)
printf("%s\n",str[i]);
}
int main(){
char *str[6]={"C language","data structure","java","database","network","operating system"};
int n=6;
sort(str,n);
print(str,n);
return 0;
}
说明:本例中使用指针数组中的元素指向各个字符串。对多个字符串进行排序,不改动字符串的存储位置,而是改动字符指针数组中各元素的指向。这样,各字符串的长度可以不同,而且交换两个指针变量的值要比交换两个字符串所用的时间少得多。
2、指针数组作为main函数的参数
实际上,main函数可以带参数,这个参数可认为是main函数的形式参数。C语言规定main函数可以有两个参数,而且只能有两个参数,习惯上这两个参数写为argc和argv。带参数的main函数定义如下:
int 或 void main(int argc,char *argv[])
{
......
}
第1个参数argc是一个整型数据,第2个参数argv是一个字符指针数组,每个指针都指向一个字符串。
当一个C的源程序经过编译、链接后,会生成拓展名为.exe的可执行文件,这是可以在操作系统下直接运行的文件。main函数不能由其他函数调用和传递参数,只能由系统在启动运行时传递参数。
在操作系统环境下,一条完整的运行命令包括两部分:
命令及相应的参数,其格式为:
可执行文件名 参数1 参数2 ...参数n
当命令执行程序时,系统会把参数1、参数2、......参数n依次传递给该文件名中main函数的形参。