C语言指针_详解

目录

一、指针的概念

二、指针变量

1、指针变量的定义

2、指针变量的赋值

3、指针的运算

4、多级指针

三、指针与数组

1、指针与一维数组

2、用数组名作为函数参数

3、指针与二维数组

四、指针与字符串

1、字符串的表示形式

2、字符指针作为函数参数

五、指向函数的指针

1、指向函数的指针变量

2、指向函数的指针变量作为函数参数(▲)

六、返回指针的函数

1、返回指针型函数的定义形式

2、返回指针的函数的应用

七、指针数组

1、指针数组的概念

2、指针数组作为main函数的参数


一、指针的概念

一个变量的地址称为该变量的“指针”;

二、指针变量

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函数的形参。

  • 6
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Random_ _

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值