被const修饰的变量,不可以被修改,然而对于const修饰指针的不同位置,到底是哪部分不可以被修改?
int a[] = {1,2,3};
const int* p1 = a;
int* const p2 = a;
两个指针p1和p2都指向数组a,数组名a即数组首元素地址,const分别写在不同位置:
对于p1:const修饰的是*p1,即修饰的是解引用该地址后得到的东西,这个东西就是数组中的所有元素。因此,数组中的元素不可以被改变。
对于p2:const修饰的是p2,即修饰的是这一串地址。因此这一个整型空间内储存的地址编号不可以被改变,指针永远指向数组所占用的那一块空间。(至于空间的元素可以被改变)
int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
static int staticVar = 1;
int localVar = 1;
int num1[10] = { 1, 2, 3, 4 };
char char2[] = "abcd";
const char* pChar3 = "abcd";
int* ptr1 = (int*)malloc(sizeof(int) * 4);
int* ptr2 = (int*)calloc(4, sizeof(int));
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
free(ptr1);
free(ptr3);
}
选择题:
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
globalVar在哪里?答:(全局变量)C
staticGlobalVar在哪里?答:(静态变量)C
staticVar在哪里?答:(静态变量)C
localVar在哪里?答:(局部变量)A
num1 在哪里?答:(局部变量,数组名是首元素地址)A
char2在哪里?答:(局部变量,数组名是首元素地址)A
*char2在哪里?答:(解引用得到首元素,存放在栈区)A
pChar3在哪里?答:(指针名,首元素地址)A
*pChar3在哪里?答:(*pChar3被const修饰)D
ptr1在哪里?答:(指针,指向动态空间,但指针本身储存于栈区)A
*ptr1在哪里?答:( *ptr1是动态空间上储存的数据)B
填空题:
sizeof(num1) = 答:4×10=40(num1是数组名,算数组本身大小)
sizeof(char2) = 答:5×1=5(字符串以“\0”结尾,要算上“\0”)
strlen(char2) = 答:4(strlen计算时以“\0”为结束标志,不计“\0”)
sizeof(pChar3) = 答:4或8(pChar3是地址,占4或8字节)
strlen(pChar3) = 答:4
sizeof(ptr1) = 答:4或8(ptr1是地址,占4或8字节)
sizeof 和 strlen 区别?
sizeof(数组名)=数组大小(以字节为单位)
sizeof(字符串)=类型大小×元素个数+1(字符串以“\0”结尾,要计算在内)
sizeof(指针)=4或8(指针储存于一个整形空间)
strlen(数组名)=数组大小
strlen(字符串)=字符串本身大小
strlen(指针)=指针指向的字符串的长度
虽然数组名即指针名,但被放在sizeof和strlen的括号中时,计算的是整个数组的大小;当strlen括号中放入一个指针时,计算的是指针指向字符串的长度。
malloc/calloc/realloc开辟,free释放。
malloc:指针指向新开辟空间的起始位置:
size以字节为单位。
calloc:指针指向新开辟空间的起始位置,将所有位置初始化为0:
开辟num个空间,每个开辟的空间的大小是size,总字节数为num×size。
realloc:在原有空间的基础上扩大或缩小,指针仍指向空间的起始位置:
prt指向原始的空间,size是扩大或缩小后空间的字节数
通过new和delete操作符。
void Test()
{
// 动态申请一个int类型的空间
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
// 动态申请10个int类型的空间
int* ptr6 = new int[3];
delete ptr4;
delete ptr5;
delete[] ptr6;
}
T是某个内置类型
主要针对的是类
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
nt main()
{
// new可以初始化,malloc不行
A* p1 = (A*)malloc(sizeof(A));
A* p2 = new A(1);
free(p1);
delete p2;
//申请一段连续空间时,new后面要跟[N],delete后面要跟[]
A* p3 = (A*)malloc(sizeof(A)*10);
A* p4 = new A[10];
free(p3);
delete[] p4;
return 0;
}
涉及到两层动态开辟:1.先开辟一个Stack大小的空间,系统计算Stack有多大 2.然后调用Stack的构造函数,函数内部n还需要动态开辟指针_a指向的空间,该空间大小为sizeof(int)*capacity。
涉及到两层delete:1.先调用析构函数,函数内的delete是释放_a指向的空间 2.然后释放掉类本身占用的空间。
class Stack
{
public:
Stack(int capacity = 4)
{
cout<<"Stack(int capacity = 4)"<<endl;
_a = new int[capacity];
_top = 0;
_capacity=capacity;
}
~Stack()
{
cout<<"~Stack()"<<endl;
delete[] _a;
_a=nullptr;
_top=0;
_capacity=0;
}
private:
int* _a = nullptr;
int _top;
int _capacity;
};
答:当内部开空间时,实际上会多开辟四个字节,存放n这个整型数据,用这个数据告诉编译器你在delete时要释放多大的空间,也因此,delete时不需要我们传个数。
比如,用new A[10]申请了10份空间,但用delete释放,没加[ ]:
会程序崩溃。开空间是会在空间前面多开四个字节储存N,此时对应的释放方法是delete[ ],[ ]的作用就是让指针往前移动四个字节,把所有空间都释放掉;如果没有[ ]的话,指针不往前移动,仅仅释放了部分的空间,于是导致程序崩溃。
malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。
不同的地方是:
定位new表达式一般和内存池配合使用
为了配合内存池,需要显示进行初始化,然而不可以显式调用构造函数,于是使用定位new表达式就用来在已有空间的基础上初始化。定位new表达式就属于显式初始化。
格式:
new (place_address) type或者new (place_address) type(initializer-list);
place_address必须是一个指针,initializer-list是类型的初始化列表。
为了配合构造函数,也同样需要显式释放,与构造函数不同的是,可以显式调用析构函数。
格式:place_address->~type( );
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main()
{
A* p1 = (A*)malloc(sizeof(A));
new(p1)A; // 注意:如果A类的构造函数有参数时,此处需要传参
p1->~A();
free(p1);//malloc和free对应
A* p2 = (A*)operator new(sizeof(A));
new(p2)A(10);
p2->~A();
operator delete(p2);//operator new和operator delete对应
return 0;
}