编程练习之数组与指针

数组与指针

阅读如下代码,为何出错。

 1 int main() {  
 2     char a[] = { "I am a bad boy" };  
 3     char * pA = new char[ sizeof( a ) ];  
 4     pA = a;  
 5       
 6     for ( size_t i = 0; i < sizeof( a ); ++i ) {  
 7         std::cout << *pA << std::endl;  
 8         ++pA;  
 9     }  
10     delete [] pA;  
11     pA = NULL;  
12     return 0;  
13 }

我们把那段魔术字符串命名为x。

第一步我们将字符串x存入了栈空间;第二步我们试图在堆空间申请一块可以正好存放x的的内存。以上两步都做得很对,但第三步大有问题。

本意是企图将x复制入堆内存中,但实际操作的结果是将指针指向了原x所在的栈空间。这将导致一个内存泄漏。(要注意的是string类的等于号能够赋值是因为操作符的重载)。接下来是遍历整个字符串,运行结果仿佛复制成功了一样,但实际上你是在遍历存在于栈空间的x。遍历的过程中还不断修改指针指向的值,因为分配字符串时最后会自动添加一个空字符串(等价于NULL或0),指针最终指向了栈空间的x末尾的空字符串处。企图释放栈空间导致的爆炸就更不用说了。

代码如何修改才正确呢?

 1 int main() {  
 2     char a[] = { "I am a bad boy" };  
 3     char * pA = new char[ sizeof( a ) ]; 
 4     strcpy( pA, a );    
 5     for ( size_t i = 0; i < sizeof( a ); ++i ) {  
 6         std::cout << *( pA + i );  
 7     }  
 8     delete [] pA;  
 9     pA = NULL;  
10     return 0;  
11 }  

将字符串拷贝到堆内存分配的空间中,需要用到c函数strcpy,将值拷贝过去。你也可以将x的每一个字符赋值过去。注意,千万尽量不要改变指向堆内存首个位置的指针,如果你有把握还原回去的话。释放一定要从堆内存首部分开始,不然也会导致爆炸。

期间你有可能会忽略这些问题。

(1)

1 ++pA;

上面已经提到过了,没有把握释放的是首位的话尽量不要改变指针的指向。你可以使用指针的偏移或数组下标来进行操作

(2)

1 char * pA = new char[ strlen( a ) ]; 

要注意strlen不计空字符的,因此堆内存分配时中会少一个存放空字符的空间,导致很多函数不能在这段字符结束时停止读取。

1 char * pA = new char[ strlen( a ) + 1 ]; 

多分配一个即可,或你可以使用sizeof(注意 sizeof 指向堆内存指针 为指针的大小)。

(3)

你可能会这样释放数组堆内存。

1 delete pA;  

编译运行,没有任何问题。但这是一个未定义行为(UB),在不同的平台或编译器下运行结果可能不同,总之这是一个潜在的危险操作,你应该让自己严格使用delete[]去释放数组堆内存。

后记:

提到了string重载的等号。对于熟悉C/C++的人在这方面不会有任何问题,但对于初学者这可能确实不太好理解和弄懂。我一直不使用重载操作符或许和这也有关系吧。

原文地址:https://www.cnblogs.com/PROJECT-IDOLPROGRAM/p/5097344.html