C指针的进阶使用

1. 数组与指针

概述:数组由若干个类型相同的元素组成,在内存中占据一段连续的存储空间。数组名就是其存储空间的起始地址,本身就是一个指针。

1.1 指针访问数组元素

代码演示:

#include<stdio.h>

int main()
{
	int a[5] = { 1,2,3,4,5 };
	int *p=a;  //等价于p=&a[0] ,把数组的首地址给指针p

	//以下的方式,基于数组内的元素地址连续。
	printf("----------指针法---------\n");
	for (int i = 0; i < 5; ++i)
	{
		printf("*(p+%d) = %d a[%d]的首地址为:%p\n", i, *(p + i), i, p + i);
		
	}
	printf("----------地址法---------\n");
	for (int i = 0; i < 5; ++i)
	{
		printf("*(a+%d) = %d a[%d]的首地址为:%p\n", i, *(a + i), i, a + i);

	}
	printf("----------下标法---------\n");
	for (int i = 0; i < 5; ++i)
	{
		printf("a[%d] = %d a[%d]的首地址为:%p\n", i, a[i], i, &a[i]);
	}
	printf("----------下标法---------\n");
	printf("这里只是把a换成p,因为p和a值相同,所以与上面的下标法等价\n");
	for (int i = 0; i < 5; ++i)
	{
		printf("a[%d] = %d a[%d]的首地址为:%p\n", i, p[i], i, &p[i]);
	}

	return 0;
}

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

注:
①下标法和地址法访问效率相同,下标法直观,易懂。指针法运行速度快。
②a是可以看成是指针常量,p是指针变量,故p++正确,a++错误,因为a是常量,不能改变。

1.2 指向多维数组的指针

数组的定义(抽象数据类型)数据结构里的数组较为复杂,其实不知道也没有关系,这里只要知道多维数组怎样存储在内存中的就行了,C语言里面我们一般讨论的是一维到三维的数组。这里我们一二维数组为例。

简单说一下二维数组如何存储在内存里的。我们都知道一维数组的存储是连续的,同样的,二维数组的存储也是连续的,地址是这样连续的:
如有二维数组a[3][3]
a[0][0] //先是第一个元素的二维数组的地址,等到第一
a[0][1] //个元素的二维数组地址完,才到第二个元素的地址
a[0][2] //依次类推
a[1][0]
… … … …

代码演示:

#include<stdio.h>

int main()
{
	int a[3][4] = {
		{1,2,3,4},
		{11,12,13,14},
		{21,22,23,24}
	};   //初始化
	int *p = &a[0][0]; //等价于p=a[0]

	for (p = &a[0][0]; p < &a[2][3]; ++p)
	{
		printf("%d ", *p);
	}
	//运行结果:1 2 3 4 11 12 13 14 21 22 23
	return 0;
}

注意:不能写成 p = a;如果这么写了,那么*p = a[0] ,而a[0]在二维数组中是a[0][0]的地址,而不是值,故会出错。

2. 字符串和指针

2.1 指针处理字符串

C语言中没有字符串类型,C语言以字符数组充当字符串。

代码演示:(以实现函数strcat功能为例)

#include<stdio.h>

void my_strsta(char *str1, char *str2)
{
	while (*str1)
		str1++;
	//先取值在自增,指针指向最后的'\0'自然会停下来
	while (*str1++ = *str2++);//这里有个分号
}

int main()
{
	char str1[15] = "hello ";
	char str2[10] = "world!";
	char *p1, *p2;
	p1 = str1;
	p2 = str2;
	printf("str1 = %s\n", str1);
	printf("str2 = %s\n", str2);

	printf("-------调用函数后-------\n");
	my_strsta(str1, str2);
	printf("str1 = %s\n", str1);
	printf("str1 = %s\n", str2);

	return 0;
}

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

2.2使用字符指针变量和字符数组的区别

代码演示:

#include<stdio.h>

int main()
{
	char str[] = "hello world";
	char *p = "hello C";
	printf("str = %s\n", str);
	printf("p = %s\n", str);
	str[0] = 'W';  //可以单个赋值
	//p[0] = 'W'; //不可以单个赋值

	printf("修改后---\n");
	printf("str = %s\n", str);//str = "hhhhhhh" //只能定义的时候初始化,不能赋值
	
	p = "hhjhhhhh"; //可以再次赋值
	printf("p = %s", p);


	return 0;
}

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

3. 函数和指针

3.1 普通变量作函数参数

代码演示:(经典例题用函数交换两数)

#include<stdio.h>

void swap_1(int a, int b)
{
	int t;
	t = a;
	a = b;
	b = t;
}
void swap_2(int *a, int *b)
{
	int t;
	t = *a; 
	*a = *b; 
	*b = t;
}
int main()
{
	int a = 12, b = 15;
	int x = 19, y = 99;
	printf("交换后\n");
	swap_1(a, b);
	printf("a = %d,b = %d\n", a, b); //将会失败
	swap_2(&x, &y);
	printf("x = %d,y = %d\n", x, y);  //将会成功
	return 0;
}

这里有函数参数传递的机制

3.2 数组名作函数的参数

代码演示1:(冒泡排序)

#include<stdio.h>

void bubbleSort(int a[], int n)  //非降序
{
	int i, j;
	int t;
	for (i = 0; i < n - 1; i++)  //n个数,花n-1次来确定大小关系
	{
		for (j = 0; j < n - 1 - i; j++) //确定一个数的大小得分别与其他数比较
		{                          //每次比较确定一个数,下次便可以少比较一个数
			if (a[j + 1] < a[j]) 
			{
				t = a[j + 1];
				a[j + 1] = a[j];
				a[j] = t;
			}
		}
	}
}

int main()
{
	int a[10] = { 12,2,33,-4,45,-6,37,84,59 };

	for (int i = 0; i < 10; i++)
	{
		printf("%3d", a[i]);
	}
	printf("\n排序后----------------\n");
	bubbleSort(a,10);
	for (int i = 0; i < 10; i++)
	{
		printf("%3d", a[i]);
	}
	return 0;
}

运行结果:
在这里插入图片描述
代码演示2:(选择排序 )

#include<stdio.h>
#include<stdlib.h>

void printArray(int a[][6], int n)
{
	for (int i=0; i < n; ++i)
	{
		for (int j=0; j < 6; j++)
		{
			printf("%3d", a[i][j]);
		}
		printf("\n");
	}
}

void insertSort(int a[][6], int n)  //6必须加,n为第一维得长度
{   //非降序
	int i, j;
	int min;
	for (i = 0; i < n * 6; i++)
	{
		min = i;
		for (j = i + 1; j < n * 6 - 1; j++)
		{
			if (a[j / 6][j % 6] < a[min/6][min%6])
				min = j;
		}
		if (min != i)
		{
			int t = a[min / 6][min % 6];
			a[min / 6][min % 6] = a[i / 6][i % 6];
			a[i / 6][i % 6] = t;
		}
	}
}

int main()
{
	int a[5][6] = { 0 };

	for (int i=0; i < 5; i++)
	{
		for (int j=0; j < 6; j++)
		{
			a[i][j] = rand() % 100;//函数rand生成伪随机数,每次运行得随机数都一样
		}					//rand在头文件<stdlib.h>中
	}
	printf("排序前:\n");  
	printArray(a, 5);  //打印二维数组

	printf("排序后:\n");
	insertSort(a, 5);
	printArray(a, 5);
	return 0;
}

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

3.3 指针作为函数得返回值

代码演示:

#include<stdio.h>

int *fun(int a[],int n)
{
	for (int i = 0; i < n; ++i)
	{
		a[i] = i+1;
	}
	return a;
}

int main()
{//例子除了简单之外,没啥用
	int a[10];
	int *p = fun(a,10);

	for (int i = 0; i < 10; i++)
	{
		printf("%3d", *(p + i));
	}

	return 0;
}

3.3 指向函数的指针

声明格式:
数据类型 (*指针变量名)(形参列表)

代码演示:

#include<stdio.h>

int add(int a, int b)  
{
	return a + b;
}
int minus(int a, int b)
{
	return a - b;
}
//  int(*fun)(int x, int y);
//  x,y为占位符有没有都行
//  int为数据类型,即返回值类型
//  fun为指针变量名
//  int x,int y为形参列表
int compute(int a, int b, int(*fun)(int, int))
{ //最后一个参数是函数参数
	return (*fun)(a, b);
}
int main()
{
	int a = 12, b = 15;
	printf("%d + %d = %d\n", a, b, compute(a, b, add));
	printf("%d - %d = %d\n", a, b, compute(a, b, minus));
	
	return 0;
}

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

小结:那么指针函数究竟有什么用呢?
①它可以管理,同类型的函数。
②多态
③当然,还用很多用处

4. 指针数组

4.1 指针数组的定义

指针数组中的每一个元素都是一个指针,并且指向相同类型的变量。

定义格式:
类型名 *数组名[数组长度]

代码演示:

#include<stdio.h>

int main()
{
	char *p[5] = {
		"one",
		"two",
		"three",
		"four",
		"five"
	};
	for (int i = 0; i < 5; i++)
	{
		printf("%s\n", p[i]);
	}
	return 0;
}
//运行结果:
one
two
three
four
five

注:数组中的每个元素只存储了字符串的首地址,故只能访问字符串,不能修改字符串中的字符。不然会有类似的错误。
在这里插入图片描述

4.2 带参数的main函数

用于接收程序运行过程中,用户输入的参数

定义格式:
void main(int argc,char *argv[])
{

}
注:若main函数带参数,只能带这两中类型的参数,否则就不带。
代码演示:

#include<stdio.h>

int main(int argc,char *argv[])
{
	for (int i = 0; i < argc; ++i)
	{
		printf("%s\n", argv[i]);
	}
	
	return 0;
}

使用步骤:
①运行上述代码,生成一个可执行文件
②按win+R 输入cmd打开命令行(windows系统)
③进入保存可执行文件的路径
④输入可执行文件的名字即可执行
⑤若在可执行文件的后面进行输入,那么输入将会被那俩参数收到。

如图:
在这里插入图片描述

小结:argc=参数个数+1
argv[0]存储文件名的首地址
argv[1]存储第一个参数的首地址
argv[2]存储第二个参数的首地址
argv[3]存储第三个参数的首地址

5. 数组指针

注:二维数组int a[m][n],可以看成是长度为m类型为int[n]的一维数组

声明格式:
数据类型 (*变量名)[一维数组的长度]

代码演示:

#include<stdio.h>

int main()
{
	int(*p)[4];  //声明指针,不过指针没有指向
	int a[3][4] = {    //初始化
		{1, 2, 3, 4},
		{5, 6, 7, 8},
		{9, 10, 11, 12}
	};
	p = a;     //p指向数组a中的int[4]类型,等价于 p=&a[0]
				//故*p = a[0],又*p=a[0]等价于*p=&a[0][0]
				//因此**p = a[0][0],依次类推
	for (int i=0; i < 3; ++i)
	{
		printf("**(p+%d) = %d\n", i, **(p + i));  
		//p指向int[4]类型,故每次p+i,实际上是偏移了1*4*sizeof(int)=16
		printf("p+%d的地址为: %p\n",i, p + i);
	}
	/*
	运行结果:   (十六进制)
			**(p + 0) = 1
			p + 1的地址为: 004FFA18
			**(p + 1) = 5
			p + 1的地址为: 004FFA28
			**(p + 2) = 9
			p + 1的地址为: 004FFA38
			*/
	return 0;
}

小结:
代码演示:

#include<stdio.h>

int main()
{
	int a[2][3][4] = { 0 };
	int *p1, (*p2)[4], (*p3)[3][4];

	p1 = &a[0][0][0];  //二者等价,下同
	p1 = a[0][0];

	p2 = &a[0][0];
	p2 = a[0];

	p3 = &a[0];
	p3 = a;
	//指针指向的类型一定要正确,如p2为int[4]类型,
	//则可以把数组的a[2][3]中的任意地址赋值给它
	//但是必须有[][]
	return 0;
}

6. 指向指针的指针

声明格式:
数据类型 **指针变量名;

注:有**的指针变量,称为二级指针,同理,有***的指针变量称为三级指针,依次类推.

代码演示:

#include<stdio.h>

int main()
{
	char *str[3] = { "one","two","three" };
	char **p = str;
	for (int i = 0; i < 3; i++)
	{
		printf("%s\n",*(p + i)); 
	}
	/*
	运行结果:
			one
			two
			three
	*/
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值