C语言指针进阶(一)

目录

一、字符指针

二、指针数组

三、数组指针

 四、指针笔试题

一、字符指针

1. 指向单个字符:

int main()
{
	char a = 'a';
	char* pa = &a;
	printf("%c", *pa);
	return 0;
}

2.指向字符串:此时p指向字符数组arr首元素的地址

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

注意:如果要打印p指向的字符数组的所有元素,在printf传参里传的是p而不是*p。

下面我们来观察这样一段代码:

int main()
{
	char arr[] = "abcdef";
	char* p = arr;
	printf("p:%p\n", p);
	printf("&p:%p\n", &p);
	printf("arr:%p\n", arr);
	printf("&arr:%p\n", &arr);
    printf("&arr[0]:%p\n",&arr[0]);
	return 0;
}

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

从结果上来看,p和&p看起来是不一样的,而p、arr、&arr[0]和&arr看起来是一样的。

针对它们的关系,画张图好理解:

C语言指针进阶(一)_第2张图片

①p是一级指针,本质是地址,p自己本身有一个地址(因此可以有二级指针指向它),p内部存着的arr的地址。

②从&arr[0]、&arr和arr的地址打印来看,arr和&arr都是arr[0]的地址。

虽然p、arr、&arr地址的值是一样的,它们之间有什么关系呢?

 我们用下面这段代码解释:

int main()
{
	char arr[] = "abcdef";
	char* p = arr;
	printf("p+1:%p\n", p + 1);
	printf("arr+1:%p\n", arr + 1);
	printf("&arr+1:%p\n", &arr + 1);
	return 0;
}

C语言指针进阶(一)_第3张图片

从打印结果来看,p和arr跳过1个字节后的地址仍然相等,而&arr似乎跳过不止一个字节,将十六进制换算为十进制,B代表12,和前面的差了7个字节,正好是这个字符数组元素的个数。

C语言指针进阶(一)_第4张图片

究其原因

①p和arr是一样的,都代表arr数组首元素的地址,

②&arr代表的是整个arr数组的地址,但是与其首元素地址是一样的,

③p和arr的类型都是char*类型的指针,&arr的类型是char(*)[7]的数组指针(后面会讲)。

二、指针数组

顾名思义,就是存放指针的数组。

那指针数组到底有什么用呢?我们来看下面这段代码:

int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 2,3,4,5,6 };
	int arr3[5] = { 3,4,5,6,7 };
	int* arr[3] = { arr1,arr2,arr3 };
	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;
}

C语言指针进阶(一)_第5张图片

 在字符指针的讲解中,我们知道数组名就是数组首元素的地址,我们将arr1、arr2、arr3数组的首元素地址放进arr的指针数组中,将arr模拟成了一个二维数组进行遍历打印。

 其中比较难理解的是这句代码:

printf("%d ", *(*(arr + i) + j));

我们印象中的二维数组应该是这样连续存储的:

C语言指针进阶(一)_第6张图片

但其实在内存中arr是这样存储的:

C语言指针进阶(一)_第7张图片

我们先用arr+i解引用得到arr1、arr2和arr3的首元素地址,再用它加上j变量后解印用得到每个元素的值,也就是第一次解印用得到地址,第二次解印用得到值

其实可以将*(*(arr + i) + j)直接写成arr[i][j],结果是一样的。

三、数组指针

区别数组指针和指针数组:一个是指针,一个是数组,一个是指向数组的指针,一个是存放指针的数组。

指针数组:

int arr1[5] = { 1,2,3,4,5 };
int arr2[5] = { 2,3,4,5,6 };
int arr3[5] = { 3,4,5,6,7 };
int* arr[3] = { arr1,arr2,arr3 };

arr数组有3个元素,分别是三个数组名,数组名表示数组首元素的地址,类型是int*。

数组指针:

int arr[5]={1,2,3,4,5};
int (*parr)[5]=&arr;

parr是指向arr数组的指针,类型是int(*)[5]。 

 问:为什么不能写成这样呢?

int(*parr)[5]=arr;

虽然编译过得去,但是报了一个警告:

C语言指针进阶(一)_第8张图片

原因是arr是数组首元素的地址,类型是int *,而&arr的类型是int(*)[5]。

这就解释了上面为什么arr+1只是跳过1个数组元素,而&arr+1跳过了整个数组的元素。

数组指针的应用:二维数组传参

void print(int(*parr)[5], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf("%d ", *(*(parr+i)+j));
		}
		printf("\n");
	}
}
int main()
{
	int arr[2][5] = {{ 1,2,3,4,5 }, { 2,3,4,5,6 }};
	print(arr,2,5);
	return 0;
}

C语言指针进阶(一)_第9张图片

 四、指针笔试题

int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;
	printf("%s\n", **++cpp);
	printf("%s\n", *-- * ++cpp + 3);
	printf("%s\n", *cpp[-2] + 3);
	printf("%s\n", cpp[-1][-1] + 1);
	return 0;
}

c、cp、cpp之间的关系画图表示: 

C语言指针进阶(一)_第10张图片

**++cpp:

C语言指针进阶(一)_第11张图片

 *--*++cpp+3:

C语言指针进阶(一)_第12张图片

*cpp[-2]+3,即*(*(cpp-2))+3:

C语言指针进阶(一)_第13张图片

cpp[-1][-1]+1,即*(*(cpp-1)-1)+1:

C语言指针进阶(一)_第14张图片

C语言指针进阶(一)_第15张图片

你可能感兴趣的:(c语言,数据结构)