我们在嵌入式工程的笔试或面试经常遇到关于指针的问题,那就整理一下,取之于网络,用之于网络。
指针概述:
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)良好的动态内存申请和释放习惯。: