C语言指针知识点


地址与指针


每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。
请看下面的实例,它将输出定义的变量地址:

#include <stdio.h>
 
int main ()
{
   int  var1;
   char var2[10];
 
   printf("var1 变量的地址: %p\n", &var1  );
   printf("var2 变量的地址: %p\n", &var2  );
 
   return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

var1 变量的地址: 0x7fff5cc109d4
var2 变量的地址: 0x7fff5cc109de

什么是指针?
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:

type *var-name;

在这里,type 是指针的基类型,它必须是一个有效的 C 数据类型,var-name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:

int    *ip;    /* 一个整型的指针 */
double *dp;    /* 一个 double 型的指针 */
float  *fp;    /* 一个浮点型的指针 */
char   *ch;     /* 一个字符型的指针 */

所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。

指针变量的定义与使用

1.指针变量的定义
指针变量的定义格式为:
类型说明符 *指针变量名 [=初值];
要同时定义两个指针变量:
int *p1=NULL,*p2=NULL;
(NULL是c语言中预定义的空指针关键字,它作为一个特殊的指针,表示不指向任何对象)
定义指针变量时要注意以下几点:
(1)类型说明符表示该指针变量所指向的变量的数据类型。
(2)定义指针变量时,指针变量名前必须有一个“ * ”号,表示定义的变量是指针变量。
(3)指针变量在定义时允许对其初始化。
例如:
int a=8;
int p=&a;
在这里插入图片描述
2.指针变量的使用
两个重要运算符
(1)&:取地址运算符,如p=&a; 则p为变量a的地址。
(2)
:指针运算符,后面只能接指针变量。用于访问指针变量所指向的变量。如:*p表示访问指针变量p所指向的变量的内容。

int a=8,b;
int *p;
p=&a;

对变量a有两种访问方式:
(1)直接访问。b=a;
(2)通过指针变量间接访问。b=*p;
在这里插入图片描述
变量a与指针变量p的引用

&a表示变量a的地址,&a=2000
p指针变量p的值,p=2000=&a
*p表示指针变量p指向的变量,*p=a=8
&*p相当于&(*p),&(*p)=&a=2000
*&a相当于*(&a),*(&a)=*p=a=8
&p表示指针变量p的地址,&p=2000
*&p相当于*(&p),*(&p)=2000

注意:指针是一个地址,而指针变量是存放地址的变量。
3.指针的运算
(1)指针和整数的加减运算:
可以通过指针与整数的加减运算来移动指针p,实现对不同数据单元的访问操作。对不同的数据类型,移动的单位长度不同。单位长度一般是指针所指向的变量的数据类型长度。
格式一:p=p+n;(或p=p-n;)
格式二:p++;(或p–;)
格式三:++p;(或–p;)
注意:
指针变量的值加1(或减1),并不是给地址加1(或减1),而是加上(或减去)1个数据类型长度,
也就是指针所指向的变量在内存中所占的字节数。
(2)指针和指针的赋值运算:

int a=10,*p,*q;
p=&a;
q=p;
//p和q的值都是变量a的地址。

4.指向指针的指针
关系:
(1)*二级指针变量:代表所指向的以及指针变量。如:*q就代表p;
(2)**二级指针变量:代表它所指的一级指针变量所指向的变量。如:**q代表a;
(3)*一级指针变量:代表它所指向的变量。如:*p代表a。


指针与数组


一。指向一维数组元素的指针

c语言数组中的数组元素可以看作相应类型的变量。只要类型匹配,就可以定义一个指针变量,让这个变量存储数组中数组元素的地址,则该指针便指向该数组元素。

int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };    //定义a为包含十个数据的数组
int *p; //p为指向整型变量的指针变量
p = &a[0]; //把a[0]元素的地址赋给指针变量p
//p=a;       与p = &a[0]等效,p的值是数组a首元素的地址

通过指针引用一维数组元素:
1.指向一维数组首元素的指针
假设指针p指向一维数组a的第一个元素a[0]。则:
p+1: 使p指向下一个元素a[1].
p+i: 使p指向元素a[i].
注意!!!
(1)可以使用*(p+i)访问元素a[i]。
(2)因为p和a都表示数组首地址,所以p+i也可以记作a+i,指向元素a[i]。
(3)指向数组的指针变量也可以带下标,如:p[i]与*(p+i)和*(a+i)等价,表示元素a[i]。
由此可知:当指针变量p指向一维数组a,即指向一维数组的第一个元素a[0]后,数组的第i+1个元素有以下4种写法:
a[i] ; p[i] ; *(a+i) ; *(p+i) 。
a[i]的地址也对应有4种写法:
&a[i] ; &p[i] ;a+i ; p+i ;
2.指向一维数组非首元素的指针

例:

int a[5],*p;
p=&a[2];
//用法较少

举例:
(1)利用指向数组元素的指针输出数组a的各个元素。


#include<stdio.h>
#include<stdlib.h>
int main()
{
	int a[6] = { 11,22,33,44,55,66 };
	int *p;
	int i;
	for (p = a; p < a + 5; p++)
	{
		printf("%4d", *p);
		i++;
		if (i == 6)
			break;  //避免指针越界
	}
	system("pause");
	return 0;
}

(2)字符串复制:实现将字符数组str2中的字符串复制到字符数组str1中。

// 方法一:用数组名[下标]来访问数组元素
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int i;
	char str1[20], str2[20] = { "how are you?" };
	for (i = 0; (str1[i] = str2[i]) != '\0'; i++);
	puts(str1);
	system("pause");
	return 0;
}
// 方法二:用指针名[下标]来访问数组元素
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int i;
	char str1[20], str2[20] = { "how are you?" };
	char *p1 = str1, *p2 = str2;
	for (i = 0; (p1[i] = p2[i]) != 0; i++);
	puts(str1);
	system("pause");
	return 0;
}
// 方法三:用指针名加偏移量计算出来的地址来访问数组元素
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int i;
	char str1[20], str2[20] = { "how are you?" };
	char *p1 = str1, *p2 = str2;
	for (i = 0; (*(p1+i) = *(p2+i)) != 0; i++);
	puts(str1);
	system("pause");
	return 0;
}
// 方法四:用数组名加偏移量计算出来的地址来访问数组元素
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int i;
	char str1[20], str2[20] = { "how are you?" };
	for (i = 0; (*(str1+i) = *(str2+i)) != 0; i++);
	puts(str1);
	system("pause");
	return 0;
}

一维数组中几个关键符号的理解:
以int buf[100]={0}为例:
1.buf:两层含义,一是数组名,sizeof(buf)时,就是数组名的含义。二是等价于&buf[0],表示数组第一个元素的首字节地址,是一个常量值。(不能作为左值(作为数组名时,不包括数组的初始化),作为右值时,表示一个地址)
2.buf[0]:第一个元素的空间,可对其进行读写操作,作为左值被写,作为右值被读。
3.&buf[0]:等价于buf,是一个地址常量,只能作为右值。
4.&buf:表示数组首地址,是一个地址常量,只能作为右值。
buf与&buf的值相等,但含义不同。printf("%p\n",buf)与printf("%p\n",&buf)打印结果相同,表示值相等。printf("%p\n",buf+1)与printf("%p\n",&buf+1)打印结果完全不相同。buf表示数组的第一个元素首字节的地址,加1加的是一个元素空间的大小;&buf表示数组首地址,加1加的是整个数组空间大小,主要用于构造多维数组。

二。指向二维数组元素的指针
当指针p指向二维数组中的某个元素后,可以用指针p访问二维数组的所有元素。
例:

int a[2][5];
int *p = &a[0][0];

p指向二维数组a的首元素,如图一。若将数组元素用指针p表示出来,如图二。
在这里插入图片描述
假设指针变量p已经指向共有M行N列的数组A的首元素,则:
A[i][j]的地址是:p+i * N+j ;A[i][j]可表示为:*(p+i * N+j)。
二维数组a[ ][ ]的有关指针
注意:&a[i]和a[i]值虽然一样,但&a[i]或a+i指向行,而a[i]或 * (a+1)指向列

表示形式含义
a二维数组名,指向一维数组a[0], 即0行起始地址
a[0],*(a+0),*a0行0列元素的地址
a+1,&a[1]1行起始地址
a[1],*(a+1)1行0列元素a[1][0]的地址
a[1]+2,*(a+1)+2,&a[1][2]1行2列元素a[1][2]的地址
*(a[1]+2), ((a+1)+2),a[1][2]1行2列元素a[1][2]的值

例:输出二维数组的有关数据(地址和元素的值)。

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int a[3][4] = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23 };
	printf("%d  %d\n\n", a, *a);		//0行起始地址  ,  0行0列元素的地址
	printf("%d  %d\n\n", a[0], *(a + 0));  //0行0列起始地址
	printf("%d  %d\n\n", &a[0], &a[0][0]);  //0行起始地址 ,   0行0列元素的地址
	printf("%d  %d\n\n",a[1],a+1);  //1行0列元素地址 ,   第1行起始地址
	printf("%d  %d\n\n",&a[1][0],*(a+1)+0);  //1行0列元素地址
	printf("%d  %d\n\n",a[2],*(a+2));  // 2行0列元素地址
	printf("%d  %d\n\n",&a[2],a+2); //2行起始地址 
	printf("%d  %d\n\n",a[1][0],*(*(a+1)+0));   //1行0列元素的值
	printf("%d  %d\n\n",*a[2],*(*(a + 2) + 0));  //2行0列元素的值
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述

例:用指针法求二维数组中的最大值。

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int a[2][5], max, i, j;
	int *p = &a[0][0];
	printf("请输入数组中的各元素\n");
	for (i = 0; i < 2; i++)
	{
		for (j = 0; j < 5; j++)
		{
			scanf("%d", p++);		//通过p++依次引用各数组元素的地址
		}
	}
	max = a[0][0];
	p = &a[0][0];
	for (i = 0; i < 2; i++)
	{
		for (j = 0; j < 5; j++)
		{
			if (max < *(p + i * 5 + j))
				max = *(p + i * 5 + j);		//max总是存放比较大的数
		}
	}
	for (i = 0; i < 2; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%5d", *(p + i * 5 + j));	//输出数组
		}
	}
	printf("\n数组中的最大值为:%d\n", max);
	system("pause");
	return 0;
}

上述例子为指针指向整型变量,下面给出例子为:指针指向一个包含m个元素的一维数组。

//输出二维数组任一行任一列元素的值。
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int a[3][4] = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23 };		//定义二维数组并初始化
	int(*p)[4], i, j;  //指针变量p是指向包含4个整型元素的一维数组
	p = a;		//p指向二维数组的0行
	printf("输入要求输出的行号和列号:\n");
	scanf("%d%d", &i, &j);
	printf("a[%d][%d]=%d\n", i, j, *(*(p + i) + j));		//输出a[i][j]的值
	system("pause");
	return 0;
}

三.指向数组首元素的指针变量的运算
若指针变量p指向数组a的首元素,则:
(1)p++(或p+=1)
p指向下一个元素。
(2)*p++
相当于 *(p++)。都是先取 *p 的值,然后使p加1。
(3) *(p++)与 *(++p)
*(p++)同 *p++,先取 *p 的值,然后使p加1;
*(++p)先使p加1,然后取 *p 的值。
若p初值为a(即&a[0]),若输出 *(p++),得到a[0]的值,若输出 *(++p),得到a[1]的值。
(4)(*p)++
表示p指向的元素值加1。相当于(a[0]++)。
(5)如果指针指向数组a的非首元素a[i]。则:

  • *(p–)相当于a[i–],先取 *p,再使p减1.
  • *(p++)相当于a[++i],先使p+1,在取 *p.
  • *(–p)相当于a[–i],先使p-1,在取 *p.

指针数组和数组指针的区别:

指针数组
指针数组:指针数组可以说成是”指针的数组”,首先这个变量是一个数组。
其次,”指针”修饰这个数组,意思是说这个数组的所有元素都是指针类型。
在 32 位系统中,指针占四个字节。
数组指针
数组指针:数组指针可以说成是”数组的指针”,首先这个变量是一个指针。
其次,”数组”修饰这个指针,意思是说这个指针存放着一个数组的首地址,或者说这个指针指向一个数组的首地址。

指针数组的例题:

//将若干字符串按字母顺序(由小到大)输出
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void sort(char *name[], int n)		//对字符串排序
{
	char *t;
	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)
		{
			t = name[i];
			name[i] = name[k];
			name[k] = t;
		}
	}
}
void print(char *name[], int n)		//打印
{
	int i;
	for (i = 0; i < n; i++)
	{
		printf("%s\n", name[i]);
	}
}
int main()
{
	char *name[] = { "Follow me", "BASIC", "Great Wall", "FOREVER", "Computer design" };		//指针数组的值为各字符串的首字符的地址
	int n = 5;
	sort(name, n);		//实参为数组首元素的地址,形参为指针数组名
	print(name, n);
	system("pause");
	return 0;
}

*指针的一些复杂说明:

  1. int p; 这是一个普通的整型变量
  2. int *p 首先从 p 处开始,先与*结合,所以说明 p 是一个指针, 然后再与 int 结合, 说明指针所指向的内容的类型为 int 型。所以 p 是一个返回整型数据的指针。
  3. int p[3] – 首先从 p 处开始,先与[] 结合,说明 p 是一个数组, 然后与 int 结合, 说明数组里的元素是整型的, 所以 p 是一个由整型数据组成的数组。
  4. int *p[3]首先从 p 处开始, 先与 [] 结合, 因为其优先级比 * 高,所以 p 是一个数组, 然后再与 * 结合, 说明数组里的元素是指针类型, 然后再与 int 结合, 说明指针所指向的内容的类型是整型的, 所以 p 是一个由返回整型数据的指针所组成的数组。(指针数组)
  5. int (*p)[3] 首先从 p 处开始, 先与 * 结合,说明 p 是一个指针然后再与 [] 结合(与"()"这步可以忽略,只是为了改变优先级), 说明指针所指向的内容是一个数组, 然后再与int 结合, 说明数组里的元素是整型的。所以 p 是一个指向由整型数据组成的数组的指针。(数组指针)
  6. int **p 首先从 p 开始, 先与 * 结合, 说是 p 是一个指针, 然后再与 * 结合, 说明指针所指向的元素是指针, 然后再与 int 结合, 说明该指针所指向的元素是整型数据。由于二级指针以及更高级的指针极少用在复杂的类型中, 所以后面更复杂的类型我们就不考虑多级指针了, 最多只考虑一级指针。
  7. int p(int) 从 p 处起,先与 () 结合, 说明 p 是一个函数, 然后进入 () 里分析, 说明该函数有一个整型变量的参数, 然后再与外面的 int 结合, 说明函数的返回值是一个整型数据。
  8. int (*p)(int)从 p 处开始, 先与指针结合, 说明 p 是一个指针, 然后与()结合, 说明指针指向的是一个函数, 然后再与()里的 int 结合, 说明函数有一个int 型的参数, 再与最外层的 int 结合, 说明函数的返回类型是整型, 所以 p 是一个指向有一个整型参数且返回类型为整型的函数的指针。
  9. int *(*p(int))[3] 可以先跳过, 不看这个类型, 过于复杂从 p 开始,先与 () 结合, 说明 p 是一个函数, 然后进入 () 里面, 与 int 结合, 说明函数有一个整型变量参数, 然后再与外面的 * 结合, 说明函数返回的是一个指针, 然后到最外面一层, 先与[]结合, 说明返回的指针指向的是一个数组, 然后再与 * 结合, 说明数组里的元素是指针, 然后再与 int 结合, 说明指针指向的内容是整型数据。所以 p 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数。

指针与函数


一。指针做函数参数
指针可以作为参数在调用函数和被调用函数之间传递数据,传递的是“地址值”。

如果有一个实参数组,要想改变此数组中元素的值,实参与形参的对应关系有四种:

  • 形参和实参都用数组名。(不举例)
  • 实参用数组名,形参用指针变量。
  • 实参为指针变量,形参用数组名。
  • 形参和实参都用指针变量。(实参中指针指向数组)

1.函数形参为指针,实参为地址表达式
例:利用指针做参数,交换两个变量的值。

#include<stdio.h>
#include<stdlib.h>
void swap1(int *a, int *b)
{
	int c;
	c = *a;	//*为解引用操作
	*a = *b;
	*b = c;
}

int main()
{
	int a = 10, b = 100;
	swap1(&a, &b);
	printf("调用swap1函数后,主函数中a和b的值为%d  %d\n", a, b);
	system("pause");
	return 0;
}

执行结果为:
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
void swap2(int *a, int *b)
{
	int *c;
	c = a;
	a = b;
	b = c;
}
int main()
{
	int a = 10, b = 100;
	swap2(&a, &b);
	printf("调用swap2函数后,主函数中a和b的值为%d  %d\n", a, b);
	system("pause");
	return 0;
}

执行结果为:
在这里插入图片描述
分析:
很明显,swap1函数正确,两个变量值得到了交换,而swap2函数调用后两个值并没有交换。
因为swap2函数虽然经过了一系列的变换,但是地址中的内容没有变。

不能企图通过改变指针形参的值而使指针实参的值而改变。

2.函数形参为指针,实参为数组名
例:将数组a中的n个整数按相反顺序存放。

#include<stdio.h>
#include<stdlib.h>
void fun(int *x, int n)		//形参为指针
{
	int *p, *i, *j;
	int temp,m;
	m = (n - 1) / 2;
	j = x + n - 1;
	p = x + m;  //p指向a[m]的地址
	for (i = x; i <= p; i++, j--)
	{
		temp = *i;  //交换a[i]和a[j]
		*i = *j;
		*j = temp;
	}
}
int main()
{
	int i, a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	printf("原顺序为:\n");
	for (i = 0; i < 10; i++)
	{
		printf("%4d", a[i]);
	}
	printf("\n");
	fun(a, 10);		//实参为数组名
	printf("排序后为:\n");
	for (i = 0; i < 10; i++)
	{
		printf("%4d", a[i]);
	}
	printf("\n");
	system("pause");
	return 0;
}

运行结果为:
在这里插入图片描述

  • 用指针做形参,数组名做实参,有两种方法:
  • 用指向变量的指针变量
  • 用指向一维数组的指针变量。
  • 举例如下:
//1.有3个学生,各学4门课,计算总平均成绩,第n个学生的学习成绩
#include<stdio.h>
#include<stdlib.h>
void average(float *p, int n)     //求平均成绩的函数
{
	float *p1;	
	float sum = 0, aver;
	p1 = p + n - 1;		//p1指向最后一个元素
	for (p; p <= p1; p++)		//使p先后指向二维数组的各个元素
	{
		sum = sum + (*p);
	}
	aver = sum / n;
	printf("总平均成绩为:%.2f\n", aver);
}

void search(float(*p)[4], int n)     //p是指向具有四个元素的一维数组的指针
{
	int i;
	for (i = 0; i < 4; i++)
	{
		printf("%.2f  ", *(*(p + n) + i));
	}
	printf("\n");
}
int main()
{
	int x;
	float score[3][4] = { { 65, 67, 70, 60 }, { 80, 87, 90, 81 }, { 90, 99, 100, 98 } };
	average(*score, 12);  //12个成绩的平均分。  实参: *score=score[0]=&score[0][0]
	
	scanf("%d", &x);
	printf("输出第%d个学生的成绩:\n", x);
	search(score, x);		//实参:score代表该数组第0行起始地址
	system("pause");
	return 0;
}

运行结果为:
在这里插入图片描述

//2.在题1的基础上,查找有一门以上课程不及格的学生,输出他们全部课程的成绩。
#include<stdio.h>
#include<stdlib.h>
void search(float(*p)[4], int n)		 //形参:指针p指向包含4个元素的一维数组
{
	int i, j, flag;
	for (j = 0; j < n; j++)
	{
		flag = 0;
		for (i = 0; i < 4; i++)
		{
			if (*(*(p + j) + i) < 60)		//p+j为score数组第j行的起始地址,*(p + j)=&score[j][0]  ,*(p + j)+i=&score[j][i]。
				flag = 1;
		}
		if (flag == 1)
		{
			printf("第%d个学生不及格,他的成绩为:\n", j + 1);
			for (i = 0; i < 4; i++)
			{
				printf("%.2f  ", *(*(p + j) + i));		//*(*(p + j) + i)就是score[j][i]的值
			}
			printf("\n");
		}
	}
}
int main()
{
	float score[3][4] = { { 65, 57, 70, 60 }, { 50, 87, 90, 81 }, { 90, 99, 100, 98 } };
	search(score, 3);
	system("pause");
	return 0;
}

结果为:在这里插入图片描述

3.实参为指针变量,形参用数组名。
举例:

//对10个整数由从大到小排序
#include<stdio.h>
#include<stdlib.h>
void fun(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;
		}
	}
}
int main()
{
	int i, a[10], *p;
	p = a;
	printf("请输入10个数\n");
	for (i = 0; i < 10; i++)
	{
		scanf("%4d", p++);
	}
	p = a;				//很重要!!使指针重新指向a[0]
	fun(p, 10);		//实参为指针
	printf("排序后的数为:\n");
	for (p = a, i = 0; i < 10; i++)
	{
		printf("%4d", *p);
		p++;
	}
	printf("\n");
	system("pause");
	return 0;
}

4.实参和形参都为指针变量
举例:

//对10个整数由从大到小排序
#include<stdio.h>
#include<stdlib.h>
void fun(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;
		}
	}
}
int main()
{
	int i, a[10], *p;
	p = a;
	printf("请输入10个数\n");
	for (i = 0; i < 10; i++)
	{
		scanf("%4d", p++);
	}
	p = a;				//很重要!!使指针重新指向a[0]
	fun(p, 10);		//实参为指针
	printf("排序后的数为:\n");
	for (p = a, i = 0; i < 10; i++)
	{
		printf("%4d", *p);
		p++;
	}
	printf("\n");
	system("pause");
	return 0;
}

函数指针与指针函数

一。函数指针:

1.定义:

函数指针是指向函数的指针变量,本质上是一个指针变量,表示的是一个指针,它指向的是一个函数,其形式一般如下:

类型说明符 (*函数名)(参数)

例如,int(*p)(int ,int);
定义p是一个指针,指向函数类型为整型且有两个整型参数的函数。
此时指针p的类型用:int( * )(int ,int)来表示。
2.用函数指针调用函数
(1)通过函数名调用函数。

//输出a和b中的最大值。
#include<stdio.h>
#include<stdlib.h>
int max(int x, int y)
{
	return((x > y) ? x : y);
}
int main()
{
	int a, b, c;
	printf("输入a和b的值\n");
	scanf("%d%d", &a, &b);
	c = max(a, b);
	printf("最大值为:%d", c);
	system("pause");
	return 0;
}

(2)通过指针调用它所指向的函数

//输出a和b中的最大值。
#include<stdio.h>
#include<stdlib.h>
int max(int x, int y)
{
	return((x > y) ? x : y);
}
int main()
{
	int(*p)(int, int);		//定义函数指针
	p = max;
	int a, b, c;
	printf("输入a和b的值\n");
	scanf("%d%d", &a, &b);
	c =(*p)(a, b);
	printf("最大值为:%d", c);
	system("pause");
	return 0;
}

3。用函数指针做函数参数

原理简述:

有一个函数(fun),有两个形参(x1和x2),定义x1和x2为指向函数的指针。在调用函数fun时,实参为两个函数名f1,f2,给形参传递的是函数f1和f2的入口地址,这样在fun中就可以调用f1和f2函数了。

void fun(int(*x1)(int), int(*x2)(int, int))	    //形参为函数指针(f1,f2)
{
	int a, b, i = 3,j = 5;
	a = (*x1)(i);			//调用f1函数,i是形参
	b = (*x2)(i, j);		//调用f1,f2函数,i,j是形参
}

例题:
有两个整数a和b,由用户输入1,2,3其中一个值。如输入1,则程序给出最大值,如输入2,则给出最小值,如输入3,则求两数之和。

#include<stdio.h>
#include<stdlib.h>
int fun(int x, int y, int(*p)(int, int))			//形参为函数指针
{
	int result;
	result = (*p)(x, y);
	printf("%d\n", result);		//输出结果
	return(result);
}
int max(int x, int y)
{
	printf("最大值为:\n");
	return((x > y) ? x : y);
}
int min(int x, int y)
{
	printf("最小值为:\n");
	return((x < y) ? x : y);
}
int add(int x, int y)
{
	int z; 
	z = x + y;
	printf("和为:\n");
	return (z);
}
int main()
{
	int a = 100, b = -10,n;
	printf("请输入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);
	else
		printf("请重新输入。");
	system("pause");
	return 0;
}


二。指针函数

1.定义:
是指带指针的函数,本质上是一个函数,函数返回类型是某一类型的指针,
其形式一般如下所示:

类型标识符 *函数名(参数列表)

例如:
int *max(int x,int y);
函数名为max,其返回值为指针,指向整型数据。

例题:

//1.有a个学生,每个学生有b门课程的成绩。要求用户输入学号后,输出该学生的全部成绩。
//用指针函数来实现。

#include<stdio.h>
#include<stdlib.h>
float *search(float(*pointer)[4], int n)		//search为指针函数,  形参pointer是指向一维数组的指针变量
{
	float *pt;
	pt = *(pointer + n);		//pt的值为:&score[k][0]
	return(pt);
}
int main()
{
	float score[][4] = { { 65, 57, 70, 60 }, { 50, 87, 90, 81 }, { 90, 99, 100, 98 } };
	float *p;
	int i, k;
	printf("输入你要找的学生学号:\n");
	scanf("%d", &k);
	printf("学生%d的成绩为:\n", k);
	p = search(score, k-1);  //实参为数组首行地址, 返回score[k][0]的地址
	for (i = 0; i < 4; i++)
	{
		printf("%.2f\t", *(p + i));		//输出score[k][0]——score[k][3]的值。
	}
	printf("\n");
	system("pause");
	return 0;
}

结果为:
在这里插入图片描述

//在第一题的基础上,找出有不及格的成绩的学生和学号。
#include<stdio.h>
#include<stdlib.h>
float *search(float(*pointer)[4])		//search为指针函数,  形参pointer是指向一维数组的指针变量
{
	int i;
	float *pt;
	pt =NULL;		//初始化
	for (i = 0; i < 4; i++)
	{
		if (*(*pointer + i) < 60)
			pt = *pointer;				//有不及格,使pt指向score[i][0]
	}
	return(pt);
}
int main()
{
	float score[][4] = { { 65, 57, 70, 60 }, { 50, 87, 90, 81 }, { 90, 99, 100, 98 } };
	float *p;
	int i, j;
	for (i = 0; i < 3; i++)
	{
		p = search(score + i);	//调用search函数,如果有不及格则返回score[i][0]的地址,否则返回NULL.
		if (p == *(score + i))		//如果返回score[i][0]的地址,则p不为空。
		{
			printf("学生%d的成绩为:\n", i+1);
			for (j = 0; j < 4; j++)
			{
				printf("%.2f\t", *(p + j));		//输出score[i][0]——score[i][3]的值。
			}
			printf("\n");
		}
	}
	printf("\n");
	system("pause");
	return 0;
}

结果为:
在这里插入图片描述


指针与字符串


1.使指针变量指向字符串的方法
(1)在定义的同时给指针变量赋初值。
char *p=“Gold medal”;
(2)给指针变量赋值。
char *p;
p=“Gold medal”;

#include<stdio.h>
#include<stdlib.h>
int main()
{
	char *p = "Gold medal";
	printf("%s", p);
	system("pause");
	return 0;
}

#include<stdio.h>
#include<stdlib.h>
int main()
{
	char *p = "Gold medal";
	while (*p != '\0')
	{
		printf("%c", *p++);
	}
	system("pause");
	return 0;
}

都是将字符串的第一个字符的地址赋给指针变量p。
C语言对字符串常量是按字符数组处理的,在内存中开辟一个字符数组来存放字符串常量(包括字符串结束符‘\0’),程序在定义字符指针变量p时只是把字符串的首地址赋给p,而不能把整个字符串赋给p。
2.指向字符串常量的指针变量的使用
(1)把字符串当作整体来处理

  • scanf("%s",指针变量);
  • gets(指针变量);
  • printf("%s",指针变量);
  • puts(指针变量);

(2)处理字符串中的单个字符
若指针已经指向了字符串常量,则用指针变量表示的第i个字符为:
*(指针变量+i)
3.字符串中字符的存取方式

  • 下标方法
  • 指针方法
    例题:
    将字符串a复制为字符串b,然后输出字符串b。(两种方法)
//1。数组的方法
#include<stdio.h>
#include<stdlib.h>
int main()
{
	char a[] = "I am a student.", b[20];  //定义字符数组
	int i;
	for (i = 0; *(a + i) != '\0'; i++)
	{
		*(b + i) = *(a + i);		//将a[i]的值赋给b[i]
	}
	*(b + i) = '\0';		//数组b的有效字符后加'\0'
	printf("字符串a是:%s\n", a);
	printf("字符串b是:\n");
	for (i = 0; b[i] != '\0'; i++)
	{
		printf("%c", b[i]);		//逐个输出字符
	}
	printf("\n");
	system("pause");
	return 0;
}
//2。指针的方法
#include<stdio.h>
#include<stdlib.h>
int main()
{
	char a[] = "I am a student.", b[20];  //定义字符数组
	char *p1, *p2;
	p1 = a; p2 = b;		//指针分别指向数组的第一个元素
	for (p1; *p1 != '\0'; p1++,p2++)
	{
		*p2 = *p1;		//将p1所指向的元素的值赋给p2所指向的元素
	}
	*p2 = '\0';		//有效字符后加'\0'
	printf("字符串a是:%s\n", a);
	printf("字符串b是:%s\n", b);
	system("pause");
	return 0;
}

4.使用字符指针变量与字符数组的区别
(1)存储内容不同:
字符指针变量本身是一个变量,用于存放字符串的首地址,编译时只为其分配一个存储单元。而字符串本身就是存放在以该首地址为首的一块连续内存空间中并以‘\0’作为结束标志。
字符数组是由若干数组元素组成,每个元素中放一个字符,编译时为其分配若干存储单元
(2)赋值方法不同:
对于字符指针变量,可用以下方法赋值:
char *p;
p=“Gold medal”;
而字符数组只能对各个元素逐个赋值,不能对数组名赋值。

char p[20];
p = "Gold medal";
//该种赋值方法错误

(3)指针变量的值是可以改变的。
而数组名虽是地址,但它的值是不能改变的。

#include<stdio.h>
#include<stdlib.h>
int main()
{
	char *p ="Gold medal";
	p = p + 5;
	printf("%s", p);
	system("pause");
	return 0;
}
//运行结果为:
medal
char p[] ="Gold medal";
p = p + 5;
printf("%s", p);
//是错误的

(4)输入字符串时有区别;
对于字符指针变量:

	char *p;
	scanf("%s", p);
	//是不可以的。但是可以这样输入:
	
	char str[20];
	char *p=str;  //使p指向了确定内存
	scanf("%s", p);  

对于字符数组:

char p[10];
scanf("%s", p);
//是可以的

5.字符指针做函数参数
在被调函数中可以改变字符串的内容,在主调函数中可以引用改变后的字符串。
(1)用字符数组名作为函数参数。

//用函数调用实现字符串的复制。
#include<stdio.h>
#include<stdlib.h>
void copy(char from[], char to[])		//形参为字符数组
{
	int i = 0;
	while (from[i] != '\0')
	{
		to[i] = from[i];
		i++;
	}
	to[i] = '\0';
}
int main()
{
	char a[] = "I am a student.";
	char b[] = "You are a teacher.";
	printf("原字符串a为:%s\n", a);
	printf("原字符串b为:%s\n", b);
	printf("把字符串a复制到字符串b中\n");
	copy(a, b);
	printf("复制后的字符串为:\n%s\n%s\n", a, b);
	system("pause");
	return 0;
}

(2)用字符型指针做为函数实参

#include<stdio.h>
#include<stdlib.h>
void copy(char from[], char to[])		//形参为字符数组
{
	int i = 0;
	while (from[i] != '\0')
	{
		to[i] = from[i];
		i++;
	}
	to[i] = '\0';
}
int main()
{
	char a[] = "I am a student.";
	char b[] = "You are a teacher.";
	char *from = a, *to = b;
	printf("原字符串a为:%s\n", a);
	printf("原字符串b为:%s\n", b);
	printf("把字符串a复制到字符串b中\n");
	copy(from, to);		//实参为字符指针变量
	printf("复制后的字符串为:\n%s\n%s\n", a, b);
	system("pause");
	return 0;
}

(3)用字符型指针做为函数实参和形参

#include<stdio.h>
#include<stdlib.h>
void copy(char *from, char *to)		//形参为字符指针变量
{
	int i = 0;
	for (from; *from != '\0';from++,to++)
	{
		*to = *from;
	}
	*to = '\0';
}
int main()
{
	char *a = "I am a student.";
	char b[] = "You are a teacher.";
	char *p=b;
	printf("原字符串a为:%s\n", a);
	printf("原字符串b为:%s\n", b);
	printf("把字符串a复制到字符串b中\n");
	copy(a,p);		//实参为字符指针变量
	printf("复制后的字符串为:\n%s\n%s\n", a, b);
	system("pause");
	return 0;
}

此篇博客创作属实不易,博主熬到半夜好几天啦,一字一句地可算整理出来了。如果有错误,请多多指教。

  • 50
    点赞
  • 160
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值