原创
c++新手容易犯的几个错误:
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。