1、下面这个程序测试会有什么结果?
#include<iostream>
using namespace std;
void GetMemory(char *p,int num)
{
p=(char *)malloc(sizeof(char) * num);
}
int main ()
{
char *str = NULL;
GetMemory(str,100);
strcpy(str,"hello");
return 0;
}
解析:毛病出在函数GetMemory中,编译器总是要为函数的每个参数制作临时副本,在本例中,void GetMemory(char *p , int num)中的*p实际上是主函数中str的一个副本,而在函数GetMemory中只是把p所指向的内存地址改变了,但是str丝毫未变,因为函数GetMemory没有返回值,因此str并不指向p所申请的那段内存,所以函数GetMemory并不能输出任何东西,如下图所示。事实上,每次执行一次GetMemory就会申请一块内存,但是申请的内存却不能有效释放,结果是内存一直被独占,最终造成内存泄露。
如果一定要用指针去申请内存,那么应该采用指向指针的指针,传str 的地址给函数GetMemory。代码如下:
#include<iostream>
using namespace std;
void GetMemory(char **p,int num)
{
*p=(char *)malloc(sizeof(char) * num);
}
int main ()
{
char *str = NULL;
GetMemory(&str,100);
strcpy(str,"hello");
cout << *str << endl;
cout << str << endl;
cout << &str << endl;
return 0;
}
这样的话程序就可以运行成功了,我们分别打印 *str 、 str 、 &str 可以发现,结果分别是 h 、 hello 、 0024FA80 。str就是字符串的值;*str 是字符串首字符,&str 就是字符串的地址值。
当然也可以用函数返回值来传递动态内存。这种方法更简单,代码如下:
#include<iostream>
using namespace std;
char *GetMemory(char *p,int num)
{
p=(char *)malloc(sizeof(char) * num);
return p;
}
int main ()
{
char *str = NULL;
str = GetMemory(str,100);
strcpy(str,"hello");
cout << *str << endl;
cout << str << endl;
cout << &str << endl;
return 0;
}
我们可以对这道题推而广之,看一下整型变量是如何传值的,代码如下:
#include<iostream>
using namespace std;
void GetMemory1(int *num)
{
*num=5;
}
int main ()
{
int a;
GetMemory1(&a);
cout << a << endl;
return 0;
}
GetMemory1把 a 的地址传了进来,*num 是地址里的值,是 a 的副本.通过直接修改地址里的值,不需要有返回值,也把 a 给修改了,因为 a 所指向的地址的值发生了改变.
答案:
程序崩溃.因为GetMemory 并不能传递动态内存,主函数中的 str 一直是NULL。
2、写出下面程序运行的结果。
#include<iostream>
using namespace std;
int main ()
{
int a[3];
a[0]=0; a[1]=1; a[2]=2;
int *p , *q;
p=a;
q=&a[2];
cout << a[q-p] <<endl;
}
解析:本程序的结构如下:
(1)先声明了一个整型数组a[3],然后分别给数组赋值。
(2)又声明了两个整型指针 p、q,但是并没有定义这两个指针所指向的地址。
(3)使整型指针 p 的地址指向 a(注意 a 就是a[0]),使整型指针 q 的地址指向 a[2]。
可实际验证程序如下:
#include<iostream>
using namespace std;
int main ()
{
int a[3];
a[0]=0; a[1]=1; a[2]=2;
int *p , *q;
p=a;
cout << p <<endl;
cout << *p <<endl;
q=&a[2];
cout << q <<endl;
cout << *q <<endl;
cout << a[q-p] <<endl;
}
上面的输出结果分别是:
002DFD24
0
002DFD2C
2
2
2
q 的实际地址是 002DFD2C,p 的实际地址是 002DFD24。 002DFD2C-002DFD24=0x08(十六进制减法),相差是 8。
q-p的实际运算是(q的地址值(002DFD2C)- p的地址值(002DFD24))/sizeof(int),即结果为 2 。
答案:
运行结果是 2 。
3、请问下面的代码的输出结果是多少?
#include<iostream>
using namespace std;
class A
{
public:
A()
{
m_a = 1;
m_b = 2;
}
~A(){};
void fun()
{
printf("%d%d",m_a,m_b);
}
private:
int m_a;
int m_b;
};
class B
{
public:
B()
{
m_c=3;
}
~B();
void fun()
{
printf("%d",m_c);
}
private:
int m_c;
};
void main ()
{
A a;
B *p = (B *)(&a);
p->fun();
}
解析:首先可以肯定的是上面的代码是非常槽糕的,无论是可读性还是安全性都很差。写这种代码的人,按照Jarne Stroustrup(C++标志化制定者)的说法,应该“斩立决”。
但是不得不说这也是一道很好考察你对内存偏移的理解的题:
B *p = (B *)(&a);
这是一个野蛮的转化,强制把 a 地址内容看成是一个B类对象,p 指向的是 a 类的内存空间。
B类只有一个元素m_c 但是 A类的内存空间存放第一个元素的位置是 m_a, p指向的是对象的内存首地址,比如:0x22ff58,但p->fun()调用B::fun()来打印m_c时,编译器对m_c的认识就是m_c距离对象的偏移量是 0,于是打印了对象A首地址的偏移量 0x22ff58+0变量值,即就是m_a的值1.
答案:
运行结果是:1
4、下面程序输出结果是什么?
#include<iostream>
using namespace std;
class A
{
public:
int m_a;
A()
{
m_a = 1;
}
void print ()
{
printf("%d",m_a);
}
};
class B : public A
{
public:
int m_a;
B()
{
m_a = 2;
}
};
int main ()
{
B b;
b.print();
printf("%d\n",b.m_a);
}
解析:B类中的 m_a 把 A 类中的 m_a 覆盖掉了。在构造 B 类时,先调用 A 类的构造函数,所以 A 类中的 m_a 是1,b.print()打印的是 A 类中的 m_a ,而B类中的 m_a 是2。
答案:
12
5、C++中有了 malloc/free ,为什么还需要 new/delete 呢?
答案:
(1)malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
(2)对于非内部数据类型的对象而言,只用malloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
(3)因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new ,以及一个能完成清理与释放内存工作的运算符delete。new/delete 不是库函数而是运算符。
指针与引用部分算是写完了,还有很多没弄明白的,比如:指针函数、指向指针的指针也说得很少。可能上面的内容还有很多错误之处,欢迎指正…