C语言----指针(G1)1. 字符指针2. 指针数组3.数组指针4. 数组传参和指针传参5. 函数指针6. 函数指针数组7. 指向函数指针数组的指针8. 回调函数

C语言----指针(G1)1. 字符指针2. 指针数组3.数组指针4. 数组传参和指针传参5. 函数指针6. 函数指针数组7. 指向函数指针数组的指针8. 回调函数,第1张

回顾:

指针的主题,我们在初级阶段的《指针》章节已经接触过了,我们知道了指针的概念:

1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。


2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。


3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用 *** 作的时候的权限。


4. 指针的运算。




1,字符指针

一般最常见的运用方式:


int main()
{
	char a = 'w';
	char* p = &a;
	*p = 'q';
	printf("%c", a);

	return 0;
}

还有一种使用方式如下:


int main()
{
    const char* p = "hello";//这里就是是把一个字符串的地址放到p指针变量里
    //*p = 'a';//注意这里不能修改字符串里面的值。


因为他是个常量字符串,不能修改 //即使不加const 也不能修改,常量字符串不能改。


建议加const。


//有些编译器不加const会报警告 printf("%c\n", *p);//打印h printf("%s\n", p);//打印hello //p这里相当于数组名 return 0; }

这里的hello是放在内存里的常量区的是只能读但是不能修改的,建议加const 修饰一下,
有些编译器不加const会报警告的

跟下面情况很相似:


int main()
{
	char arr[10] = "hello";
	char* p = arr;


	printf("%c\n", *p);//打印h
	printf("%s\n", p);//打印hello
	

	return 0;
}

但是这个里的字符是可以修改的。


因为这里的arr【】是在栈区的变量。


不是常量。


练习:

#include 
int main()
{
    char arr1[] = "hello";
    char arr2[] = "hello";
//这里是内存里面先后创建了两个数组,并用“hello”初始化
//这个时候内存里面会有两个数组存在,所以arr1 != arr2

    const char* p1 = "hello";
    const char* p2 = "hello";
//这个是常量字符串,且都不能被修改,编译器默认内存里面只存了一个
//所以p1 == p2  都是指向同一块空间。


if (arr1 == arr2) printf("arr1 == arr2\n"); else printf("arr1 != arr2\n"); if (p1 == p2) printf("p1 == p2\n"); else printf("p1 != p2\n"); return 0; } 答案为:arr1 != arr2 p1 ==p2


2. 指针数组

前面学过整型数组,存放整型的数组。


字符数组是用来存放字符的数组。


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


int* arr[10];//int* 类型的数组,arr数组名,【10】大小为10.

这个就是存放整型指针的数组。



int main()
{
	int a = 1;
	int b = 1;
	int c = 1;
	int* pa = &a;
	int* pb = &b;
	int* pc = &c;

	int* arr[3] = { &a, &b,&c };
	for (int i = 0; i < 3; i++)
	{
		printf("%d\n", *(arr[i]));//打印结果为111.

	}

//一般不会这样用,只是让大家知道指针数组里面放的是地址。


return 0; }

指针数组会用来定义二维数组。



int main()
{
	int arr1[3] = { 1,2,3 };
	int arr2[3] = { 2,3,4 };
	int arr3[3] = { 3,4,5 };
	int* parr[3] = { arr1, arr2, arr3 };//这个时候 parr就是一个指针数组。


for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { //printf("%d ", *(parr[i] + j));//实际上是这样算的 printf("%d ", parr[i][j]);//但是也可以写成这个形式。


运行结果都对的。


//这里和二维数组访问形式是一样的。


//这里parr[i][j] == *(parr[i] + j) } printf("\n"); } return 0; }

上述是模拟出来的一个二维数组的形式但是实际上二维数组在内存中是连续存放的,但是上面每一行存放的时候不是连续的。


但是访问形式都是一样的,二维数组arr[n]就是第n行的首元素的地址,arr[i][j]就是第i行第j列的元素。


 类比:一位数组 arr[1] ==*(arr+1), 就是这样的原理。



3.数组指针

数组指针 不是数组,而是指针。


整形指针: int * pint; 能够指向整形数据的指针,存放整型变量地址的。


浮点型指针: float * pf; 能够指向浮点型数据的指针,存放字符变量地址的。


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


3,1 举个例子
int* p1[10];//这里p1是数组,int*类型的数组,表示数组里面存放的是int类型的,大小为[10]。


int (*p2)[10];//这里的p2是指针,指向的数组里面放的是int类型的数据,大小为[10]。


上图中的p2 就是数组指针,指向的是一个数组 。


int arr[10];

p2里面放的地址就是arr数组的地址。


所以p2 就是数组的指针。


相当于 *(p)[10] ==arr[10];

int (*p2)[10];

//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。


所以p是一个 指针,指向一个数组,叫数组指针。


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

3,2 &数组名 和 数组名(arr和&arr)
#include
int main()
{

	/*int a = 0;
	int* p = &a;*/


	int arr[10] = { 0 };
	//数组名是首元素地址。


只是第一个元素的地址。


//当&arr 这样才是整个数组的地址。


printf("%p\n", arr);//int*类型 printf("%p\n", arr+1);//+4 printf("%p\n", &arr[0]);//int*类型 printf("%p\n", &arr[0]+1);//+4 printf("%p\n", &arr);//int(*)[10] 类型的。


//又时候可以这样int[10]*表示但是我们在写代码的时候一般不会这样写。


printf("%p\n", &arr+1);//+40 //这种类型是数组指针类型。


int(*p)[10] = &arr;//这个就是数组指针的创建。


//去掉名称就是类型 int (*)[10] return 0; }

上面在打印(arr==&arr[0]) 和&arr的时候我们发现,数值是一样的。


但是,实际上:&arr表示的是数组的地址,而不是数组首元素的地址。


(仔细体会)

练习:学会定义数组指针变量。


char* arr[10];
char* (*p)[10] = &arr;//定义arr(指针数组)的数组指针。


char* (*)[10]类型的 char arr[10]; char (*p)[10] = &arr;//定义数组arr(字符数组)的数组指针。


char (*)[10]类型的 int arr[10]; int (*p)[10] = &arr;//定义数组arr(整型数组)的数组指针。


int (*)[10] 类型的 int* arr[10]; int* (*p)[10] = &arr; //定义数组arr(指针数组)的数组指针。


int* (*)[10] 类型的

数组名该怎么理解呢?

通常情况下,我们说的数组名就是首元素的地址 arr ==&(arr[0])

但是有两个例外:

1,sizeof(数组名),这里的数组名表示整个数组, sizeof计算的是整个数组的大小。


2,&(数组名),取出的是整个数组的地址。


除了这两种情况下,其他时候看到数组名都是首元素的地址==&(arr[0])。


3,3  数组指针的使用

数组指针一般不会用到一维数组上,在一维数上运用的时候就会很别扭。


但是也可也用。


在此前我们学过的访问一维数组方法:

法一
void print(int arr[], int sz)
{

	for (int i = 0; i < sz; i++)
	{
		printf("%d", arr[i]);
	}
}

法二
void print(int* str, int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(str + i));
	}

}

但是我们用数组指针来访问的时候:


void print(int (*p)[10], int sz)
{
	//p这里指向的是整个数组。


p是个指针 指向的是整个数组的地址。


// 对p进行*p的时候实际上我们拿到了整个数组。


// 数组名虽然实际是首元素的地址,但是也代表了整个数组。


//也就是说 *p 相当于数组名, 数组名又是首元素的地址 //这里*p就是&arr[0](首元素地址) for (int i = 0; i < sz; i++) { //这里用首元素加i的形式来逐个访问。


printf("%d ", *(*p + i)); //这里用*p的时候就很别扭 //虽然也可以实现访问但是,这就会显得奇奇怪怪的 } }

很显然这里的用数组指针去访问的时候,就会显得很难理解和 不好表达(很不推荐这样使用), 所以数组指针一般会不运用到一维数组里面去。


在二维数组里面才会使用到数组指针。


举一个在二维数组里面使用的例子:

(普通方法)
void print(int arr[3][5], int r, int c)
{
	for (int i = 0; i < r;i++)
	{
		for (int j = 0; j < c; j++)
		{
			printf("%d", arr[i][j]);
		}
		printf("\n");
	}
}


//二维数组在传参的时候也就是传过来的也是首元素地址
// 二维数组传过来的就是第一行的地址。


//这里的也就是把第一行看作一个一维数组 //写法就是 int (*p)[5];//有些书上叫行指针 void print(int (*p)[5], int r, int c) { for (int i = 0; i < r; i++) { for (int j = 0; j < c; j++) { //p+i是指向第i行的 , *(p+i)相当于拿到第i行, 也相当于第i行的数组名 //数组名也相当于首元素的地址, *(p+i) 就是第i行的第一个元素的地址。


//printf("%d ", * (*(p + i) + j)); //printf("%d ", (* (p + i))[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} }; //在二维数组里面数组名表述首元素的地址。


//其实相当于是第一行的地址,二维数组把第一行看组首元素。


//但是数值上就是第一行第一列的元素的地址。


//写一个函数打印arr数组 print(arr, 3, 5); //注意这里是arr 而不是&arr,取地址arr的时候就是整二维数组的地址了 //应该这样定义了 int (*p)[3][5]; return 0; }

 

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

原文地址: http://outofmemory.cn/langs/567958.html

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

发表评论

登录后才能评论

评论列表(0条)

保存