c++新手容易犯的几个错误

时间:2021-07-26 11:20:36

1.不清楚unsigned类型的特性
#include <string.h>
#include <stdio.h>

int  main( )
{
 char* str = "Hellow"; 
 for (int i = 0; strlen(str)-i>=0; i++)//死循环
 {
  printf("%d \n",i);
 }

 return 0;

上面的程序会出现死循环。看似正确的程序,为什么会出现死循环呢。因为strlen()的返回值是unsigned int类型。unsigned int与int类型相减得到的也是unsigned int类型unsigned int是永远>=0的,所以上面的程序出现了错误。我们在与unsigned 类型变量做大小比较的时候要注意,unsigned 类型>=0永远为真.所以unsigned的类型尽量不要跟0比较大小,上面的判断可以改成i<=strlen(str)


2.返回指向栈空间数据的指针。
#include <string.h>
#include <stdio.h>

char* IntToString(int n)
{
 char strName[64];
 sprintf(strName, "%d", n);
 return strName;//返回的是栈空间的地址,函数返回后 栈空间数据会被释放。
}

int  main( )
{
 int n = 100;
 char* str =  IntToString(n);
 printf("%s", str);
 return 0;

上面的例子将不会打印“100”。第一次看的这个问题,是林锐博士写的《高质量程序设计》里面的一个例子。IntToString()函数里面定义的char strName[64]; 定义的数据是保存在栈空间,函数返回后,栈空间数据会被释放。那么函数返回的指针就是一个野指针。指向了一个无效地址。使用这个指针的时候就会出现问题。上面的函数可以改成char* IntToString(char* bufOut, int n);通过参数传递一个栈外面的申请的空间地址。或者在IntToString()里面new一个堆空间的地址。

3.对包含有string 的结构体里清零。
#include <string>
#include <stdio.h>
using namespace std;

typedef struct  
{
 int n;
 string str;
}DATA;

int  main( )
{
 DATA d;
 memset(&d, 0, sizeof(DATA));// 你知道sizeof(DATA)为多少不?
 return 0;

上面这个例子在windows下编译运行成功,但在linux下会运行错误。我们经常会给一个结构体清零,如果这个结构体里面含有string,那么这个结构体的sizeof()将不能正确反映这个结构体占用的内存大小。我做过这样的实验,sizeof(string)的值 在32位 linux下等于4, 在vc++6.0下等于16,而在vc++ 2005下面等于32。这些值和编译器有关,而且不能反映string数据大小。所以我建议在结构体里面,不要定义string、 CString等不固定大小的类型。


4.指针类型强制转换。
#include <string>
#include <stdio.h>
using namespace std;

void Change(int* n)
{
 *n = 30;
}

int  main( )
{
 char a = 10;
 char b = 20;
 Change((int*)&a);//VC++6.0 release模式运行报错
 
 return 0;

上面的程序在32位 vc++6.0 debug下运行正常,但在release下运行报错。因为char类型变量的内存大小是1个字节,在Change函数里面将指向一个字节的指针转换成了指向4个字节的int*,赋值的时候把它指向的4个字节都改变了,也就是说把变量数据之外的数据也修改了,导致错误发生。那么debug模式为什么运行正常呢,笔者把a,b的地址打印出来,发现debug下a, b内存空间申请的是4个字节,和int型的一样大。第一个字节是char的值,后面三个字节是0xCC,这3个0xCC是debug模式为了方便检查内存溢出故意添加的。所以强制转换成int*也没有问题。鉴于上面问题,我们在指向空间大小不同的指针相互转换,一定要注意是否溢出。


5.char和int转换的问题。

#include <stdio.h>
int  main( )
{
 int n = 130;
 char c = n;
 int m = c;
 
 if (n == m)
 {
  printf("m==n \n");
 }
 else
 {
  printf("m!=n \n");
 }

上面的程序会打印m!=n。上面的例子里面 c==m为真 c==n为假 m==n为假。char c=n;执行完这条语句的时候,c的值就不是130了,因为c是有符号的,值为-126了。int m=c;执行完后m的值也是-126了。
童靴们在char int类型相互转换的时候,如果用unsigned char 代替char 就没这个问题了。

6.delete 和delete[] 的用法。

#include <stdio.h>
#include <string>

class A
{
public:
 A()
 {
  printf("A");
 }
 ~A()
 {
  printf("~A");
 }
};

int  main( )
{

 A* p = new A[3];
 delete p;//应该改为delete[] p;
 
 getchar();
 return 0;

上面的例子构造方法调用了3次,而析构方法只调用了1次. C++告诉我们在回收用 new 分配的单个对象的内存空间的时候用 delete,回收用 new[] 分配的一组对象的内存空间的时候用 delete[]。 关于 new[] 和 delete[],其中又分为两种情况:(1) 为基本数据类型分配和回收空间;(2) 为自定义类型分配和回收空间。 对于 (1),上面提供的程序已经证明了 delete[] 和 delete 是等同的。但是对于 (2),情况就发生了变化。基本本类型的对象没有析构函数,所以回收基本类型组成的数组空间用 delete 和 delete[] 都是应该可以的;但是对于类对象数组,只能用 delete[]。对于 new 的单个对象,只能用 delete 不能用 delete[] 回收空间。 所以一个简单的使用原则就是:new 和 delete、new[] 和 delete[] 对应使用

7.传递给realloc的指针必须是先前通过malloc(), calloc(), 或realloc()分配的
否则报错*** glibc detected *** ./out: realloc(): invalid old size: 0x080486d0 ***
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void TestMem(void)
{
 char* page;
 char* temp;
 char* line = "test1";//此行改成下面注释的就OK了
 //char*  line = malloc(10);  strcpy(line, "test1");
 
 printf("line1=%X\n",line);
 if(temp=realloc(line, 1024))
 { 
  page=temp;
  printf("realloc [OK]\n");
 }
 strcpy(page, "test22");
 printf("line2=%X,page=%X\n",line,page);
 printf("line=%s,temp=%s,page=%s\n", line, temp, page);
 //free memory later
}

int main()
{
 TestMem();
 printf("main [OK]!\n");
 getchar();
 return 0;
}

realloc使用总结

        1. realloc失败的时候,返回NULL
  2. realloc失败的时候,原来的内存不改变,不会释放也不会移动
  3. 假如原来的内存后面还有足够多剩余内存的话,realloc的内存=原来的内存+剩余内存,realloc还是返回原来内存的地址; 假如原来的内存后面没有足够多剩余内存的话,realloc将申请新的内存,然后把原来的内存数据拷贝到新内存里,原来的内存将被free掉,realloc返回新内存的地址
  4. 如果size为0,效果等同于free()
  5. 传递给realloc的指针必须是先前通过malloc(), calloc(), 或realloc()分配的
  6.传递给realloc的指针可以为空,等同于malloc。