一、C语言
(1.1)指针内存类
(1.1.1)内存使用型
有以下程序
1
2
3
4
5
6
7
8
9
10
11
|
#include <stdio.h>
int
fun(
char
s[ ] )
{
char
*p = s ;
while
( *p ! =
0
) p+ +;
return
( p - s) ;
}
main()
{
printf (
"% d\n "
, fun (
"OABCDEF"
) ) ;
}
|
程序运行后的输出结果是?
- 1
- 6
- 7 传入数组引用,退化为指针,指向头字符
- 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#include<stdio.h>
char
*myString()
{
char
buffer[
6
] = {
0
};
char
*s =
"Hello World!"
;
for
(
int
i =
0
; i < sizeof(buffer) -
1
; i++)
{
buffer[i] = *(s + i);
}
return
buffer;
}
int
main(
int
argc,
char
**argv)
{
printf(
"%s\n"
, myString());
return
0
;
}
|
- Hello
- Hello World!
- Well
- 以上全部不正确
1
2
3
4
5
6
7
8
9
10
11
|
void
getmemory(
char
*p)
{
p=(
char
*)
malloc
(100);
}
void
test(
void
)
{
char
* str = null;
getmemory(str);
strcpy
(str,”hello,world”);
printf
(str);
}
|
- 编译错误
- 输出"hello world"
- 输出""
- segmentation fault
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
void
GetMemory(
char
**p,
int
num)
{
if
(NULL==p && num<=
0
)
return
;
*p=(
char
*)malloc(num);
//1
return
;
}
void
main(
void
){
char
*str=NULL;
GetMemory(&str,
100
);
//2
if
(NULL!=str){
strcpy(&str,
"hello"
);
//3
printf(str);
}
return
true
;
//4
}
|
4处的错误是:main函数是个无返回值的函数void main(){},return true表示bool main(){}
1处没有错误,malloc(int)函数就是这么用的
2处也没有错误,符合GetMemory()函数的定义格式,str本身是char*,那么&str就是char**
1
2
3
|
char
*p1 = ”
123
”, *p2 = ”ABC”, str[
50
] = “xyz”;
strcpy(str +
2
, strcat(p1, p2));
printr(“s\n”, str);
|
- xyz123ABC
- z123ABC
- xy123ABC
- 出错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void
getmemory(
char
*p) {
p=(
char
*)
malloc
(100);
strcpy
(p,
"hello world"
);
}
int
main( )
{
char
*str=NULL;
getmemory(str);
printf
(
"%s\n"
,str);
free
(str);
return
0;
}
|
- 正常输出'hello world"
- 输出为空
- 输出"烫烫烫"
- 程序崩溃
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void
getmemory(
char
*p) {
p=(
char
*)
malloc
(100);
strcpy
(p,
"hello world"
);
}
int
main( )
{
char
*str; //注意此处的差异
getmemory(str);
printf
(
"%s\n"
,str);
free
(str);
return
0;
}
|
- 正常输出'hello world"
- 输出为空
- 输出"烫烫烫"
- 程序崩溃
1
2
3
4
5
6
7
8
9
10
11
12
|
#define SIZE_20M (20*1024*1024)
void
func_a()
{
char
*tmp =
malloc
(SIZE_20M) //堆上获取 慢
return
;
}
void
func_b()
{
char
temp[SIZE_20M]; //栈上获取 慢
//...do something using temp
return
;
}
|
- func_a 获得临时内存的方式效率通常更高。
- func_b 使用了太多的栈,程序可能会在运行时候崩溃。
- func_b 存在内存泄露
- func_a 和func_b 分配的内存会自动初始化0
a[0] = 0; a[1] = 1; a[2] = 2;
int *p, *q;
p = a;
q = &a[2];
- 0
- 1
- 2
- 未知
1
2
3
4
5
6
7
8
9
10
11
|
#include <stdio.h>
void
f(
char
**p){
*p +=
2
;
}
main()
{
char
*a[] = {
"123"
,
"abc"
,
"456"
},**p;
p = a;
f(p);
printf(
"%s\r\n"
,*p);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
void
swap(
int
&a,
int
&b)
{
int
temp;
temp=a;
a=b;
b=temp;
cout<<a<<’ ‘<<b<<’\n’;
}
int
main(){
int
x=
1
;
int
y=
2
;
swap(x,y);
cout<<x<<’ ‘<<y<<’\n’;
return
0
;
}
|
1
2
3
4
|
const
char
str1[]=”abc”;
const
char
str2[]=”abc”;
const
char
*p1 = “abc”;
const
char
*p2 = “abc”;
|
- str1和str2地址不同,P1和P2地址相同。
- str1和str2地址相同,P1和P2地址相同。
- str1和str2地址不同,P1和P2地址不同。
- str1和str2地址相同,P1和P2地址不同。
- 4个地址都相同
- 4个地址都不相同。
有以下程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#include <stdio.h>
#include <stdlib.h>
void
fun (
int
*pl,
int
*p2,
int
*s )
{
s=(
int
*) calloc(
1
,sizeof(
int
));
*s=*pl + *p2;
free (s);
}
int
main ( )
{
int
a [
2
]={
1
,
2
},b [
2
]={
40
,
50
},*q = a;
fun(a,b,q) ;
printf (
"%d\n"
, *q);
}
|
程序运行后的输出结果是?
- 42
- 41
- 1
- 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class ClassA
{
public:
virtual ~ ClassA(){};
virtual void FunctionA(){};
};
class ClassB
{
public:
virtual void FunctionB(){};
};
class ClassC : public ClassA,public ClassB
{
public:
};
ClassC aObject;
ClassA* pA=&aObject;
ClassB* pB=&aObject;
ClassC* pC=&aObject;
|
- pA,pB,pC的取值相同.
- pC=pA+pB
- pA和pB不相同
- pC不等于pA也不等于pB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
class A{
public:
long a;
};
class B : public A {
public:
long b;
};
void seta(A* data, int idx) {
data[idx].a = 2;
}
int main(int argc, char *argv[]) {
B data[4];
for
(int i=0; i<4; ++i){
data[i].a = 1;
data[i].b = 1;
seta(data, i);
}
for
(int i=0; i<4; ++i){
std::cout << data[i].a << data[i].b;
}
return
0;
}
|
- 11111111
- 12121212
- 11112222
- 21212121
- 22221111
- p_str[3]='a'
- *(p_str+3)='a'
- p_str[4]='a'
- *(p_str+4)='a'
- int D[4][]
- int *s[8]
- int(*s)[8]
- int D[][8]
- j=r[j].next
- j=j+1
- j=j->next
- j=r[j]->next
- int arr[5];
- int arr(5);
- int *p = new int[5];
- std :: vector v(5);
- p=A或p=A[0] //编译不通过
- p=A[0]或p=A[0][0]
- p=A[0]或p=&A[0][0]
- p=A或p=&A[0][0]
- a+sizeof(int)
- &a[0]+1
- (int*)&a+1
- (int*)((char*)&a+sizeof(int))A. a+sizeof(int)
有以下程序
1
2
3
4
5
6
7
8
9
|
#include < stdio. h >
main ( )
{
int
a [
3
] [
4
] = {
1
,
3
,
5
,
7
,
9
,
11
,
13
,
15
,
17
,
19
,
21
,
23
} , (*p) [
4
] = a , i , j , k =
0
;
for
( i =
0
; i <
3
; i + + )
for
( j =
0
; j <
2
; j + + )
k = k + * ( * ( p+i )+j );
printf (
"%d"
, k );
}
|
程序运行后的输出结果是?
- 40
- 60
- 80
- 100
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
void
foobar(
int
a,
int
*b,
int
**c)
{
int
*p = &a;
*p = 101;
*c = b;
b = p;
}
int
main()
{
int
a = 1;
int
b = 2;
int
c = 3;
int
*p = &c;
foobar(a, &b, &p);
printf
(
"a=%d, b=%d, c=%d, *p=%d\n"
, a, b, c, *p);
return
(0);
}
|
- a=1, b=2, c=3, *p=2
- a=101, b=2, c=3, *p=2
- a=101, b=101, c=2, *p=3
- a=1, b=101, c=2, *p=3
- 函数foobar中的a是按值传递,因此在函数中的修改不会引起主函数中的变化。
- 函数中b传递的是主函数中b的指针,语句b = p ,其中p指向的是函数foobar内局部变量a的地址,让传递过去的指针换了指向的数据,原来指向的数据(主函数中的b)不会有影响。如果这里是*b = *p那么主函数中的b也要相应变化。
- 函数中的c传递的是双重指针,*c = b,也就是让主函数中的p指针指向了主函数中的b的地址
- 在函数foobar中对指针的变化没有影响到主函数,只是让双重指针更换了指向而已
1
2
3
|
char
s[] =
"123"
, *p;
p = s;
printf(
"%c%c%c\n"
, *p++, *p++, *p++);
|
以下程序
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <stdio.h>
void
fun(
char
** p)
{
int
i;
for
(i=
0
;i<
4
;i + + )printf(
"% s"
,p[i]);
}
main( )
{
char
*s[
6
]={
"ABCD"
,
"EFGH"
,
"IJKL"
,
"MNOP"
,
"QRST"
,
"UVWX"
};
fun(s);
printf(
"\n"
);
}
|
程序运行后的输出结果是?
- ABCDEFGHIJKL
- ABCD
- AEIM
- ABCDEFGHIJKLMNOP
1
2
3
4
5
6
|
int
main(
int
argc,
char
**argv)
{
int
a[
4
] = {
1
,
2
,
3
,
4
};
int
*ptr = (
int
*)(&a +
1
);
printf(
"%d"
, *(ptr -
1
));
}
|
- 1
- 2
- 3
- 4
但是其代表的含义不同,*(a+1)代表从a数组首地址跳跃一个int的长度,*(&a+1)表示从a数组首地址跳跃一个数组的长度,也就是指向a数组最后一个元素的下一个位置,因此*(ptr - 1)表示a数组的最后一个元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
class
Test{
public
:
int
a;
int
b;
virtual
void
fun() {}
Test(
int
temp1 = 0,
int
temp2 = 0)
{
a=temp1 ;
b=temp2 ;
}
int
getA()
{
return
a;
}
int
getB()
{
return
b;
}
};
int
main()
{
Test obj(5, 10);
// Changing a and b
int
* pInt = (
int
*)&obj;
*(pInt+0) = 100;
*(pInt+1) = 200;
cout <<
"a = "
<< obj.getA() << endl;
cout <<
"b = "
<< obj.getB() << endl;
return
0;
}
|
- 200 10
- 5 10
- 100 200
- 100 10
(1.1.2)内存大小型
以下系统中,int类型占几个字节,指针占几个字节,操作系统可以使用的最大内存空间是多大:
- 32位下:4,4,2^32 64位下:8,8,2^64
- 32位下:4,4,不限制 64位下:4,8,不限制
- 32位下:4,4,2^32 64位下:4,8,2^64
- 32位下:4,4,2^32 64位下:4,4,2^64
在32位机器上,下列代码中
sizeof(a)的值是()
- 20
- 21
- 22
- 24
- 非以上选项
char *str = arr;
strlen(str) = 3 ; 5
1
2
|
char
s[]=
"\\123456\123456\t"
;
printf(
"%d\n"
,strlen(s));
|
- 12
- 13
- 16
- 以上都不对
这里考查转义字符,注意 \\ 表示字符 \
\123表示字符 {
\t 表示制表符
这些都是一个字符。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#include <iostream>
using namespace std;
class
A {
public
:
int
b;
char
c;
virtual
void
print() {
cout <<
"this is father’s fuction! "
<< endl;
}
};
class
B: A {
public
:
virtual
void
print() {
cout <<
"this is children’s fuction! "
<< endl;
}
};
int
main(
int
argc,
char
* argv[]) {
cout << sizeof(A) <<
""
<< sizeof(B) << endl;
return
0
;
}
|
- 12 12
- 8 8
- 9 9
- 12 16
- 0
- 1
- 2
- 4
1
2
3
4
5
6
7
8
|
struct Date
{
char
a;
int
b;
int64_t c;
char
d;
};
Date data[
2
][
10
];
|
- X+195
- X+365
- X+368
- X+215
{
char flag[3];
short value;
} sampleStruct;
union
{
char flag[3];
short value;
} sampleUnion;
假设 sizeof(char)=1,sizeof(short)=2,那么sizeof(sampleStruct) = 6 , sizeof(sampleUnion) =4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class
A
{
int
a;
short
b;
int
c;
char
d;
};
class
B
{
double
a;
short
b;
int
c;
char
d;
};
|
- 12 16
- 12 12
- 16 24
- 16 20
main() { char str[]="S\065AB"; printf("\n%d", sizeof(str)); }
- 7
- 6
- 5
- error
1
2
3
4
|
unsigned
char
*p1;
unsigned
long
*p2;
p1=(unsigned
char
*)
0x801000
;
p2=(unsigned
long
*)
0x810000
;
|
p2+5= 什么?
- 801005 810005
- 801010 810014
- 801005 810014
- 801010 810015
(1.2)方法、顺序、类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
<p
class
=
"p0"
>
#include
"stdio.h"
class
Base
{
public
:
Base()
{
Init();
}
virtual
void
Init()
{
printf(
"Base Init\n"
);
}
void
func()
{
printf(
"Base func\n"
);
}
};
class
Derived:
public
Base
{
public
:
virtual
void
Init()
{
printf(
"Derived Init\n"
);
}
void
func()
{
printf(
"Derived func\n"
);
}
};
int
main()
{
Derived d;
((Base *)&d)->func();
return
0
;
}
</p>
|
- Base Init Derived func
- Base Init Base func
- Derived Init Base func
- Derived Init Derived func
在构造函数不要调用虚函数。在基类构造的时候,虚函数是非虚,不会走到派生类中,既是采用的静态绑定。显然的是:当我们构造一个子类的对象时,先调用基类的构造函数,构造子类中基类部分,子类还没有构造,还没有初始化,如果在基类的构造中调用虚函数,如果可以的话就是调用一个还没有被初始化的对象,那是很危险的,所以C++中是不可以在构造父类对象部分的时候调用子类的虚函数实现。但是不是说你不可以那么写程序,你这么写,编译器也不会报错。只是你如果这么写的话编译器不会给你调用子类的实现,而是还是调用基类的实现。在析构函数中也不要调用虚函数。在析构的时候会首先调用子类的析构函数,析构掉对象中的子类部分,然后在调用基类的析构函数析构基类部分,如果在基类的析构函数里面调用虚函数,会导致其调用已经析构了的子类对象里面的函数,这是非常危险的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class A{
public:
A(){p();}
virtual void p(){print(
"A"
)}
virtual ~A(){p();}
};
class B:public A{
public:
B(){p();}
void p(){print(
"B"
)}
~B(){p();}
};
int main(int, char**){
A* a=
new
B();
delete
a;
}
|
- AABB
- BBAA
- ABAB
- ABBA
分析一下这段程序的输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include<iostream>
using namespace std;
class
B
{
public
:
B()
{
cout <<
"default constructor"
<<
" "
;
}
~B()
{
cout <<
"destructed"
<<
" "
;
}
B(
int
i): data(i)
{
cout <<
"constructed by parameter"
<< data <<
" "
;
}
private
:
int
data;
};
B Play( B b)
{
return
b;
}
int
main(
int
argc,
char
*argv[])
{
B temp = Play(
5
);
return
0
;
}
- constructed by parameter5 destructed destructed
- constructed by parameter5 destructed
- default constructor" constructed by parameter5 destructed
- default constructor" constructed by parameter5 destructed destructed
首先要说明的是,若用户没有定义,C++隐式声明一个复制构造函数和一个赋值运算符(完成按数据成员复制的动作)。二者很像,但是在下边这点上有很大的不同:复制构造函数是只在对象实例化时才会被调用,也就是说,在复制构造函数调用期间,这个对象处于一个未决状态(直到复制构造函数被成功调用),另外复制构造函数不返回任何值,void都没有。而赋值运算符则在一个现存的对象被赋予新的值时被调用,并且它有返回值。
如果添加上复制构造函数:B(const B & r){cout << "copy ctor " << " ";}
那么输出:
constructed by parameter5 copy ctor destructed
阅读以下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class
parent
{
public
:
virtual
void
output();
};
void
parent::output()
{
printf(
"parent!"
);
}
class
son :
public
parent
{
public
:
virtual
void
output();
};
void
son::output()
{
printf(
"son!"
);
}
1
2
3
4
son s;
memset(&s ,
0
, sizeof(s));
parent& p = s;
p.output();
执行结果是()
- parent!
- son!
- son!parent!
- 没有输出结果,程序运行出错
答案: d
解释:
memset 将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值,
本代码中, 不虚函数链表地址也清空了, 所以p.output调用失败。 output函数的地址编程0
下面代码的输出是什么?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class
A
{
public
:
A() { }
~A() { cout<<
"~A"
<<endl; }
};
class
B:
public
A
{
public
:
B(A &a):_a(a)
{
}
~B()
{
cout<<
"~B"
<<endl;
}
private
:
A _a;
};
int
main(
void
)
{
A a;
//调用一次构造函数
B b(a); //1、调用父类的构造函数,也就是a的构造函数 2、初始化自己的变量
A _a 调用一次构造函数 3、调用b自己的构造函数
}
- ~B
- ~B ~A
- ~B ~A ~A
- ~B ~A ~A ~A
A a; //调用一次构造函数
B b(a); //1、调用父类的构造函数,也就是a的构造函数 2、初始化自己的变量 A _a 调用一次构造函数 3、调用b自己的构造函数
AAAB 析构就是BAAA
有如下程序段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
using namespace std;
class
A {
public
:
~A() {
cout <<
"~A()"
;
}
};
class
B{
public
:
virtual ~B() {
cout <<
"~B()"
;
}
};
class
C:
public
A,
public
B {
public
:
~C() {
cout <<
"~C()"
;
}
};
int
main() {
C * c =
new
C;
B * b1 = dynamic_cast<B *>(c);
A * a2 = dynamic_cast<A *>(b1);
delete a2;
}
则程序输出:
- ~C()~B()~A()
- ~C()~A()~B()
- A)B)都有可能
- 以上都不对
答案解析:创建一个类对象c,然后动态类型转换,让一个B *b1指针指向c,再一次动态类型转换,让一个基类A *a2指针指向b1,当delete a2时,调用析构函数,但是基类A的析构函数不是虚函数,所以只调用A的析构函数,结果应该是:~A()
如下程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<p
class
=
"p0"
>
#include
"stdio.h"
class
A
{
public
:
virtual
void
Test()
{
printf(
"A test\n"
);
}
};
class
B:
public
A
{
public
:
void
func()
{
Test();
}
virtual
void
Test()
{
printf(
"B test\n"
);
}
};
class
C:
public
B
{
public
:
virtual
void
Test()
{
printf(
"C test\n"
);
}
};
void
main()
{
C c;
((B *)(&c))->func();
((B)c).func();
}
</p>
该程序的执行结果
C test B test
((B *)(&c))->func() ==》 B *temp; temp = &c; temp->func(); ==》 这不就是一个典型的多态问题么,用基类指针指向派生类对象,所以肯定调用的是C对象的func函数,输出
C test
((B)c).func(); ==》 不涉及指针的操作,自然就没有多态行为的发生。
有如下程序段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class
A
{
public
:
A()
{
printf(“
0
”);
}
A(
int
a)
{
printf(“
1
”);
}
A& operator=(
const
A& a)
{
printf(“
2
”);
return
*
this
;
}
}
int
main()
{
A al;
al=
10
;
}
则程序输出是:
- 02
- 012
- 01
- 以上都不对
A a1; //调用A默认构造函数
a1=10; //类型不匹配,调用构造函数A(int)进行隐式转化,之后将引用传给operator=()
有如下程序段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<p
class
=
"p0"
>
#include
"stdio.h"
class
A
{
public
:
A()
{
printf(
"1"
);
}
A(A &a)
{
printf(
"2"
);
}
A &operator=(
const
A &a)
{
printf(
"3"
);
return
*
this
;
}
};
int
main()
{
A a;
A b = a;
}
</p>
则程序输出为:
- 12
- 13
- 无法确定
- 编译出错
拷贝构造函数 A a=b 初始化时候赋值
赋值构造函数 A a; a=b
-
下列程序的输出结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
using namespace std;
class
A
{
public
:
void
print()
{
cout <<
"A:print()"
;
}
};
class
B:
private
A
{
public
:
void
print()
{
cout <<
"B:print()"
;
}
};
class
C:
public
B
{
public
:
void
print()
{
A: print();
}
};
int
main()
{
C b;
b.print();
}
编译出错
B的继承为私有继承,对于C已经不能再调用A的所有方法了
有如下程序段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<p
class
=
"p0"
>
class
A
{
int
_a;
public
:
A(
int
a): _a(a)
{
}
friend
int
f1(A &);
friend
int
f2(
const
A &);
friend
int
f3(A);
friend
int
f4(
const
A);
};
</p>
以下调用哪个是错误的:
- f1(0)
- f2(0)
- f3(0)
- f4(0)
非常量引用的初始值必须为左值
在标准C++语言中,临时量(术语为右值,因其出现在赋值表达式的右边)可以被传给函数,但只能被接受为const &类型。
要理解这个先得理解左值和右值的概念
一个区分左值与右值的便捷方法是:看能不能对表达式取地址,如果能,则为左值,否则为右值。
本题举例:
执行f1(0),实参0要传成A对象,那么执行
A &a1 = 0; //这是不行的。
执行f2(0),实参0要传成A对象,那么执行
const A &a2 = 0;//这是可行的。
编译运行如下程序会出现什么结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<p
class
=
"p0"
>
#include <iostream>
using namespace std;
class
A
{
A()
{
printf(
"A()"
);
}
public
:
static
A &get()
{
static
A a;
return
a;
}
};
int
main()
{
A::get();
return
0
;
}
输出A()
调用静态函数本身不会执行构造函数, 但 get ()实例化了一个对象,所以在get()里面调用了构造函数。
设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?
C c;
void
main()
{
A*pa=
new
A();
B b;
static
D d;
delete pa;
}
ABDC 注意构造过程为:CDAB,最所以不相反是由于,手动先调用delete pa
这道题主要考察的知识点是 :全局变量,静态局部变量,局部变量空间的堆分配和栈分配
其中全局变量和静态局部变量时从 静态存储区中划分的空间,
二者的区别在于作用域的不同,全局变量作用域大于静态局部变量(只用于声明它的函数中),
而之所以是先释放 D 在释放 C的原因是, 程序中首先调用的是 C的构造函数,然后调用的是 D 的构造函数,析构函数的调用与构造函数的调用顺序刚好相反。
局部变量A 是通过 new 从系统的堆空间中分配的,程序运行结束之后,系统是不会自动回收分配给它的空间的,需要程序员手动调用 delete 来释放。
局部变量 B 对象的空间来自于系统的栈空间,在该方法执行结束就会由系统自动通过调用析构方法将其空间释放。
之所以是 先 A 后 B 是因为,B 是在函数执行到 结尾 "}" 的时候才调用析构函数, 而语句 delete a ; 位于函数结尾 "}" 之前。
若MyClass为一个类,执行
1
MyClass a[
4
],*p[
5
];
语句时会自动调用该类构造函数的次数是
4
把MyClass a[4],*p[5];分开写;
MyClass a[4];
MyClass *p[5];
则a[4]是类数组,有4个对象,调用构造函数4次
*p[5]是指针数组,也就是5个元素存放的是指向MyClass类型的对象的指针,没有初始化的指针为空,不指向任何对象,也不调用构造函数。
类似于 指针的强制转换,会导致虚函数发生作用,继续调用实际子类实例的函数。
而 类的强制转换,不会导致虚函数作用,是谁就是谁
看以下代码:
A *pa = new A[10];
delete pa;
则类A的构造函数和析构函数分别执行了几次()
- 1 1
- 10 10
- 1 10
- 10 1
-
构造10次毫无疑问
释放数组应用用delete [],这儿的代码只释放了A[0],所以是析构1次
以下代码共调用多少次拷贝构造函数:
1
2
3
4
5
6
7
8
9
10
Widget f(Widget u)
{
Widget v(u);
Widget w=v;
return
w;
}
main(){
Widget x;
Widget y=f(f(x));
}
- 1
- 3
- 5
- 7
先看Widget f(Widget u)函数,调用该函数时,形参u传递时调用一次拷贝构造函数,内部Widget w = u; 调用一次拷贝构造函数,返回w时调用一次拷贝构造,所以调用一次该函数调用3次拷贝构造函数。
再看main中Widget y=f(f(x)); 一共调用了两次f函数,所以是6次拷贝构造函数,然后表达式赋值为y时,再调用一次,共7次。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
#include<iostream>
using namespace std;
class
B
{
public
:
B()
{
cout <<
"default constructor"
<<
" "
;
}
~B()
{
cout <<
"destructed"
<<
" "
;
}
B(
int
i): data(i)
{
cout <<
"constructed by parameter"
<< data <<
" "
;
}
private
:
int
data;
};
B Play( B b)
{
return
b;
}
int
main(
int
argc,
char
*argv[])
{
B temp = Play(
5
);
return
0
;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class
parent
{
public
:
virtual
void
output();
};
void
parent::output()
{
printf(
"parent!"
);
}
class
son :
public
parent
{
public
:
virtual
void
output();
};
void
son::output()
{
printf(
"son!"
);
}
|
1
2
3
4
|
son s;
memset(&s ,
0
, sizeof(s));
parent& p = s;
p.output();
|
- parent!
- son!
- son!parent!
- 没有输出结果,程序运行出错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
class
A
{
public
:
A() { }
~A() { cout<<
"~A"
<<endl; }
};
class
B:
public
A
{
public
:
B(A &a):_a(a)
{
}
~B()
{
cout<<
"~B"
<<endl;
}
private
:
A _a;
};
int
main(
void
)
{
A a;
//调用一次构造函数
B b(a); //1、调用父类的构造函数,也就是a的构造函数 2、初始化自己的变量
}
|
- ~B
- ~B ~A
- ~B ~A ~A
- ~B ~A ~A ~A
B b(a); //1、调用父类的构造函数,也就是a的构造函数 2、初始化自己的变量 A _a 调用一次构造函数 3、调用b自己的构造函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
#include <iostream>
using namespace std;
class
A {
public
:
~A() {
cout <<
"~A()"
;
}
};
class
B{
public
:
virtual ~B() {
cout <<
"~B()"
;
}
};
class
C:
public
A,
public
B {
public
:
~C() {
cout <<
"~C()"
;
}
};
int
main() {
C * c =
new
C;
B * b1 = dynamic_cast<B *>(c);
A * a2 = dynamic_cast<A *>(b1);
delete a2;
}
|
- ~C()~B()~A()
- ~C()~A()~B()
- A)B)都有可能
- 以上都不对
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
<p
class
=
"p0"
>
#include
"stdio.h"
class
A
{
public
:
virtual
void
Test()
{
printf(
"A test\n"
);
}
};
class
B:
public
A
{
public
:
void
func()
{
Test();
}
virtual
void
Test()
{
printf(
"B test\n"
);
}
};
class
C:
public
B
{
public
:
virtual
void
Test()
{
printf(
"C test\n"
);
}
};
void
main()
{
C c;
((B *)(&c))->func();
((B)c).func();
}
</p>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
class
A
{
public
:
A()
{
printf(“
0
”);
}
A(
int
a)
{
printf(“
1
”);
}
A& operator=(
const
A& a)
{
printf(“
2
”);
return
*
this
;
}
}
int
main()
{
A al;
al=
10
;
}
|
- 02
- 012
- 01
- 以上都不对
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
<p
class
=
"p0"
>
#include
"stdio.h"
class
A
{
public
:
A()
{
printf(
"1"
);
}
A(A &a)
{
printf(
"2"
);
}
A &operator=(
const
A &a)
{
printf(
"3"
);
return
*
this
;
}
};
int
main()
{
A a;
A b = a;
}
</p>
|
- 12
- 13
- 无法确定
- 编译出错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#include <iostream>
using namespace std;
class
A
{
public
:
void
print()
{
cout <<
"A:print()"
;
}
};
class
B:
private
A
{
public
:
void
print()
{
cout <<
"B:print()"
;
}
};
class
C:
public
B
{
public
:
void
print()
{
A: print();
}
};
int
main()
{
C b;
b.print();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<p
class
=
"p0"
>
class
A
{
int
_a;
public
:
A(
int
a): _a(a)
{
}
friend
int
f1(A &);
friend
int
f2(
const
A &);
friend
int
f3(A);
friend
int
f4(
const
A);
};
</p>
|
- f1(0)
- f2(0)
- f3(0)
- f4(0)
要理解这个先得理解左值和右值的概念 一个区分左值与右值的便捷方法是:看能不能对表达式取地址,如果能,则为左值,否则为右值。本题举例:
const A &a2 = 0;//这是可行的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<p
class
=
"p0"
>
#include <iostream>
using namespace std;
class
A
{
A()
{
printf(
"A()"
);
}
public
:
static
A &get()
{
static
A a;
return
a;
}
};
int
main()
{
A::get();
return
0
;
}
|
C c;
void
main()
{
A*pa=
new
A();
B b;
static
D d;
delete pa;
}
1
|
MyClass a[
4
],*p[
5
];
|
A *pa = new A[10];
delete pa;
则类A的构造函数和析构函数分别执行了几次()
- 1 1
- 10 10
- 1 10
- 10 1
-
释放数组应用用delete [],这儿的代码只释放了A[0],所以是析构1次
1
2
3
4
5
6
7
8
9
10
|
Widget f(Widget u)
{
Widget v(u);
Widget w=v;
return
w;
}
main(){
Widget x;
Widget y=f(f(x));
}
|
- 1
- 3
- 5
- 7
(1.3)内存的基础知识类
以下对C语言的”指针“描述不正确的是:
- 32位系统下任何类型指针的长度都是4个字节
- 指针的数据类型声明的是指针实际指向内容的数据类型
- 野指针是指向未分配或者已释放的内存地址
- 当使用free释放掉一个指针内容后,指针变量的值被置为NULL
free掉一个指针后,指针的值是不会自动置为NULL的,当然其指向的内存已经被释放,可以重新分配给其他进行使用,此时该指针被称为野指针。
对野指针进行操作,可能会破坏内存结构,因为并不知道当前指针指向的内容是什么,所以一般在free操作结束后,由程序猿将指针置为NULL。
- 下面有关malloc和new,说法错误的是?
- new 建立的是一个对象, malloc分配的是一块内存.
- new 初始化对象,调用对象的构造函数,对应的delete调用相应的析构函数,malloc仅仅分配内存,free仅仅回收内存
- new和malloc都是保留字,不需要头文件支持
- new和malloc都可用于申请动态内存,new是一个操作符,malloc是是一个函数
malloc在头文件stdlib.h中的,不是关键字
引用与指针有什么区别?
- 指针是一个实体,而引用仅是个别名
- 指针没有 const,引用有 const;
- 引用不能为空,指针可以为空;
- “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
- 从内存分配上看:程序为引用变量分配内存区域,而指针不需要分配内存区域
- 指针和引用的自增(++)运算,意义一样
★ 相同点:
1. 都是地址的概念;
指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。
★ 区别:
1. 指针是一个实体,而引用仅是个别名;
2. 引用使用时无需解引用(*),指针需要解引用;
3. 引用只能在定义时被初始化一次,之后不可变;指针可变;
4. 引用没有 const,指针有 const;
5. 引用不能为空,指针可以为空;
6. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
7. 指针和引用的自增(++)运算意义不一样;
8.从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。
设有如下说明:
1
2
3
typedef
struct
ST{
long
a;
int
b;
char
c[2];
} NEW;
则下面叙述中正确的是:
- 以上的说明形式非法
- ST是一个结构体类型
- NEW是一个结构体类型
- NEW是一个结构体变量
struct ST 等价于 NEW,为同一个结构类型
test.c文件中包括如下语句:
1
2
3
4
#define INT_PTR
int
*
typedef
int
* int_ptr;
INT_PTR a,b;
int_ptr c,d;
文件中定义的四个变量中,哪个变量类型不是指针类型?
- a
- b
- c
- d
- 都是指针
- 都不是指针
typedef int* int_ptr相当于重新创建了一个数据类型,它声明的c,d都是指针,而#define INT_PTR int*,宏定义只是按照格式直接替换,所以编译的时候是按照int* a,b来声明的,所以b不是指针类型。
下面哪种C/C++分配内存的方法会将分配的空间初始化为0
- malloc()
- calloc()
- realloc()
- new[ ]
-
1) malloc 函数: void *malloc(unsigned int size)
在内存的动态分配区域中分配一个长度为size的连续空间,如果分配成功,则返回所分配内存空间的首地址,否则返回NULL,申请的内存不会进行初始化。
2)calloc 函数: void *calloc(unsigned int num, unsigned int size)
按照所给的数据个数和数据类型所占字节数,分配一个 num * size 连续的空间。
calloc申请内存空间后,会自动初始化内存空间为 0,但是malloc不会进行初始化,其内存空间存储的是一些随机数据。
3)realloc 函数: void *realloc(void *ptr, unsigned int size)
动态分配一个长度为size的内存空间,并把内存空间的首地址赋值给ptr,把ptr内存空间调整为size。
申请的内存空间不会进行初始化。
4)new是动态分配内存的运算符,自动计算需要分配的空间,在分配类类型的内存空间时,同时调用类的构造函数,对内存空间进行初始化,即完成类的初始化工作。动态分配内置类型是否自动初始化取决于变量定义的位置,在
函数体外
定义的变量都初始化为0,在
函数体内
定义的内置类型变量都不进行初始化。
参照代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class
ClassA {
public
:
virtual ~ ClassA() {
}
virtual
void
FunctionA() {
}
};
class
ClassB {
public
:
virtual
void
FunctionB() {
}
};
class
ClassC:
public
ClassA,
public
ClassB {
public
:
};
ClassC aObject;
ClassA* pA = &aObject;
ClassB* pB = &aObject;
ClassC* pC = &aObject;
下面那一个语句是不安全的
- delete pA
- delete pB
- delete pC
因为三个指针不是动态建立的,不用删除,只有用new建立的对象才会用到delete删除
参照代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class
ClassA
{
public
:
virtual
~ ClassA()
{
}
virtual
void
FunctionA()
{
}
};
class
ClassB
{
public
:
virtual
void
FunctionB()
{
}
};
class
ClassC:
public
ClassA,
public
ClassB
{
public
:
};
ClassC aObject;
ClassA *pA = &aObject;
ClassB *pB = &aObject;
ClassC *pC = &aObject;
假设定义了ClassA* pA2,下面正确的代码是:
pA2=static_cast<ClassA*>(pB);
void* pVoid=static_cast<void*>(pB); pA2=static_cast<ClassA*>(pVoid);
pA2=pB;
pA2=static_cast<ClassA*>(static_cast<ClassC*>(pB));
答案 B D
- pA2=static_cast<ClassA*>(pB); 类型转换无效
- 正确
- pA2=pB; 类型转换无效
- pA2=static_cast<ClassA*>(static_cast<ClassC*>(pB)); 转换有效
static_cast 的用法
static_cast < type-id > ( expression )
该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:
进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成
派生类表示)时,由于没有动态类型检查,所以是不安全的。
②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
③把空指针转换成目标类型的空指针。
④把任何类型的表达式转换成void类型。
注意:static_cast不能转换掉expression的const、volatile、或者__unaligned属性。
使用 char* p = new char[100]申请一段内存,然后使用delete p释放,有什么问题?
- 会有内存泄露
- 不会有内存泄露,但不建议用
- 编译就会报错,必须使用delete []p;
- 编译没问题,运行会直接崩溃
C++告诉我们在回收用 new 分配的单个对象的内存空间的时候用 delete,回收用 new[] 分配的一组对象的内存空间的时候用 delete[]。
关于 new[] 和 delete[],其中又分为两种情况:(1) 为基本数据类型分配和回收空间;(2) 为自定义类型分配和回收空间。
基本类型的对象没有析构函数,所以回收基本类型组成的数组空间用 delete 和 delete[] 都是应该可以的;但是对于类对象数组,只能用 delete[]。
以下对C语言的”指针“描述不正确的是:
- 32位系统下任何类型指针的长度都是4个字节
- 指针的数据类型声明的是指针实际指向内容的数据类型
- 野指针是指向未分配或者已释放的内存地址
- 当使用free释放掉一个指针内容后,指针变量的值被置为NULL
对野指针进行操作,可能会破坏内存结构,因为并不知道当前指针指向的内容是什么,所以一般在free操作结束后,由程序猿将指针置为NULL。
- 下面有关malloc和new,说法错误的是?
- new 建立的是一个对象, malloc分配的是一块内存.
- new 初始化对象,调用对象的构造函数,对应的delete调用相应的析构函数,malloc仅仅分配内存,free仅仅回收内存
- new和malloc都是保留字,不需要头文件支持
- new和malloc都可用于申请动态内存,new是一个操作符,malloc是是一个函数
- 指针是一个实体,而引用仅是个别名
- 指针没有 const,引用有 const;
- 引用不能为空,指针可以为空;
- “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
- 从内存分配上看:程序为引用变量分配内存区域,而指针不需要分配内存区域
- 指针和引用的自增(++)运算,意义一样
★ 相同点:
1. 都是地址的概念;
指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。
★ 区别:
1. 指针是一个实体,而引用仅是个别名;
2. 引用使用时无需解引用(*),指针需要解引用;
3. 引用只能在定义时被初始化一次,之后不可变;指针可变;
4. 引用没有 const,指针有 const;
5. 引用不能为空,指针可以为空;
6. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
7. 指针和引用的自增(++)运算意义不一样;
8.从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。
设有如下说明:则下面叙述中正确的是:
123typedef
struct
ST{
long
a;
int
b;
char
c[2];
} NEW;
- 以上的说明形式非法
- ST是一个结构体类型
- NEW是一个结构体类型
- NEW是一个结构体变量
test.c文件中包括如下语句:
1234#define INT_PTR
int
*
typedef
int
* int_ptr;
INT_PTR a,b;
int_ptr c,d;
文件中定义的四个变量中,哪个变量类型不是指针类型?
- a
- b
- c
- d
- 都是指针
- 都不是指针
1) malloc 函数: void *malloc(unsigned int size)下面哪种C/C++分配内存的方法会将分配的空间初始化为0
- malloc()
- calloc()
- realloc()
- new[ ]
在内存的动态分配区域中分配一个长度为size的连续空间,如果分配成功,则返回所分配内存空间的首地址,否则返回NULL,申请的内存不会进行初始化。
2)calloc 函数: void *calloc(unsigned int num, unsigned int size)按照所给的数据个数和数据类型所占字节数,分配一个 num * size 连续的空间。
calloc申请内存空间后,会自动初始化内存空间为 0,但是malloc不会进行初始化,其内存空间存储的是一些随机数据。 3)realloc 函数: void *realloc(void *ptr, unsigned int size)动态分配一个长度为size的内存空间,并把内存空间的首地址赋值给ptr,把ptr内存空间调整为size。
申请的内存空间不会进行初始化。 4)new是动态分配内存的运算符,自动计算需要分配的空间,在分配类类型的内存空间时,同时调用类的构造函数,对内存空间进行初始化,即完成类的初始化工作。动态分配内置类型是否自动初始化取决于变量定义的位置,在 函数体外 定义的变量都初始化为0,在 函数体内 定义的内置类型变量都不进行初始化。参照代码
12345678910111213141516171819class
ClassA {
public
:
virtual ~ ClassA() {
}
virtual
void
FunctionA() {
}
};
class
ClassB {
public
:
virtual
void
FunctionB() {
}
};
class
ClassC:
public
ClassA,
public
ClassB {
public
:
};
ClassC aObject;
ClassA* pA = &aObject;
ClassB* pB = &aObject;
ClassC* pC = &aObject;
下面那一个语句是不安全的
因为三个指针不是动态建立的,不用删除,只有用new建立的对象才会用到delete删除
- delete pA
- delete pB
- delete pC
参照代码:假设定义了ClassA* pA2,下面正确的代码是:
12345678910111213141516171819202122232425class
ClassA
{
public
:
virtual
~ ClassA()
{
}
virtual
void
FunctionA()
{
}
};
class
ClassB
{
public
:
virtual
void
FunctionB()
{
}
};
class
ClassC:
public
ClassA,
public
ClassB
{
public
:
};
ClassC aObject;
ClassA *pA = &aObject;
ClassB *pB = &aObject;
ClassC *pC = &aObject;
pA2=static_cast<ClassA*>(pB);void* pVoid=static_cast<void*>(pB); pA2=static_cast<ClassA*>(pVoid);pA2=pB;pA2=static_cast<ClassA*>(static_cast<ClassC*>(pB));答案 B D
- pA2=static_cast<ClassA*>(pB); 类型转换无效
- 正确
- pA2=pB; 类型转换无效
- pA2=static_cast<ClassA*>(static_cast<ClassC*>(pB)); 转换有效
static_cast 的用法static_cast < type-id > ( expression )该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成 派生类表示)时,由于没有动态类型检查,所以是不安全的。②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。③把空指针转换成目标类型的空指针。④把任何类型的表达式转换成void类型。注意:static_cast不能转换掉expression的const、volatile、或者__unaligned属性。使用 char* p = new char[100]申请一段内存,然后使用delete p释放,有什么问题?C++告诉我们在回收用 new 分配的单个对象的内存空间的时候用 delete,回收用 new[] 分配的一组对象的内存空间的时候用 delete[]。
- 会有内存泄露
- 不会有内存泄露,但不建议用
- 编译就会报错,必须使用delete []p;
- 编译没问题,运行会直接崩溃
关于 new[] 和 delete[],其中又分为两种情况:(1) 为基本数据类型分配和回收空间;(2) 为自定义类型分配和回收空间。
基本类型的对象没有析构函数,所以回收基本类型组成的数组空间用 delete 和 delete[] 都是应该可以的;但是对于类对象数组,只能用 delete[]。
下面哪种C/C++分配内存的方法会将分配的空间初始化为0
- malloc()
- calloc()
- realloc()
- new[ ]
-
在内存的动态分配区域中分配一个长度为size的连续空间,如果分配成功,则返回所分配内存空间的首地址,否则返回NULL,申请的内存不会进行初始化。
2)calloc 函数: void *calloc(unsigned int num, unsigned int size)按照所给的数据个数和数据类型所占字节数,分配一个 num * size 连续的空间。
3)realloc 函数: void *realloc(void *ptr, unsigned int size)
动态分配一个长度为size的内存空间,并把内存空间的首地址赋值给ptr,把ptr内存空间调整为size。
4)new是动态分配内存的运算符,自动计算需要分配的空间,在分配类类型的内存空间时,同时调用类的构造函数,对内存空间进行初始化,即完成类的初始化工作。动态分配内置类型是否自动初始化取决于变量定义的位置,在 函数体外 定义的变量都初始化为0,在 函数体内 定义的内置类型变量都不进行初始化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class
ClassA {
public
:
virtual ~ ClassA() {
}
virtual
void
FunctionA() {
}
};
class
ClassB {
public
:
virtual
void
FunctionB() {
}
};
class
ClassC:
public
ClassA,
public
ClassB {
public
:
};
ClassC aObject;
ClassA* pA = &aObject;
ClassB* pB = &aObject;
ClassC* pC = &aObject;
|
- 内存泄露一般是指程序申请了一块内存,使用完后,没有及时将这块内存释放,从而导致程序占用大量内存。
- 无法通过malloc(size_t)函数调用申请超过该机器物理内存大小的内存块。
- 无法通过内存释放函数free(void*)直接将某块已经使用完的物理内存直接还给操作。
- 可以通过内存分配函数malloc(size_t)直接申请物理内存。
free释放的内存不一定直接还给操作系统,可能要到进程结束才释放。
可以直到malloc不能直接申请物理内存,它申请的是虚拟内存
下面那一个语句是不安全的
- delete pA
- delete pB
- delete pC
- 会有内存泄露
- 不会有内存泄露,但不建议用
- 编译就会报错,必须使用delete []p;
- 编译没问题,运行会直接崩溃
关于 new[] 和 delete[],其中又分为两种情况:(1) 为基本数据类型分配和回收空间;(2) 为自定义类型分配和回收空间。
基本类型的对象没有析构函数,所以回收基本类型组成的数组空间用 delete 和 delete[] 都是应该可以的;但是对于类对象数组,只能用 delete[]。
- 指针的值是一个地址
- 非法指针是指该指针的值不是一个已经分配的内存地址
- 两个指向同类型地址的指针之间做减法是没有意义的
- 指针的指针占用的内存空间和其他指针占用的内存空间相同