C语言之指针进阶篇(1)

目录​​​​​​​

引言

字符指针

指针数组

数组指针

数组指针的定义

&数组名vs数组名

数组指针的使用 

一维数组使用 

二维数组使用

一维数组传参

二维数组传参

总结 

数组参数

一维数组传参

二维数组传参

指针参数

一级指针传参

二级指针传参


引言

今天学习指针的进阶版,那还是回顾一下我们前面学习过的指针知识。

  • 内存单元是有编号,编号 == 地址 == 指针
  • 指针变量就是个变量,用来存放地址,地址唯一标识一块内存空间。
  • 指针(地址/指针)的大小是固定的4/8个字节(32位平台/64位平台)。
  • 指针变量的大小是4/8个字节。
  • 指针有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。
  • 指针运算。

字符指针

%c   打印字符
%s   打印字符串

在指针初阶中我们学习过指针的类型,我们知道有一种指针类型为字符指针char*

#include
int main()
{
	char c = 'x';
	char ch = "abcdef";
	char* pc = &ch;//存放字符串全部的地址
	char* p = &c;//存放字符的地址
	printf("%c", *p);
	return 0;
}

 除了上面的传统使用方法,我们还有另外一种使用方法:

#include
int main()
{
	char* pc = "abcdef";
	printf("%c\n", "abcdef"[0]);
	printf("%s\n", pc);
	printf("%c", *pc);
	return 0;
}

C语言之指针进阶篇(1)_第1张图片

  •  char * pc = "abcdef"本质是把字符串"abcdef"首地址放到了字符指针变量pc中。
  • "abcdef"是表达式,值就是a的地址。
  • 一个常量字符串的首地址+%s打印=字符串所有内容(根据首地址遍历字符串所有内容)
  • "abcdef"也可以理解为数组名 == 相当于 数组的首地址被存放。
  • *pc是只能访问一个字符。

C语言之指针进阶篇(1)_第2张图片

但是上面的使用存在一定的风险,什么风险呢?

"abcdef"是常量字符串表达式,"常量"是不能改变的。

 常量字符串是不能被修改的。

那怎么解决呢?

 加上const即可。

#include
int main()
{
	const char* pc = "abcdef";//不会被修改了
	printf("%s\n", pc);
	*pc = "g";
	return 0;
}

练习——剑指offer 

#include 
int main()
{
	char str1[] = "tangsiqi";
	char str2[] = "tangsiqi";
	char* str3 = "tangsiqi";
	char* str4 = "tangsiqi";
	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");

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

	return 0;
}

C语言之指针进阶篇(1)_第3张图片

C语言之指针进阶篇(1)_第4张图片

  • str1和str2是在内存中开辟了不同的空间去存放字符串。
  • str3和str4是内存中不同的空间指向了同一个字符串的首地址。 

指针数组

 在前面我们学习过指针数组以及它的使用(模拟二维数组的作用)。

指针数组是指针还是数组呢?

是数组啦。是存放指针的数组。

字符数组——存放字符的数组

整型数组——存放整型的数组

指针数组——存放指针(地址)的数组,存放在数组中的元素都是指针类型的。

//例如:char * arr[5];   int * ch[6];

//arr[5]表明是数组

模拟二维数组

//使用指针数组,模拟一个二维数组
#include
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
     //指针数组
	int* arr[] = { arr1,arr2,arr3 };
	             //0   1     2
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)//找到首元素
	{
		for (j = 0; j < 5; j++)//遍历每一个数组的元素
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

C语言之指针进阶篇(1)_第5张图片

C语言之指针进阶篇(1)_第6张图片

除了上面整型指针数组,还有字符指针数组。 

#include
int main()
{
	char* arr[5] = { "tangsiqi","xueguodi","haha","hehe","xiaoxiao" };
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%s\n", arr[i]);
	}
	return 0;
}

C语言之指针进阶篇(1)_第7张图片

 C语言之指针进阶篇(1)_第8张图片

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

数组指针

数组指针是指针?还是数组?

指针。

指针数组——是数组,是存放指针的数组。

数组指针——是指针,指向数组的指针。

字符指针——指向字符的指针。

整型指针——指向整型的指针。

浮点型的指针——指向浮点型的指针。

int a=10;
int * pa=&a;

char b='x';
char * pb=&b;

 那么数组指针怎么写呢?

数组指针的定义

定义:数组指针是指针,指向数组的指针。 //type_t (* p) [const_n];

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

 上面那个代码是指针数组,那个是数组指针?p1,p2分别代表什么?

int arr[10];
int *p1[10];//指针数组
int (*p2)[10];//数组指针
//解释:
//p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。
//所以p是一个指针,指向一个数组,叫数组指针。
//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。

&数组名vs数组名

//数组名 vs &数组名
// &数组名是整个数组的地址
//数组名是数组首元素的地址
//除了两个例外:
//1.sizeof(数组名),这里的数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节
//2.&数组名,这里的数组名表示整个数组名,取出的是数组的地址。
#include
int main()
{
	int arr[10];
	printf("%d\n", arr);//int*
	printf("%d\n", arr + 1);
	printf("%d\n", &arr);//这个是什么类型?
	printf("%d\n", &arr + 1);
    //指针类型决定了指针+1,到底+几个字节
	return 0;
}

C语言之指针进阶篇(1)_第9张图片

根据上面代码运行结果我们发现,虽然&arr和arr值是一样的,但是意义应该是不一样的。

实际上:&arr表示的是数组的地址;arr表示的是数组首元素的地址。

数组的地址+1,跳过的整个数组的大小,所以&arr+1相对于&arr的差值是40

int*p[10]=arr    +1跳过4个字节

int(*p)[10]=&arr     +1跳过10个元素每个元素为int的数组也就是 40个字节

那应该怎样存放呢?

#include
int main()
{
	int arr[10];
	int(*p)[10] = &arr;
	//p是用来存放整个数组的地址,p就是数组指针
	char* arr2[5];
	char* (*pc)[5] = &arr2;
	return 0;
}

 int (*p) [10];

//*p表明是指针

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

特别提醒: 以下写法均是错误的❌

int arr[10];
❌int (*p)[]=&arr;//不能省略
❌int [10]*p=&arr;

数组指针的使用 

一维数组使用 

一维数组的arr数组名就是首元素地址_用指针接收
int *p =arr;
一维数组的&arr取地址数组名是整个数组的地址_用数组指针接收
int (*p)[10]=&arr;
//一维数组使用
#include
int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int(*p)[5] = &arr;
//值是放到p里面,不是*p,值是&arr
//&arr是整个数组的地址
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", (*p)[i]);//p[i]❌
                    //(*&arr) ==arr——*和&可以抵消
	}
}
//一维数组传参
#include
void print(int(*p)[5])
{
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d", (*p)[i]);
	}
}
int main()
{
	int arr[5] = { 1,2,3,4,5 };
	print(&arr);
	return 0;
}

 把数组arr的地址赋值给数组指针变量p,但是我们一般很少这样写代码。 

二维数组使用

二维数组的arr是首元素地址,就是第一行数组(一维数组)整个的地址
用数组指针来接受
int(*p)[][5]=arr;
//此时不需要取地址
//二维数组
#include
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	int(*p)[5] = arr;//二维数组数组名 ==首元素 == 二维数组的第一行
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d", p[i][j]);
		}
		printf("\n");
	}
	return 0;
}
//二维数组
#include
void print(int(*p)[5], int row, int col)//本质就是二维数组的首元素地址==第一行地址
//void print(int arr[3][5],int row,int col)
{
	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");
	}
}
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	print(arr, 3, 5);
	return 0;
}

 上面我们指针数组涉及到一维数组传参和二维数组传参的点我们来学习下。(传arr首元素地址)

一维数组传参

//数组形式
#include
void print(int arr[])
//void peintf(int arr[10])也可
{
	int i = 0;
	for (i = 0; i < 10;i++)
	{
		printf("%d ", arr[i]);//*(arr+i)
	}
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	print(arr);
	return 0;
}
//指针形式
#include
void print(int* arr)
{
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	print(arr);
	return 0;
}

C语言之指针进阶篇(1)_第10张图片

二维数组传参

//二维数组传参
//数组形式
#include
void print(int arr[3][5],int row,int col)
{
	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");
	}
}
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	print(arr, 3, 5);
	return 0;
}

//指针形式
#include
void print(int (*arr)[3][5], int row, int col)//✔
//void print(int (*arr)[5], int row, int col)✔
//void print(int (*arr)[][5], int row, int col)❌——四不像
//void print(int arr[][5], int row, int col)✔
{
	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");
	}
}
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	print(arr, 3, 5);
//&arr——首元素地址的地址
	return 0;
}

C语言之指针进阶篇(1)_第11张图片

总结 

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

C语言之指针进阶篇(1)_第12张图片

  •  arr是一个能够存放5个整型数据的数组
  • parr1是一个指针数组,数组10个元素,每个元素的类型是int*
  • parr2是一个数组指针,指向数组,指向的数组有10个元素,每个元素的类型是int
  • parr3是一个数组,数组有10个元素,存放10个元素都是数组指针,数组指针是指针,    指向的数组有5个元素,每个元素是int类型。 

C语言之指针进阶篇(1)_第13张图片

首先确定是指针还是数组——看符号的优先级,变量先与那个符号结合

把这部分除去,看剩下的就是变量的类型了。

数组参数

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

一维数组传参

#include 
void test(int arr[])//ok?✔
//不会创建一个新的数组,本质是指针所有[]里有无都可
{}
void test(int arr[10])//ok?✔
{}
void test(int *arr)//ok?✔
//本质是指针——首元素地址
{}
void test2(int *arr[20])//ok?✔
{}
void test2(int **arr)//ok?✔
//接收的是首元素地址的地址
{}
int main()
{
int arr[10] = {0};//数组
int *arr2[20] = {0};//指针数组
test(arr);//首元素地址
test2(arr2);//首元素_是指针的地址 
}

数组传参,参数是可以写成数组形式的,可以写成指针形式。

数组传参本质是:传递了数组首元素的地址。 

二维数组传参

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);//arr首元素地址,第一行(一维数组)整个数组的地址
}

 二维数组传参行可以省略,列不能省略。

指针参数

一级指针传参

#include 
void print(int *p, int sz)//形式参数写成一级指针就可以了
{
int i = 0;
for(i=0; i

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

#include
void test(int *p)
{
//
}

int main()
{
  int a=10;
  int * pa=&a;
  int arr[5];
//传参
  test(&a);//传整型变量的地址
  test(pa);//传整型的指针
  test(arr);//传整型一维数组的数组名
  return 0;
}

二级指针传参

#include 
void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int*p = &n;
int **pp = &p;
test(pp);
test(&p);//传指针的地址_地址的地址
return 0;
}

思考:当函数的参数为二级指针的时候,可以接受什么参数? 

#include
void test(int **p)
{
//
}
int main()
{
   int n=10;
   int*p=&n;
   int **pp=&p;
   int* arr[5];//指针数组
//传参
   test(&p);
   test(pp);
   test(arr);
   return 0;
}

关于数组和指针以及它们传参相信大家有了深刻的理解,函数指针我们会在下篇博文讲到。

✔✔✔✔✔最后,感谢大家的阅读,若有错误和不足,欢迎大家指正!

代码-------→【gitee:https://gitee.com/TSQXG】

联系-------→【邮箱:[email protected]

你可能感兴趣的:(c语言,开发语言)