1 C++: 内存申请出错:
1.new :
这是一个奇怪的bug,而我当时定位的错误后来证明其实并不是bug.
错误警告是“段出错”:n=1时的内存分配部分语句报错。我当时定位的是如下文:
int n;
cin>>n;
int **array=new int *[n];//报错 ,n=1时段出错 ,n<0时显然也不对
assert(array!=NULL);
for(int i=0;i<n;i++)
{
array[i]=new int [n];
asssert(array[i]!=NULL);
}
可以改为
unsigned int n;
cin>>n;
//或者此处另加 assert(n>=1);
if(n>1)
{
int **array=new int *[n];
assert(array!=NULL);
for(int i=0;i<n;i++)
{
array[i]=new int [n];
asssert(array[i]!=NULL);
}
}
else if(n==1)
{
int *array=new int [n];
}
else
{
cout<<"! ERROR:n=0";
}
这真的是一个严重的、令我十分尴尬的错误。然而我今天查了下其实上面写的是没问题的,因为new int [0]
、new int *[1]
都是正常的语法,不会报错。只要[]
里面的大于等于0即可。但我当时的确是改了这个问题于是“段出错的ERROR”消失了,看来是另有原因,等以后有时间好好复现一下查一查。
下面是我这次测试的代码案例
using namespace std;
int * array=new int[0];
cout<<&array<<endl;
int *array1=new int [1];
cout<<&array1<<endl;
int **array2=new int*[0];
cout<<&array2<<endl;
int **array3=new int*[1];
cout<<&array3<<endl;
for (int i = 0; i < 1; i++)
{
array3[i]=new int [1];
}
结果是正确的,看来我犯错了,再问题定位上出了错。
上面代码块结果是
另外:现在发现unsigned int n;来接受输入的值也不好,有可能会造成一些隐患(细节后补),还是应该在使用前对输入进行合法性检查。
2 依旧是内存管理
曾经看过子函数中申请空间返回的经典题目,没想到还是在这儿翻了车。
首先明确第一个概念,C语言中的函数传值,传的是参量值的复制,不是参量本身;C语言为了绕开函数传值的复制提供了指针;C++又引入了“引用”的概念。虽然这里没用到。
//错误复现
//函数目的:连接字符串,并返回长度,即模拟strcat()函数
int mystrcat(char *ptr1, char *ptr2)//错误定位于此处
{
int len1 = strlen(*ptr1);
int len2 = strlen(ptr2);
char *strret = new char[len1 + len2 + 1];//另先标记一下,这样写不好,容易诱发栈溢出问题。细节后补。
assert(strret != NULL);
strcpy(strret, *ptr1);
strcpy(strret + len1, ptr2); //这两句完成复制
//delete *ptr1;planA:子函数内释放空间(后发现错误,但仍然留此以供鞭策)
ptr1 = strret; //这是为了返回新的字符串
return len1 + len2; //返回新字符串的长度
}
原理是老生常谈的子函数中申请的空间,其首地址在函数退出时被注销掉,空间申请完了,但索引确没有了。
为了解决这种问题,常用的方法:
1. 二级指针传参
即改写为:
int mystrcat(char **ptr1, char *ptr2)
{
int len1 = strlen(*ptr1);
int len2 = strlen(ptr2);
char *strret = new char[len1 + len2 + 1];
assert(strret != NULL);
strcpy(strret, *ptr1);
strcpy(strret + len1, ptr2);
*ptr1 = strret;
return len1+len2}
外面使用时,本来是
char *str1;
char *str2;
int new_len=mystrcat(str1,str2);
现在是
char *str1;
char *str2;
int new_len=mystrcat(&str1,str2); //此处更改
3动态数组访问出错
其实还是申请空间的问题:
申请并输入二维动态数组之后
//申请并输入二维数组
cout << "请输入行列数: " << endl;
cin >> n;
int **array = new int *[n];
assert(array != NULL);
for (int i = 0; i < n; i++)
{
array[i] = new int[n];
assert(array[i] != NULL);
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
cin >> array[i][j];
}
}`
经典的指针访问二维数组有4种方式
即
char **p=array;
p[i][j];//1.
*(p[i]+j)//2
*(*(p+i)+j);//3.
char *p1=&array[0][0];//4仅适用于静态数组,对动态数组适用性较低。
*(p+i*rowSize+j)
//或者
char *p1=*array;//或*p1=*array[0]
*(p+i*rowSize+j)
这里重点说一下第四种方式,对动态数组适用性较低。这是因为,二维动态数组所申请的空间一般不是完全连续的。从上面申请空间来看(其内存分布是一块一块的)
例如接上文申请的空间,显示各元素地址及内特容`
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
cout <<array[i][j]<<" "<<&array[i][j] << " ";
}
cout << endl;
}
其结果为:
显然每一行的元素都连续,不同行之间不连续,这种情况下使用*(p+i*row+j)
就无法正确寻址到其他行的元素。
如果铁了心要用的话,必须是
int *p=array[i];
*(p+j);//可以寻址完全不如array[i][j]方便
4 .memset使用出错
memset(void *src,int val ,unsigned long size)
其中,val虽然是int类型,但是只有低8位有效…例如
int val=0x12345678;
那么赋值之后,会变成 0x78787878;