嵌入式笔试关于指针的问题

本文深入探讨了嵌入式工程中指针的基本概念,包括指针的定义、运算、类型转换以及动态内存管理。通过具体示例,解析了一级指针与二级指针的区别,以及如何避免野指针的产生,强调了合理内存分配的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们在嵌入式工程的笔试或面试经常遇到关于指针的问题,那就整理一下,取之于网络,用之于网络。

指针概述:

1.指针是变量,但是指针的字节长度是固定的。因为指针保存的是地址,由操作系统的位数决定,32位机的字节是4,64位机的字节是8。

2.指针指向的内存空间和对应的内存空间
在这里插入图片描述
这里定义了一个整型指针p保存num的地址(0x1000),num的地址就是首字节的地址,而指针p本身的地址为0x2000。下面我们来引入指针的定义:
num对应的内存空间为5,指针p对应的内存空间为0x1000,指针指向的内存空间为5。

num++:对num对应的内存空间加1。 5 --> 6
p++:对p对应的内存空间加1。(步长) 0x1000 --> 0x1004
(p)++:对p指向的内存空间加1。其中p是根据p对应的内存空间找到其对应的内存空间。 5 --> 6
等式:
p == &num
*p == num == *(&num)

下面我们再举一个例子来进一步说明,如下图所示

在这里定义了一个二级指针pp保存指针p的地址(0x2000),而指针pp本身的地址为0x3000。

pp对应的内存空间为0x3000,pp指向的内存空间为0x2000。

等式:
num == *p == **pp == *(&num)
p == &pp

3.指针的运算
a.指针 +/- 整数 = 指针所对应的内存空间与它所指向的类型乘以整数相加减。eg. p++ --> p = p + 1
b.指针 - 指针 = 两个指针相差的数据个数。
c.指针的比较:如果两个指针变量指向同一个数组的元素,那么指向前面元素的指针变量小于指向后面元素的指针变量。
d.指针加指针没有实际意义。

*4.万能指针(void
void * 可以表示任意一种类型的指针。我们知道相同指针类型之间才能进行赋值。

5.指针的使用
当我们给指针赋值的时候,我们要注意指针是否分配了合理的内存。如果指针的地址为空,那么就要在堆上用malloc给它分配空间,这样可以避免野指针的出现!
在这里插入图片描述
上面的解释比较抽象,所以再进一步解释分析一下:
1,调用GetMemory( str )后, str并未产生变化,依然是NULL.只是改变的str的一个拷贝的内存的变化 。
2,strcpy( str, “hello world” );程序运行到这将产生错误。
3,new的时候有可能内存出错,应该在*p = (char *) malloc( num ); 后判断内存是否申请成功,应加上:
if ( *p == NULL )
{
  …//进行申请内存失败处理
}
4.动态创建的内存,free()对str操作很危险。

错误分析:
错认为 GetMemory(char *p)中的 p “就是” GetMemory(str)中的str。但p“不是”str,它只是“等于”str 。比如:
int a = 10;
int b = a;
b = 100;
现在a = 10, b=a=10 ;最后b = 100 ;但是我们能认为a= 100;显然不能,当b改变的时候,a并不会改变,b就不等于a了。因此,虽然p已经有new内存,但是str仍然市null。

GetMemory(str); //把str传进去,str是一个指针,而他实际上是一个int
void GetMemory(char *p) // p是str的一个副本
{
p=(char *)new char[100]; // p的值改变,但是str的值并没有改变。
}

而双重指针为什么就可以了呢:
GetMemory(&str); //把str的地址传进去
void GetMemory(char ** p) //p是str地址的一个副本
{
* p = (char *)new char[100]; // p指向的值改变,也就是str的值改变。
}

我们知道原因后,就可以对其修改:
第一种修改方法(推荐)
void GetMemory2(char **p)变为二级指针.
void GetMemory2(char **p, int num)
{
*p = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
char *str=NULL;
GetMemory=(&str);
strcpy(str,“hello world”);
printf(str);
}

还有一种修改方法:
char *GetMemory()
{
char *p=(char *)malloc(100);
return p;
}
void Test(void){
char *str=NULL;
str=GetMemory();
strcpy(str,“hello world”);
printf(str);
}

在这里插入图片描述
分析:试题11中
char p[] = “hello world”;
return p; 的p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。这是许多程序员常犯的错误,其根源在于不理解变量的生存期。
在这里插入图片描述
分析有两大问题:
1、GetMemory避免了试题10的问题,传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句
*p = (char *) malloc( num );
后未判断内存是否申请成功,应加上:
if ( *p == NULL )
{
 …//进行申请内存失败处理
}
2、试题12的Test函数中也未对malloc的内存进行释放。

在这里插入图片描述

分析: 存在与试题12同样的问题,在执行
char *str = (char *) malloc(100); 后未进行内存是否申请成功的判断;另外,在free(str)后未置str为空,导致可能变成一个“野”指针,应加上: str = NULL;

在这里插入图片描述
分析:在swap函数中,p是一个“野”指针,有可能指向系统区,导致程序运行的崩溃。在VC++中DEBUG运行时提示错误“Access Violation”。该程序应该改为:
swap( int* p1,int* p2 )
{
 int p;
 p = *p1;
 *p1 = *p2;
 *p2 = p;
}

对以上总结:
试题考查面试者对内存操作的理解程度,基本功扎实的面试者一般都能正确的回答其中50%~60%的错误。但是要完全解答正确,却也绝非易事。
对内存操作的考查主要集中在:
  (1)指针的理解;
  (2)变量的生存期及作用范围;
  (3)良好的动态内存申请和释放习惯。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值