C语言学习笔记(11)指针进阶

Posted 小倪同学 -_-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言学习笔记(11)指针进阶相关的知识,希望对你有一定的参考价值。

之前我们已经学习了指针的一些基础内容,接下来几章将更加深入的去运用指针。

字符指针

在指针的类型中我们知道有一种指针类型为字符指针char*。

一般使用:

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

还有一种使用方法如下:

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

在这里插入图片描述
代码char* ps = "hello world. ";特别容易让人以为是把字符串hello world放到字符指针ps里了,但是本质是把字符串hello bit. 首字符的地址放到了ps中。

根据上文,容易将字符型指针和字符型数组联系到一起,它们还是有区别的

  1. 字符型指针本质上是地址而字符型数组是变量。
  2. 字符型指针的数值不能改变,字符型数组可以随意改变。

当想改变字符指针时,会报这样错误:
在这里插入图片描述

观察如下代码思考输出结果是什么

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;
}

这里str 3和str4指向的是一个同一个常量字符串。C会把常量字符串存储到单独的一个内存区域,当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同 ,str3和str4不同。

在这里插入图片描述

指针数组

指针数组指的是:一个存放指针的数组(地址)

应用实例

int main()
{
	int a[5] = { 1,2,3,4,5 };
	int b[] = { 2,3,4,5,6 };
	int c[] = { 3,4,5,6,7 };

	int* arr[3] = {a,b,c};
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", *(arr[i] + j));
		}
		printf("\\n");
	}
	return 0;
}

在这里插入图片描述

printf("%d ", *(arr[i] + j));改为printf("%d ",arr[i][j]);会有相同的输出结果

上面代码虽然可以用arr[i][j]输出,但是它和二维数组有很大区别。
指针数组中每个元素指向的地址没有联系,而二维数组中行与行间的地址紧密相连。

如下面代码

int main()
{
	int a[5] = { 1,2,3,4,5 };
	int b[] = { 2,3,4,5,6 };
	int c[] = { 3,4,5,6,7 };

	int* arr[3] = {a,b,c};
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			/*printf("%d ", *(arr[i] + j));*/
			printf("%d ", arr[i][j]);
		}
		printf("\\n");
	}

	int arr2[3][5] = { { 1, 2, 3, 4, 5 }, { 2, 3, 4, 5, 6 }, { 3, 4, 5, 6, 7 } };
	int j=0;
	for (i = 0; i < 3; i++)
    {
	   for (j = 0; j < 5; j++)
	   {
		   printf("%d ", arr[i][j]);
	   }
	   printf("\\n");
    }
	return 0;
}

指针数组中每个元素指向的地址
在这里插入图片描述
二维数组中行与行间的地址
在这里插入图片描述

数组指针

数组指针的定义

数组指针是指针?还是数组?
答案是:指针。

我们已经熟悉:整形指针: int * pint;能够指向整形数据的指针。浮点型指针: float * pf; 能够指向浮点型数据的指针。

那数组指针应该是:能够指向数组的指针。

下面代码哪个是数组指针

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

答案是:int(*p2)[10];
p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。

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

&数组名和数组名

通过前面的学习,我们知道数组名表示的是元素的首地址,那么&数组名又是什么呢?
先看一段代码

int main()
{
	int arr[10] = {0};

	int* p1 = arr;
	int (*p2)[10] = &arr;

	printf("%p\\n", p1);
	printf("%p\\n", p2);

	return 0;
}

输出结果如下
在这里插入图片描述
数组名和&数组名输出的地址是一样的,难道数组名等同于&数组名?
让我们再看如下代码

int main()
{
	int arr[10] = {0};

	int* p1 = arr;
	int (*p2)[10] = &arr;

	printf("%p\\n", p1);
	printf("%p\\n", p1+1);

	printf("%p\\n", p2);
	printf("%p\\n", p2+1);

	return 0;
}

输出结果为
在这里插入图片描述
根据上面的代码我们发现,其实&arr和arr ,虽然值是一样的,但是意义是不一样的。

实际上: &arr表示的是数组的地址,而不是数组首元素的地址。数组的地址+1 ,跳过的是整个数组的大小。

数组名是数组首元素的地址
但是有2个例外:
1.sizeof(数组名) - 数组名表示整个数组,计算的是整个数组大小,单位是字节
2.&数组名 - 数组名表示整个数组,取出的是整个数组的地址

数组指针的使用

数组指针指向的是数组,那数组指针中存放的应该是数组的地址。

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int (*pa)[10] = &arr;//把arr的地址赋值给数组指针变量pa

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

	return 0;
}

上述代码虽然可以达到预期的效果,但是过程较为复杂,一般很少写这样的代码。

一个数组指针的使用

void print2(int(*p)[5], int r, int c)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < r; i++)
	{
		for (j = 0; j < c; j++)
		{
			printf("%d ", *(*(p+i) + j));
		}
		printf("\\n");
	}
}
int main()
{
	int arr[3][5] = { { 1, 2, 3, 4, 5 }, { 2, 3, 4, 5, 6 }, { 3, 4, 5, 6, 7 } };
	print2(arr, 3, 5);
	return 0;
}

数组名arr,表示首元素的地址,但是二维数组的首元素是二维数组的第一行,所以这里传递的arr,其实相当于第一行的地址, 是一维数组的地址,这样就可以数组指针来接收。

学了指针数组和数组指针我们来看看下面代码的意思:

int arr[5];
int *parr1[10];
int(*parr2)[10];
int(*parr3[10])[5];
int arr[5];

整型数组

int *parr1[10];

整型指针数组

int(*parr2)[10];

数组指针,该指针指向一个数组,数组10个元素,每个元素的类型是int

int(*parr3[10])[5];

parr3是一个储存数组指针的数组,该数组能够存放10个数组指针
每个数组指针能够指向一个数组,数组5个元素,每个元素是int型。

数组传参

一维数组传参

#include <stdio.h>
void test(int arr[])
{}
void test(int arr[10])
{}
void test(int *arr)
{}
void test2(int *arr[20])
{}
void test2(int **arr)
{}

int main()
{
	int arr[10] = (0); 
	int *arr2[20] = (0); 
	test(arr);
	test2(arr2);
}

注意:一维数组传参的时候,可以传数组的形式也可以传指针的形式。传数组形式时,接收可以省去数组元素个数。传指针形式时,要注意指针指向元素的类型,元素是什么类型就要用什么类型接收。如上述代码,指针指向的类型是int ,接收就要用int型指针接收。

二维数组传参

void test(int arr[3][5])//ok 
{}
void test(int arr[][])//err
{}
//行可以省略,列不可以,第一个[ ]内容可以不写,第二个[ ]要写
void test(int arr[][5])//ok 
{}
void test(int* arr)//err  用一级指针接收二级指针不可取。
{}
void test(int* arr[5])//err  int* arr[5]是数组指针,一个数组中储存5个指针
{}
void test(int(*arr)[5])//ok 
{}
void test(int** arr)//err  传过来的是一维数组首地址,不能用二级指针接收。
{}
int main()
{
	int arr[3][5] = { 0 };
	test(arr);
}

注意:二维数组传参,接收时行可以省,但是列不能省。二维数组指针传参要注意接收类型。

指针传参

一级指针传参

void print(int* ptr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(ptr + i));
	}
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	//p是一级指针
	print(p, sz);
	return 0;
}

输出结果如下:
在这里插入图片描述

二级指针传参

void test(int** p2)
{}

int main()
{
	int a = 10;
	int* pa = &a;//pa是一级指针
	int** ppa = &pa;//ppa是二级指针
	
	//如何把二级指针进行传参呢?
	test(ppa);//直接传二级指针
	test(&pa);//传一级指针变量的地址

	int* arr[10] = {0};
	test(arr);//传存放一级指针的数组

	return 0;
}

以上是关于C语言学习笔记(11)指针进阶的主要内容,如果未能解决你的问题,请参考以下文章

C语言进阶笔记深入了解进阶指针

C语言进阶笔记深入了解进阶指针

C语言进阶笔记深入了解进阶指针

C语言进阶笔记深入了解进阶指针

C语言进阶学习笔记二指针的进阶(重点必看+代码图解+练习)

C语言进阶学习笔记二指针的进阶(练习篇)