深入理解指针(4)

接下来还是讲数组和和指针的关系: 

1.一维数组传参的本质

一维数组传参的本质是指在函数调用时,将数组的首元素的地址作为参数传递给函数。在C语言和许多其他编程语言中,数组名本身就是一个指向数组第一个元素的指针。当你将数组名作为参数传递给函数时,实际上是传递了一个指向数组首元素的指针
 
这样做的好处是,函数内部可以通过这个指针直接访问和修改数组元素,而不需要复制整个数组,这样可以节省内存和时间。同时,由于传递的是指针,函数内部对数组的修改会影响到原始数组。

如果不能理解,现在举个例子

我们发现在函数内部没有正确获得数组的元素个数

这时就要考虑数组传参的本质:将数组的首地址作为参数传递给函数。

这里的test(arr)中的arr就已经是&arr[0]的的地址了,所以传上去的不是arr而是&arr[0]所以sizeof()括号里面的就是&arr[0]。(如果不明白sizeof(arr)的大小的看一看

 2.冒泡排序

那么接下来我们要利用指针和数组的知识来解决问题

首先我们要知道冒泡排序是什么?其实就是两个数字,两个数字之间比较大小,然后进行排序。

有这样一串数字10,9,8,7,6,5,4,3,2,1让你排序拍成正序。

下面我会给一串代码会在代码中注释。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void bubble_sort(int arr[], int sz)
{
	int i = 0;
//这里为什么两个循环?
// 第一个循环是把10,9,8..移到最右边的大循环来表明几趟的趟数
//而第二个循环是一个大循环(就是一趟)中两两相交换的小循环
	for (i = 0; i < sz-1; i++)
//sz-1是把10移到右边是一趟9又是一趟一共9趟sz-1
	{
		int j = 0;
//sz-1-i是第一趟你把10移到右边要9次而第二趟把9移到10旁边就需要8次这是依次减少的。
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = tmp;
			}
		}
	}
}
int main()
{
	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(&arr[0]);//这里为什么没有在
	//bubble函数中求sz的值?因为arr这个数组名传上去还是首元素的地址,sz求出来还是1。
	bubble_sort(arr, sz);
	int i = 0;
	for (i = 0; i < sz; i++)//这里的for循环是为了把排好序的arr打印出来
	{
		printf("%d\n", arr[i]);
	}

	return 0;
}

就是这样啦。

3.二级指针

首先要先了解什么是二级指针?

指针变量也是一个变量,是变量就会有地址,那指针变量存放到哪里?对啦,存放到二级指针里面。

比如:

int a = 0;

int *pa = &a;

int **ppa = &pa;

对于二级指针有以下运算:

*ppa通过对ppa中的地址进行解引用,找到的是pa,*ppa其实访问的就是pa,而pa就是&a。

所以*ppa = &a所以**ppa = a;还是很神奇的。

4.指针数组

什么是指针数组呢?我们可以先类比一下整型数组,字符数组。

整型数组就是存放整型的数组  int arr [5]字符数组就是存放字符的数组char arr [5],那么指针数组就是存放指针的数组   int *arr [5]表示数组中每个元素都是int *类型的。

指针数组中的每个元素是地址,又可以指向一块区域

那指针数组有什么用呢?不要着急下面就是

5.指针数组模拟二维数组

当我们了解了指针数组之后我们就可以开始练习一下:用指针数组模拟二维数组

首先要知道指针数组每个元素都是地址,而二维数组又是多个一维数组的组成。知道这些我们就可以让指针数组包含多个数组的地址,知道这些我们可以来实践一下:

#include <stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
//数组名是数组首元素的地址,类型是int*的,就可以存放在parr数组中
	int* parr[3] = { arr1, arr2, arr3 };
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%d ", parr[i][j]);
		}
		
		 printf("\n");
		
	}
	return 0;
	
}

就像这样,我们可以把多个数组通过地址的形式存放到指针数组中,在通过访问地址的方法,经过两重循环就可以根据地址打印出多个数字这样就形成了指针数组模拟二维数组

OK了兄弟们指针与数组情情爱爱大体已经结束,下面要探讨不同类型的指针变量,加油!!

6.字符指针变量

在指针类型中我们知道有一种指针类型为字符指针char*,我们可以这样使用它:

int main()
{
 char ch = 'w';
 char *pc = &ch;
 *pc = 'w';
 return 0;
}

而现在我们要拓展一下,还有一种写法是字符指针指向字符串:

int main()
{
 char* p = "hello world.";
 printf("%s\n", p);
 return 0;
}

这时候我们要思考一下pstr这个指针变量里放的是什么?

一开始看的时候我们会认为是把整个整个字符串存到了指针变量里面,但是其实并不是这样的。

你可以把这个用双引号包含的字符串理解为一个字符数组,这样写其实就是把hello world.首字符的地址存放到了指针变量中了。

这里补充一点:打印字符串的时候这里用%s打印字符串只需要提供一个地址就可以。

也可以这样理解:char arr[] = "hello world";

                            char *p = arr;

那么这个arr数组名中不就是首元素的地址嘛。虽然说可以想象成这样的但是这两种写法还是有差异的。

因为这个代码p指向的arr是数组,数组是可以改变的,但是常量字符串是不可以的,char* p = "hello world.";中的hello world就是常量字符串他是不可以改变的,就像下面这样。

那接下来在看个题目:

#include <stdio.h>
int main()
{
 char str1[] = "hello bit.";
 char str2[] = "hello bit.";
 const char *str3 = "hello bit.";
 const char *str4 = "hello bit.";
 if(str1 ==str2)
 printf("str1 and str2 are same\n");
 else
 printf("str1 and str2 are not same\n");
 
 if(str3 ==str4)
 printf("str3 and str4 are same\n");
 else
 printf("str3 and str4 are not same\n");
 
 return 0;
}

 答案是这样的:

那怎么去理解呢? 这里可以这样去解释:

对于  str1  和  str2  :
 
  str1  和  str2  是两个字符数组,它们在栈上分配空间。当创建这两个数组时,它们分别在栈上分配了足够的空间来存储字符串  "hello bit."  ,包括结尾的空字符  \0  。尽管字符串的内容相同,但每个数组在内存中都有不同的地址。
 
对于  str3  和  str4  的理解是这样的:

const char *str3 = "hello bit.";
const char *str4 = "hello bit.";

 当我写下这串代码,这两行代码实际上是告诉编译器:“我想把一个指向某个字符的指针赋值给我定义的两个变量  str3  和  str4  ,而那个字符就是字符串  "hello bit."  ”。
 
但是这里有一个细节:字符串  "hello bit."  并不是被存储在程序的运行时内存里的(也就是栈或者堆)。相反,这个字符串实际上被存储在了一个特殊的区域,这个区域在程序被编译的时候就已经准备好了,而且这个区域是只读的,因为字符串一旦被放在那儿,它就不会变了。

所以str3和str4 are same.


  7.数组指针变量

7.1数组指针是什么

在上面我们学了指针数组,而现在我们要学数组指针,显而易见数组指针是指针,那么数组指针变量是指针变量。

首先先判断一下,这两串分别是什么。

int *p1[10];
int (*p2)[10];

第一个母庸置疑就是指针数组,那么第二个就是数组指针。那现在拿出来单独看一看。

int (*p)[10];

这里我们可以看到:p先和*结合,说明p是一个指针变量,然后指针指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。

这里要先注意一下[]的优先级高于*所以必须加上()来保证p和*先结合。

7.2数组指针变量怎么初始化

数组变量是用来存数组地址的,那怎么获得数组地址呢?就是我们之前学的&数组名

&arr取出的是数组的地址,数组指针变量就是存放数组地址的,所以数组指针变量中就是存放的arr的地址

就像这样:

我们调试也能看到&arr和p的类型是完全一致的并且的地址和值都是一样的。这就是数组指针的初始化。

这里我找来了数组指针类型的解析图:

第一张是数组指针第二张是指针数组可以对比一下看不同。 

在int (*p)[10] = &arr;在这个里面,p的类型是什么呢?当然就是把p去掉,int (*)[10] = &arr;这个就是p的类型

我们之前有过这样一个题目,就是算他们差的字节数,那么好到现在我们知道本质了。

        int arr [10] = {0};

第一个为什么差四个字节,因为数组名就是首元素的地址,他的类型是int *,+1就是跳过一个整型4个字节第二个也是这样。

但是第三个为什么是差40个字节呢?因为&arr他的类型是int (*)[10],像这种类型的指针+1是要跳过一个数组的就是40个字节。所以是40。        o k了。

总结:

这些就是今天的总结与归纳,还是那句话:还请兄弟们多多提问题,我才会更加进步,写的更加严谨。

 

 

 

 


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值