【QIUC】第一卷-C/C++高级(九)-由动态内存出现的问题引发的思考
文章目录
对*p++的出现的问题的思考
在内存的动态分配的学习时遇到如下问题,最初的代码是这样的,当然结果是对的:
int main1(void) {
int farmer[6] = {
20, 22, 25, 19, 18, 23 };
int* p = new int[3];
for (int i = 0; i < 3; i++) {
*(p + i) = farmer[i];
}
for (int j = 0; j < 3; j++) {
cout<<*(p+j)<<" ";
}
cout<<endl;
delete []p;
system("pause");
return 0;
}
//输出的结果是:20 22 25
1. 使用*p++
在写到这里,我想是否可以用后置加加的方式来完成赋值这个过程,于是:
int main() {
int farmer[6] = {
20, 22, 25, 19, 18, 23 };
int* p = new int[3];
memset(p, 0, 3 * sizeof(int));
for (int i = 0; i < 3; i++) {
cout << "------对p操作前------" << endl;
cout << p << endl;
cout << *p << endl;
*p++ = farmer[i];
cout << "------对p操作后------" << endl;
cout << p << endl;
cout << *p << endl;
cout << endl;
cout << endl;
}
//delete[]p;
system("pause");
return 0;
}
//没有delete[]p 的话 只是打印出的值是随机的
//若有delete[]p ,则程序直接终止
//注释delete[]p,结果是
/*
------对p操作前------
00D1CBA8
0
------对p操作后------
00D1CBAC
0
------对p操作前------
00D1CBAC
0
------对p操作后------
00D1CBB0
0
------对p操作前------
00D1CBB0
0
------对p操作后------
00D1CBB4
-33686019 这里已经指向分配的内存外部了,所以是随机值
*/
上面出现的问题是由于,此时p地址经过后置++的作用已经是指向另外的地址了,不再指向原来分配的内存的地址,所以这里的释放的地址不再是对应的,这是非常危险的
2. 不使用动态分配内存
于是,我就在想,那直接定义一个数组来分配内存会怎样
int main() {
int farmer[6] = {
20, 22, 25, 19, 18, 23 };
int a[3] = {
0 };
int* p = NULL;
p = a;
memset(p, 0, 3 * sizeof(int));
for (int i = 0; i < 3; i++) {
cout << "------对p操作前------" << endl;
cout << p << endl;
cout << *p << endl;
*p++ = farmer[i];
cout << "------对p操作后------" << endl;
cout << p << endl;
cout << *p << endl;
cout << endl;
cout << endl;
}
system("pause");
return 0;
}
/*
------对p操作前------
010FF8D4
0
------对p操作后------
010FF8D8
0
------对p操作前------
010FF8D8
0
------对p操作后------
010FF8DC
0
------对p操作前------
010FF8DC
0
------对p操作后------
010FF8E0
-858993460
//这个地址已经越界,不属于定义的数组的元素,所以下面的访问是非法的
//所以数是一个随机数
*/
3. 修改正确后
这样还是不行,但是当把对p操作后的*p改为 *(p-1),得到的结果完全正确,在这里,需要注意的是当 *p++ = farmer[i];执行完的时候,当farmer[i] 的地址已然被赋值了,赋值完成的一瞬间就将p的地址给加1了,此时如果再用下面的代码对p解引,显示的自然是初始化的值,而要通过 *(p-1)才能够正确访问。(感觉写这种代码就是自己给自己找麻烦)
代码如下:
int main() {
int farmer[6] = {
20, 22, 25, 19, 18, 23 };
/*int* p = new int[3];*/
int a[3] = {
0 };
int* p = NULL;
p = a;
memset(p, 0, 3 * sizeof(int));
for (int i = 0; i < 3; i++) {
cout << "------对p操作前------" << endl;
cout << p << endl;
cout << *p << endl;
*p++ = farmer[i];
cout << "------对p操作后------" << endl;
cout << p << endl;
cout << *(p - 1) << endl;
cout << endl;
cout << endl;
}
/*
------对p操作前------
008FF890
0
------对p操作后------
008FF894
20
------对p操作前------
008FF894
0
------对p操作后------
008FF898
22
------对p操作前------
008FF898
0
------对p操作后------
008FF89C
25
*/
与*p++有关的C语言语法
int main3() {
int a[5] = {
0,1,2,3,4 };
int* p = a;
//cout << *p++ << endl;//输出是0,证明是先进行*p然后输出,
//cout << *(p++) << endl;//输出是0,证明是先进行*p然后输出,其实是一样的
//cout << *++p << endl;//输出是1,证明是先进行++然后输出,
//cout << ++(*p) << endl;//输出是1,但是先计算的是*p,然后整个式子再加上1作为表达式的值,最后输出
//cout << (*p)++ << endl;//输出是0,证明是先进行*p然后输出,最后++
return 0;
}
综上所述:
*(p++) 和 *p++ 一样,都是先进行解引,再对其指向的地址进行改变
++(*p) 和 *++p 一样,都是先对其指向的地址进行改变,再进行解引
对*p++拓展
//字符转置函数(中文也可以实现)
int len = strlen((char*)s);
unsigned char tmp[1024];
unsigned char* p1 = s;
unsigned char* p2 = tmp + len;
*p2-- = 0;
while (*p1) {
//临界条件使结束符0
if (*p1 < 0x80) {
*p2-- = *p1++;
}
else {
*(p2 - 1) = *p1++;
*p2 = *p1++;
p2 -= 2;
}
}
/*
if (*p1 < 0x80) {
*p2-- = *p1++;
}
对于这串代码出现的表达式,实际上是当临界值出现时p2的指向不合法和p1指向字符串结束符是同时的,此时进行判断,然后退出循环
*/