笔记19-1(C语言进阶 指针进阶)

笔记19-1(C语言进阶 指针进阶),第1张

目录

注:

字符指针

例子(求打印结果 char --- char*)

指针数组

数组指针

知识点

数组指针的使用

应用情景(打印二维数组的每一个元素)

小结

数组参数、指数参数

一维数组传参

二维数组传参

一级指针传参

二级指针传参

总结

指针

数组


注:

 本笔记参考B站up鹏哥C语言的视频


  1. 指针就是给变量,用来存放地址,地址唯一标识一块内存空间。
  2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
  3. 指针是有类型的,指针的类型决定了:①指针的±整数的步长,②指针解引用 *** 作时的权限。
字符指针

有一种指针的类型为字符指针 char*

一般使用:

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

但是,字符指针不仅可以指向字符,还可以指向字符串:

int main()
{
	char* ps = "Hello World";
	printf("%c\n", *ps);
	return 0;
}

这种写法本质上是把这个字符串首字符'H'的地址存入ps内,打印结果是:H。(ps:这种写法区别于数组,一个放入首地址,另一个放入字符串;而且ps是个变量,arr是个数组。不过,访问形式可以一样,都是访问字符串的首地址。)

例子(求打印结果 char --- char*)

题目来自《剑指Offer》

int main()
{
	char str1[] = "Hello World.";
	char str2[] = "Hello World.";
	char* str3[] = "Hello World.";
	char* str4[] = "Hello World.";

	if (str1 == str2)
		printf("str1 and str2 are same\n");
	else
		printf("str1 and str2 are no same\n");

	if (str3 == str4)
		printf("str3 and str4 are same\n");
	else
		printf("str3 and str4 are no same\n");

	return 0;
}

打印结果:

str1 and str2 are no same
str3 and str4 are same

解析:

对于 str1 和 str2:

对于 str3 和 str4:

注意:str3 和 str4 内存放的"Hello World"是常量字符串,字符串内容无法更改。

譬如:

int main()
{
	char* ps = "Hello World";
	*ps = 'w';
	return 0;
}

开始调试,发生:

--- 代码挂掉了。

结论:相同的字符串在内存中只存一份。

指针数组
  • int* arr[10];      //整型指针的数组
  • char *arr2[4];  //一级字符指针的数组
  • char **arr3[5];//二级字符指针的数组

指针数组是一个存放指针的数组。如:int* arr[3] --- 这就是一个存放整型指针的数组。

一种不推荐的写法:

int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	int* arr[3] = { &a,&b,&c };
	int i = 0;
	for ( i = 0; i < 3; i++)
	{
		printf("%d ", *(arr[i]));
	}
	return 0;
}

---

推荐的写法:

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

	return 0;
}

这时候,数组arr的三个元素都是int*类型的。

arr
int*→(a)1  2  3  4  5
int*→(b)2  3  4  5  6
int*→(c)3  4  5  6  7

通过这种方式,可以访问每个数组的每一个元素。那么要如何访问呢?

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]);
		}
	}
	return 0;
}

打印结果:1 2 3 4 5 2 3 4 5 6 3 4 5 6 7

补充:[printf("%d ", *(arr[i] + j))] 在这里有另一种写法(模拟二维数组)[printf("%d ", arr[i] [j])],结果完全一致。

数组指针
  • 整型指针 - 指向整型的指针 - int* p
  • 字符指针 - 指向字符的指针 - char* p

数组指针 - 指向数组的指针

int main()
{
	int arr[10] = { 1,2,3,4,5 };

	int(*parr)[10] = &arr;

	return 0;
}

此时的parr就是一个数组指针 - 其中存放的是数组的地址。

理解:[int (*) [10]]表示的是指针的类型,通过(*xxx)表示这是指针,[10]表示这是个数组指针。

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

以此类推:

double* d[5];
double* (*pd)[5] = &d;

注意:指针要和指向数组的类型和大小相一致。

知识点
  • arr和&arr指向的地址一模一样。

但是arr和&arr的类型不一样:

  • arr - 数组名是数组首元素的地址 - arr[0]的地址
  • ps:两个例外
  1. sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小(单位是字节)。
  2. &数组名 - 数组名表示整个数组,取出的是整个数组的地址(如下)。
  • &arr - 取出的是数组的地址
  • 如果要存取二者的地址,会发现:
    int* p1 = arr;
    int (*p2)[10] = &arr;
  •  其中p1是个整型指针,加1跳过1个字节;而p2是一个数组指针,加1跳过1个数组:

p1 (前进4个字节)→ p1 + 1

p2 (前进40个字节)→ p2 + 2

结论:&arr 表示数组的地址,而不是数组首元素的地址。数组的地址+1,跳过整个数组的大小,所以 &arr + 1 相对于 &arr 的差值是40。

数组指针的使用

数组指针一般在二维数组内使用。如果在一维数组内使用:

譬如需要打印数组内每一个元素

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

	int(*pa)[10] = &arr;//数组指针
	int i = 0;
	for (size_t i = 0; i < 10; i++)
	{
		printf("%d ",*((*pa) + i));//*pa相当于arr
	}
	return 0;
}

这种写法别扭而且麻烦。

应用情景(打印二维数组的每一个元素)

注意:

        二维数组的数组名表示元素的地址。而二维数组的首元素是:第一行(相当于第一行是一个一维数组)。

传统写法

void print1(
	int arr[3][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 ", arr[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} };
	print1(arr, 3, 5);
	return 0;
}

---

指针写法

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));//p + i 指向了某一行
		}
		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);
}

分析:

  1. p是一个数组指针,指向了二维数组的某一行 
  2. *(p + i) 就相当于某一行第一个元素的地址

 

小结
int arr[5];整型数组
int* parr1[10];整型指针的数组
int(*parr2)[10];数组指针,该指针能够指向一个数组,这个数组有10个元素,每个元素的类型是 int。
int(*parr3[10])[5];parr3是一个存放数组指针的数组,该数组能够存放10个数组指针。每个数组指针能够指向一个数组,这些数组是5个元素,每个元素是int类型。

数组参数、指数参数

在写代码时难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计?

一维数组传参
#include
void test(int arr[])//Ok?
{}
void test(int arr[10])//Ok?
{}
void test(int *arr)//Ok?
{}
void test2(int *arr[20])//Ok?
{}
void test(int **arr)//Ok?
{}
int main()
{
	int arr[10] = { 0 };
	int* arr2[20] = { 0 };
	test(arr);
	test(arr2);
}

解答(顺序从上到下):

  • [void test(int arr[])] 可行;
  • [void test(int arr[10])] 可行(不过[10]可以省略);
  • [void test(int *arr)] 可行,分析:传输的是整型数组的首元素的地址,用整型指针接收。

---

  • [void test2(int *arr[20])] 可行(不过[20]可以省略),分析:arr2是一个存放int*(整型指针)的数组,函数接收时也是使用整型指针的数组进行接收,故可行。
  • [void test(int **arr)] 可行,此时arr2存放int*:

函数使用二级指针接收,故可行。

二维数组传参
#include
void test(int arr[3][5])//Ok?
{}
void test(int arr[][])//Ok?
{}
void test(int arr[][5])//Ok?
{}

void test(int* arr)//Ok?
{}
void test(int* arr[5])//Ok?
{}
void test(int (*arr)[5])//Ok?
{}
void test(int **arr)//Ok?
{}
int main()
{
	int arr[3][5] = { 0 };
	test(arr);
}

ps:二维数组传参,函数形参的设计只能省略第一个[]的数字。因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素,这样才方便计算。

解答(顺序从上到下):

  • [void test(int arr[3][5])] 可行;
  • [void test(int arr[][])] 不可行,分析:二维数组传参,最多省略行;
  • [void test(int arr[][5])] 可行。

---

  • [void test(int* arr)] 不可行,分析:二维数组传参,数组名指的是第一行(即一个一维数组)的地址,第一行的地址是一个数组的地址,无法用一个整型指针接收;
  • [void test(int* arr[5])] 不可行,分析:该函数参数部分是一个数组,同上;
  • [void test(int (*arr)[5])] 可行,分析:(*arr)表示一个指针,这个指针可以指向一个5个元素的一维数组;
  • [void test(int **arr)] 不可行,分析:(int **arr)是一个二级指针,但是传输过来的是第一行一维数组的地址,匹配不上。

一级指针传参
#include
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;//p是一级指针
	int sz = sizeof(arr) / sizeof(arr[0]);

	print(p, sz);

	return 0;
}

这就是一种一级指针传参。

思考:
|||   当一个函数的参数部分为一级指针时,函数能接收什么参数?

比如:

void test(
	char* p
)
{}

1.这样的一个函数,可以接收变量的地址

char ch = 'w';
test(&ch);

2.或者接收一级指针

char ch = 'w';
char* p1 = &ch;
test(p1);

二级指针传参
void test(
	int** p2
)
{
	**p2 = 20;
}
int main()
{
	int a = 10;
	int* pa = &a;//pa是一级指针
	int** ppa = &pa;//ppa是二级指针
	
	//把二级指针进行传参
	test(ppa);
	printf("%d\n", a);

	return 0;
}

这是一种二级指针。

思考:
|||   当函数的参数是二级指针时,函数能接收什么参数?

1.接收二级指针

test(ppa);

2.接收一级指针的地址

test(&pa);

3.接收存放一级指针的数组

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

总结 指针

一级指针

  • int* p; - 整形指针 - 指向整型的指针
  • char* pc; - 字符指针 - 指向字符的指针
  • void* pv; - 无类型的指针
  • ……

二级指针

  • char**p;
  • int**p;
  • ……

数组指针:指向数组的指针

  • int(*p)[4];
  • ……

数组

一维数组、二维数组

指针数组 - 存放指针的数组

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/706935.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-24
下一篇 2022-04-24

发表评论

登录后才能评论

评论列表(0条)

保存