【期末不挂科,高分直通车】快要期末考试了?C语言指针还有救不

一、前言
快要期末考试了,C语言还没掌握扎实怎么办?别着急,快来跟着我带你复习重难点和易错点吧,相信会对你有所帮助。

[模块七:指针]

7.1指针变量

易错一:指针变量的初始化和赋值

【案例演示】

int main()
{
    
    
	int* p;
	*p = 2;
	return 0;
}

【分析】
就和创建一个变量没有赋初始值一样,创建的指针没有赋初始地址值,指针指向随机的空间,因此不能使用这块空间存储变量。形象的比喻就是:你随便找了家旅馆,不和老板打招呼就要拎包入住,老板不打死你就怪了。

易错二:指针越界访问

【案例演示】

int main()
{
    
    
	int i = 0;
	int arr[10] = {
    
     0 };
	for (i = 0; i <= 12; i++)
	{
    
    
		arr[i] = 0;
		printf("hehe\n");
	}
	return 0;
}

【分析】
不难发现,上面的程序发生了越界访问。但一个有趣的现象是上面的程序会陷入死循环,甚至没有机会给你报错,为什么呢?(扩展内容,选择性阅读)

  1. i和arr是局部变量,局部变量开辟在栈区上。
  2. 栈区上空间的使用特点是:先使用高地址空间,再使用低地址空间。
  3. 而对于数组而言,其内部元素地址随着下标增长而地址增长

    从上图不难发现,i=12时恰好找到了i的地址,并将i修改为0,程序由此陷入死循环。为什么arr[9]和i之间隔了两个呢,这完全取决于编译器对内存分配的处理。

易混一:指针变量大小

在这里插入图片描述
【答】在32位平台下, 指针的大小都是4个字节;在64位平台下,指针的大小是8字节

  • char指针的大小和int指针的大小相等,都是四个字节,我们也知道char类型的大小为1个字节,int类型的大小是4个字节。那就有一个疑问了,为什么指针的大小相等但是访问内存时读取的字节大小不同呢?原因就在于两种指针的基类型不同,基类型决定了编译器对指向该空间的处理方式,即处理时是以什么数据类型来进行处理的。char类型的就读取一个字节,int类型的就读取四个字节,以此类推。
    [下面打印的结果为11223300]
#include <stdio.h>
int main()
{
    
    
	int a = 0x11223344;
    char *pc = (char*)&a;
    *pc = 0;
    printf("%x\n", a);
    return 0;
}

7.1指针与二维数组

二维数组在内存中的存储形式?

在这里插入图片描述
在这里插入图片描述

  • 所以我们不难发现,所谓的二维数组本质上就是一维数组,他们在内存上连续。只不过为了方便理解和使用,我们才用上图的方式。

【易混点】给定一个二维数组arr[3][3],arr, arr+1, *arr + 1, *(*arr + 1)是什么意思

  • arr是什么意思呢?
  • 若我给你一个一维数组arr[10],你是不是可以很快反应过来,没错arr表示的是首元素的地址!⭐重要基本认识:数组名是首元素地址
  • 对于二维数组同样的,数组名arr表示首元素地址,这个首元素可不一般!
    在这里插入图片描述
  • arr代表的元素是第一行的数组!!!
  • 一维数组中arr表示的是第一个元素的地址,所以arr的类型为int* 。而二维数组arr也是表示首元素的地址,不过它的首元素是一个一维数组,也就是说它是一个指向数组的指针,那么它的类型就是int(*)[]。所以我们可以创建一个数组指针p去取接收arr。但要区别于二级指针。(结合类型去判断是很好的方法)
int main()
{
    
    
  int arr[3][4] = {
    
    1,2,3,4,5,6,7,8,9,10,11,12};
  int** p1 = arr;//错误
  int(*p2)[4] = arr;//正确
  return 0;
}
  • arr+1 是什么意思?
  • 同样的我们类比一维数组,arr+1指针移动到第二个元素;当我们把二维数组的一行看成是一个元素的时候,二维数组等价于一维数组,所以这一加我们跳过一个元素(一行)。
    在这里插入图片描述
  • *arr是什么意思?
  • 我们之前指出arr是一个 二维数组数组名 → 二维数组首元素地址 → 一个数组指针,对于数组指针解引用,不用说嘛,我们得到了一个数组。
  • arr[0] 等价于 *(arr + 0)同样的我们可以推出 arr[1] = *(arr+1)……他们分别表示着第0行的一维数组,第1行的一维数组……以此类推,见上图。
  • 注意arr 和 arr[0]都是数组,代表一块连续的内存空间,意义上不是一个指针。
    在这里插入图片描述
  • 不过使用的时候数组名可以用于表示数组的首元素地址而已。arr表示二维数组的数组名,arr[0]表示一维数组的数组名。
    在这里插入图片描述
  • 但数组名就算是可以表示数组的首元素地址,也不能通过++ - - 操作来移动指向,因为数组名是指针常量,一旦移动指向表示的数组空间就变了。
  • 检测题*§
  • int arr[10] = {1,2,3},*p = arr,下面叙述正确的是
    A. a++ 表示 arr[1] 的地址
    B. *(p + 1) 和 &arr[i] 相等
    C. arr 和 p 完全相等
    D. *(p + 4) 与 arr[4] 相等

【分析】

  • A.数组名不能进行++ - -操作
  • B.看类型就可以判断
  • D. 越界

[小总结]
①arr代表一个二维数组,但在使用时数组名表示首元素地址,此时arr的为一个类型为int( * )[3]的数组指针,指向第0行的一维数组。
②* arr代表一个一维数组,但在使用的时候数组名表示首元素地址,此时 * arr的类型为一个int* 的指针。指向一维数组的第一个元素。
在这里插入图片描述

  • *arr+1表示什么意思呢?
  • *arr在使用的时候表示 数组名→首元素地址,+1找到首元素下一个元素的地址。见上图。
  • *(*arr + 1)表示什么意思呢?
  • 上文提到*arr+1找到下一个元素的地址,解引用得到这个元素。也就是arr[0][1]
  • &arr是什么意思呢?
  • arr表示的是整个二维数组,所以&arr表示整个二维数组的地址,它的类型是int[3][3](*)
  • &arr+1,相当于我们把整个arr看成一个整体元素,+1跳过整个二维数组
    在这里插入图片描述

易错点:传参操作和传址操作

【案例】
(下面哪一种swap函数可以实现互换)

void swap1(int a, int b)
{
    
    
	int tmp = a;
	a = b;
	b = tmp;
}

void swap2(int* a, int* b)
{
    
    
	int* tmp = a;
	a = b;
	b = tmp;
}
int main()
{
    
    
	int a = 10;
	int b = 20;
	swap1(a,b);
	swap2(&a,&b);
	return 0;
}
  • 传参操作,传入的是变量a和变量b的临时拷贝,与主函数中的ab是独立的,修改函数里的a , b对主函数中的a,b没有一点关系。
  • swap1中的a,b显然是传参操作,不能实现交换。
  • swap2中的&a,&b虽然传入了地址,但要知道指针也是一种变量。所以传入的a和b的指针其实是主函数a,b指针的临时拷贝,即使在swap函数中发生交换,对主函数中也没有影响。勿混。

易混点:数组指针,指针数组,二级指针(了解)

  • 数组指针:本质还是指针,只不过指针指向一个数组。类型例如 int(*)[ ] (注意[ ]优先级高于 *)
int main
{
    
    
	int arr[3][3] = {
    
    0};
	int(*p)[3] = arr; 
	return 0;
}
  • 指针数组:本质是一个数组,只不过数组的元素是一个个指针。类型例如int*[ ].
int main()
{
    
    
	char*s[] = {
    
    "hello", "world", "hello", "bite"};//字符串常量本质是每个字符首元素地址
}
  • 二级指针:简单的理解就是指针的指针,即存放指针地址,以此类推还有三级指针……
int main()
{
    
    
	int*p = NULL;
	int** pp = &p;
	return 0;
}

课后练习

真的学会了吗?做几道练习题来巩固一下吧。

  • 练习题①
int main()
{
    
    
	int a[5] = {
    
     1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(ptr - 1));
	return 0;
}

【考察知识点】&数组名
【分析】&a表示一个指向数组的指针,数组指针+1跳过整个数组。强制类型转化,将数组指针变成普通指针,但指针的地址不变。
【答案】2 5
在这里插入图片描述

  • 练习题②
int main()
{
    
    
	int a[4] = {
    
     1, 2, 3, 4 };
	int* ptr1 = (int*)(&a + 1);
	printf("%d", ptr1[-1]);
	return 0;
}

【分析】对上一题的巩固,请自行推理
【答案】4

  • 练习题③
int main()
{
    
    
	int a[3][2] = {
    
     (0, 1), (2, 3), (4, 5) };
	int* p;
	p = a[0];
	printf("%d", p[0]);
	return 0;
}

【考察知识点】逗号表达式+二维数组数组名
【分析】首先要小心,数组里面的赋值语句是逗号表达式,坑在哪里体会到了吧。a[0]表示二维数组第0行的一维数组的数组名→即一维数组首元素地址。
【答案】1

  • 练习题④
int main()
{
    
    
int aa[2][5] = {
    
     1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *ptr1 = (int *)(&aa + 1);
int *ptr2 = (int *)(*(aa + 1));
printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
return 0;
}

【考察知识点】&二维数组+ 一维数组名+ 二维数组
【分析】&aa+1跳过一整个数组,再将二维数组指针强转为int*,强转后地址不发生改变。aa+1跳过一行一维数组,强转操作同理。
【答案】10,5
在这里插入图片描述

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/whc18858/article/details/121916558
今日推荐