10. C语言 指针

    目录

10.1 指针的说明

10.2 使用指针

10.3 C 中的 NULL 指针

10.4 指针的算术运算

10.5 指针数组

10.5.1. 下标法

10.5.2. 指针法

10.5.3 指针数组存储字符串

10.6 指向指针的指针

10.7 指针作函数参数

10.7.1 函数参数使用数组名a

10.7.2 函数参数使用指针变量

10.7.3 指针变量作实参

10.8 从函数返回指针

10.9 指针处理链表

10.9.1 静态链表

10.9.2 动态链表

10.10 指向函数的指针


每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。

10.1 指针的说明

        指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。

        就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对其进行声明。

type *var-name;

        type 是指针的基类型,它必须是一个有效的 C 数据类型,var-name 是指针变量的名称。

        用来声明指针的星号 * 与乘法中使用的星号是相同的。

        但是,在这个语句中,星号是用来指定一个变量是指针。

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

10.2 使用指针

        使用指针时会频繁进行以下几个操作:

        定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。

        这些是通过使用一元运算符 * 来返回位于操作数所指定地址的变量的值。

/*-----------------------------------------------------------------------*/
#include <stdio.h>
/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 main 的功能描述:
*			使用指针并获取相关数据.
*  @param[in]  void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int main()
{
	int  var = 20;	//实际变量的声明
	int  *ip;		//指针变量的声明

	ip = &var;	//在指针变量中存储 var 的地址

	printf("Address of var variable: 0x%x\n", &var);

	/* 在指针变量中存储的地址 */
	printf("Address stored in ip variable: 0x%x\n", ip);

	/* 使用指针访问值 */
	printf("Value of *ip variable: %d\n", *ip);

	return 0;
}
/*-----------------------------------------------------------------------*/

Visual Studio 运行结果:

10.3 C 中的 NULL 指针

        在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。

        赋为 NULL 值的指针被称为空指针。

        NULL 指针是一个定义在标准库中的值为零的常量。

/*-----------------------------------------------------------------------*/
#include <stdio.h>
/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 main 的功能描述:
*			使用NULL指针.
*  @param[in]  void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int main()
{
    int  *ptr = NULL;
	printf("ptr 的值是 %x\n", ptr);

	return 0;
}
/*-----------------------------------------------------------------------*/

Visual Studio 运行结果:

        在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。

        然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。

        但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。

        如需检查一个空指针,使用 if 语句,如下所示:

if(ptr)     /* 如果 p 非空,则完成 */
if(!ptr)    /* 如果 p 为空,则完成 */

10.4 指针的算术运算

        可以对指针进行四种算术运算:++、--、+、-

/*-----------------------------------------------------------------------*/
#include <stdio.h>
/*-----------------------------------------------------------------------*/
/* 全局变量 */
const int MAX = 3;
/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 main 的功能描述:
*			指针的算术运算.
*  @param[in]  void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int main()
{
	int  var[] = { 10, 100, 200 };
	int  i, *ptr;

	/* 指针中的数组地址 */
	ptr = var;
	for (i = 0; i < MAX; i++)
	{

		printf("Address of var[%d] = %x\n", i, ptr);
		printf("Value of var[%d] = %d\n", i, *ptr);

		/* 移动到下一个位置 */
		ptr++;
	}

	return 0;
}
/*-----------------------------------------------------------------------*/

Visual Studio 运行结果:

10.5 指针数组

        可以定义用来存储指针的数组。

int *ptr[MAX];

        ptr 声明为一个数组,由 MAX 个整数指针组成。

        因此,ptr 中的每个元素,都是一个指向 int 值的指针。

int a[10] = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 };
int *p;     //定义p为指向整型变量的指针变量
p = &a[0];  //把a[0]元素的地址赋值给指针变量p
// 等价
p = a;      //p的值是a首元素的地址

//定义指针变量时可以对它初始化
int *p = &a[0];
//也可以写成:
int *p = a;

有一个整型数组a,有10个元素,要求输出数组中的全部元素

10.5.1. 下标法
/*-----------------------------------------------------------------------*/
#include <stdio.h>
/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 main 的功能描述:
*			使用数组下标法,
*				整型数组a,有10个元素,要求输出数组中的全部元素.
*  @param[in]  void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int main()
{
	int a[10] = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 };
	int i;
	printf("Please output 10 numbers:");
	for (i = 0; i < 10; i++)
	{
		printf("%d ", a[i]);
	}
    printf("\n");

	return 0;
}
/*-----------------------------------------------------------------------*/

Visual Studio 运行结果:

10.5.2. 指针法
/*-----------------------------------------------------------------------*/
#include <stdio.h>
/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 main 的功能描述:
*			使用指针法,
*				整型数组a,有10个元素,要求输出数组中的全部元素.
*  @param[in]  void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int main()
{
	int a[10] = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 };
	int i, *p;
	printf("Please output 10 numbers:");
	for (p = a; p < (a + 10); p++)
	{
		printf("%d ", *p);
	}
    printf("\n");

	return 0;
}
/*-----------------------------------------------------------------------*/

Visual Studio 运行结果:

10.5.3 指针数组存储字符串

        也可以用一个指向字符的指针数组来存储一个字符串列表,如下:

/*-----------------------------------------------------------------------*/
#include <stdio.h>
/*-----------------------------------------------------------------------*/
/* 全局变量 */
const int MAX = 4;
/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 main 的功能描述:
*			一个指向字符的指针数组来存储一个字符串列表.
*  @param[in]  void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int main()
{
	char *names[] =
	{
		"AAAAA",
	    "BBBBB",
		"CCCCC",
		"DDDDD",
	};
	int i = 0;

	for (i = 0; i < MAX; i++)
	{
		printf("Value of names[%d] = %s\n", i, names[i]);
	}

	return 0;
}
/*-----------------------------------------------------------------------*/

Visual Studio 运行结果:

10.6 指向指针的指针

        C 允许指向指针的指针。

        指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。

        通常,一个指针包含一个变量的地址。

        当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。

int **ptr;

10.7 指针作函数参数

        通过引用或地址传递参数,使传递的参数在调用函数中被改变。

        C 语言允许传递指针给函数,只需要简单地声明函数参数为指针类型即可。

10.7.1 函数参数使用数组名a
/*-----------------------------------------------------------------------*/
#include <stdio.h>
/*-----------------------------------------------------------------------*/
/* 函数声明 */
void Sort(int x[], int n);

/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 main 的功能描述:
*			将数组a中n个整数从小到大存放.
*  @param[in]  void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int main()
{
	int i = 0;
	int a[10] = { 3, 7, 9, 11, 0, 6, 7, 5, 4, 2 };
	/*-----------------------*/
	printf("The original array: \n");
	for (i = 0; i < 10; i++)
		printf("%d ", a[i]);
	printf("\n");
	/*-----------------------*/
	Sort(a, 10);	//功能函数调用
	/*-----------------------*/
	printf("The array has been sort: \n");
	for (i = 0; i < 10; i++)
		printf("%d ", a[i]);
	printf("\n");
	/*-----------------------*/
	return 0;
}
/*-----------------------------------------------------------------------*/

/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 Sort 的功能描述:
*			功能函数.
*  @param[in]
*				int x[] 输入的数组标
*				int n 待排序数据个数
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
void Sort(int x[], int n)
{
	int i = 0, j = 0;
	int k1 = 0;

	for (i = 0; i < n-1; i++)
	{
		k1 = x[i];
		for (j = i+1; j< n ; j++)
		{
			if (k1>x[j])
			{
			    x[i] = x[j];
				x[j] = k1;
				k1 = x[i];
			}
		}
	}
}
/*-----------------------------------------------------------------------*/

Visual Studio 运行结果:

10.7.2 函数参数使用指针变量
/*-----------------------------------------------------------------------*/
#include <stdio.h>
/*-----------------------------------------------------------------------*/
/* 函数声明 */
void Sort(int *x, int n);

/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 main 的功能描述:
*			将数组a中n个整数从小到大存放.
*  @param[in]  void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int main()
{
	int i = 0;
	int a[10] = { 3, 7, 9, 11, 0, 6, 7, 5, 4, 2 };
	/*-----------------------*/
	printf("The original array: \n");
	for (i = 0; i < 10; i++)
		printf("%d ", a[i]);
	printf("\n");
	/*-----------------------*/
	Sort(a, 10);	//功能函数调用
	/*-----------------------*/
	printf("The array has been sort: \n");
	for (i = 0; i < 10; i++)
		printf("%d ", a[i]);
	printf("\n");
	/*-----------------------*/
	return 0;
}
/*-----------------------------------------------------------------------*/

/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 Sort 的功能描述:
*			功能函数.
*  @param[in]
*				int x[] 输入的数组标
*				int n 待排序数据个数
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
void Sort(int *x, int n)
{
	int i = 0, j = 0;
	int k1 = 0;

	for (i = 0; i < n-1; i++)
	{
		k1 = *(x+i);
		for (j = i+1; j< n ; j++)
		{
			if (k1>*(x + j))
			{
			    *(x + i) = *(x + j);
				*(x + j) = k1;
				k1 = *(x + i);
			}
		}
	}
}
/*-----------------------------------------------------------------------*/

Visual Studio 运行结果:

10.7.3 指针变量作实参
/*-----------------------------------------------------------------------*/
#include <stdio.h>
/*-----------------------------------------------------------------------*/
/* 函数声明 */
void Sort(int *x, int n);

/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 main 的功能描述:
*			将数组a中n个整数从小到大存放.
*  @param[in]  void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int main()
{
	int i = 0;
	int a[10] = { 3, 7, 9, 11, 0, 6, 7, 5, 4, 2 };
	int *p = a;//指针变量指向a[0]
	/*-----------------------*/
	printf("The original array: \n");
	for (i = 0; i < 10; i++)
		printf("%d ", a[i]);
	printf("\n");
	/*-----------------------*/
	Sort(p, 10);	//功能函数调用
	/*-----------------------*/
	printf("The array has been sort: \n");
	for (p = a; p < a+10; p++)
		printf("%d ", *p);
	printf("\n");
	/*-----------------------*/
	return 0;
}
/*-----------------------------------------------------------------------*/

/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 Sort 的功能描述:
*			功能函数.
*  @param[in]
*				int x[] 输入的数组标
*				int n 待排序数据个数
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
void Sort(int *x, int n)
{
	int i = 0, j = 0;
	int k1 = 0;

	for (i = 0; i < n-1; i++)
	{
		k1 = *(x+i);
		for (j = i+1; j< n ; j++)
		{
			if (k1>*(x + j))
			{
			    *(x + i) = *(x + j);
				*(x + j) = k1;
				k1 = *(x + i);
			}
		}
	}
}
/*-----------------------------------------------------------------------*/

Visual Studio 运行结果:

10.8 从函数返回指针

        C 允许函数返回指针到局部变量、静态变量和动态内存分配。

        C 允许从函数返回指针。为了做到这点,必须声明一个返回指针的函数。

        另外,C 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。

        下面的函数会生成 10 个随机数,并使用表示指针的数组名(即第一个数组元素的地址)来返回它们。

/*-----------------------------------------------------------------------*/
#include <stdio.h>
#include <time.h>
#include <stdlib.h> 
/*-----------------------------------------------------------------------*/
/* 函数声明 */
int *getRandom();

/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 main 的功能描述:
*			将使用表示指针的数组名(即第一个数组元素的地址)来返回生成的数组.
*  @param[in]  void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int main()
{
	int i;
	int *p=NULL;	//一个指向整数的指针
	/*-----------------------*/
	p = getRandom();
	/*-----------------------*/
	for (i = 0; i < 10; i++)
	{
		printf("*(p + [%d]) : %d\n", i, *(p + i));
	}
	/*-----------------------*/
	return 0;
}
/*-----------------------------------------------------------------------*/

/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 getRandom 的功能描述:
*			要生成和返回随机数的函数.
*  @param[in]	void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int *getRandom()
{
	int i;
	static int  r[10];
	srand((unsigned)time(NULL));	//设置种子
	for (i = 0; i < 10; i++)
	{
		r[i] = rand();
		printf("%d\n", r[i]);
	}

	return r;
}
/*-----------------------------------------------------------------------*/

Visual Studio 运行结果:

10.9 指针处理链表

10.9.1 静态链表
/*-----------------------------------------------------------------------*/
#include <stdio.h>
/*-----------------------------------------------------------------------*/
/* 创建结构体 */
struct Student		//声明结构体类型struct Student
{
	int num;
	float score;
	struct Student *next;
};
/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 main 的功能描述:
*			使用指针处理静态链表.
*  @param[in]  void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int main()
{
	struct Student a, b, c, *head, *p;	//定义3个结构体变量a,b,c作为链表的结点
	a.num = 101; a.score = 89.5;		//对结点a的num和score成员赋值
	b.num = 103; b.score = 90;
	c.num = 107; c.score = 85;
	head = &a;							//将结点a的起始地址赋给头指针head
	a.next = &b;
	b.next = &c;
	c.next = NULL;						//c结点的next成员不存放其他结点地址
	p = head;							//使p指向a结点
	do
	{
		printf("%ld %5.1f\n", p->num, p->score);
		p = p->next;	//输出完c结点后p的值为NULL,循环终止
    } while (p != NULL);

	return 0;
}
/*-----------------------------------------------------------------------*/

Visual Studio 运行结果:

10.9.2 动态链表
/*-----------------------------------------------------------------------*/
#include <stdio.h>
#include <malloc.h>
/*-----------------------------------------------------------------------*/
/* 创建结构体 */
struct Student
{
	int num;
	float score;
	struct Student *next;
};
/*-----------------------*/
#define LEN sizeof(struct Student)	//获取结构体长度
int n;	//链表单元计数器
/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 creat 的功能描述:
*			建立链表的函数.
*  @param[in]	void
*
*  @return
*				struct Student *head	所配置的链表
*
*  @implements 低层需求标识号 XXXX
*/
struct Student *creat()
{
	struct Student *head = NULL;	//初始化头指针为空
	struct Student *p1;	//p1 用于遍历链表
	struct Student *p2;	//p2 用于连接链表节点

	n = 0;	//初始化链表单元计数器
	p1 = p2 = (struct Student*) malloc(LEN);	//先动态分配内存空间,并将 p1 和 p2 指向它
	/*-----------------------*/
	scanf("%ld,%f", &p1->num, &p1->score);	//p1是当前要录入数据的指针
	/*-----------------------*/
	while (p1->num != 0)//当输入0值时,跳出链表配置的输入
	{
		n = n + 1;
		if (n == 1)//将头指针指向链表第一个节点
		{
			head = p1;
		}
		else//连接上一个节点和当前节点
		{
			p2->next = p1;
		}
		p2 = p1;//更新 p2 为当前节点,以便连接下一个节点
		p1 = (struct Student*)malloc(LEN);//为下一个节点分配内存空间
		scanf("%ld,%f", &p1->num, &p1->score);
	}
	/*-----------------------*/
	p2->next = NULL;	//最后一个节点的 next 指针为 NULL,标志链表结束!
	/*-----------------------*/
	return(head);
}
/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 print 的功能描述:
*			输出链表的函数.
*  @param[in]  
*				struct Student *head	所配置的链表
*
*  @return  void
*
*  @implements 低层需求标识号 XXXX
*/
void print(struct Student *head)
{
	struct Student *p;
	p = head;
	/*-----------------------*/
	printf("\nNow,These %d records are:\n", n);	//输出链表元素实际个数
	/*-----------------------*/
	for (p = head; p != NULL; p = p->next)
	{
		printf("%ld %5.1f\n", p->num, p->score);
	}
	/*-----------------------*/
}
/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 main 的功能描述:
*			使用指针处理动态链表.
*  @param[in]  void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int main()
{
	struct Student *head;	//创建链表头指针
	head = creat();	//配置链表内容
	print(head);	//输出链表内容

	free(head);	//释放指针内存
	return 0;
}
/*-----------------------------------------------------------------------*/

Visual Studio 运行结果:

10.10 指向函数的指针

/*-----------------------------------------------------------------------*/
#include <stdio.h>
/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 main 的功能描述:
*			求两个数中的最大值.
*  @param[in]  void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int main()
{
	int max(int, int);	//函数声明
	int(*p)(int, int);	//定义指向函数的指针变量p
	int a, b, c;
	p = max;	//使p指向函数
	printf("Please enter a and b:");
	scanf("%d,%d", &a, &b);
	c = (*p)(a, b);	//通过指针变量调用函数
	printf("a=%d\nb=%d\nmax=%d\n", a, b, c);

	return 0;
}
/*-----------------------------------------------------------------------*/


/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 max 的功能描述:
*			功能函数.
*  @param[in]
*				int x 输入的值1
*				int y 输入的值2
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int max(int x, int y)
{
	int z;
	if (x > y)
	{
		z = x;
	}
    else
	{
		z = y;
	}
	return (z);
}
/*-----------------------------------------------------------------------*/

Visual Studio 运行结果:

非常感谢您的支持!创作不易,转发备注出处!

自用展示内容,不定期更新维护内容,让我们把 C开发 变得更专业。

十年开发基本功,常年开班收徒(一年学徒制度),每年10个名额。

商业合作加微信,项目接包与外包。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值