目录
数组地址问题
- 数组初始化int data[3]。此时data[]中有三个数据,而不是四个。
- 关于数组的地址问题:
int main() {
int data[] = { 2,8,6,1,10,15,3,12,11 };
//data代表着data首元素的地址
cout << "data:" << data << endl;
//data+1代表着data第二元素的地址
//因此其地址比data多sizeof(int)
cout << "data+1:" << data + 1 << endl;
//&data中的data表示的是整个data数组
//但&data的值也是数组首元素地址
cout << "&data:" << &data << endl;
//&data+1代表着除了data数组外,还多了一个与data相同的数组
//因此其地址比data多sizeof(data),即4*9=36
cout << "&data+1:" << &data+1 << endl;
//*表示取地址,因此为data[0]
cout << "*data:" << *data << endl;
//可以将data[0]理解为一个指针,指向data
cout << "data[0]:" << data[0] << endl;
system("pause");
return 0;
}
形参和实参的区别:
实参(argument):
全称为"实际参数"是在调用时传递给函数的参数. 实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值。
形参(parameter):
全称为"形式参数" 由于它不是实际存在变量,所以又称虚拟变量。是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数.在调用函数时,实参将赋值给形参。因而,必须注意实参的个数,类型应与形参一一对应,并且实参必须要有确定的值。
3、Swap函数一些注意事项
1)达不到交换的作用
template<class T>
void swap(T a,Tb)
{
T temp=a;
a=b;
b=temp;
}
因为当main函数调用swap时,仅仅是相当于实参的数值赋予swap函数的形参(除此之外实参无作用),形参中的a,b确实进行了交换,但由于形参是局部变量,因此在swap函数结束时便销毁的形参。所以实参并不会进行swap的动作。
2)可以达到交换的作用
template<class T>
void swap(T &a,T &b)
{
T c(a);
a=b;
b=c;
}
在swap函数中,其形参使用了引用&。引用即是对某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。而且引用本身并不占用内存,而是和目标变量共同指向目标变量的内存地址。因此对形参的操作就是对main函数中实参的操作,所以可以交换。
template<class T>
void swap(T *a,T *b)
{
T temp;//不可以T *temp,因为指针必须初始化,否则其值不可预见
temp=*a;
*a=*b;
*b=temp;
}
int main()
{
int a=1,b=2;
swap(&a,&b);
return 0;
}
swap形参的类型为指针变量,因此main函数中需要传递的实参为带交换变量的地址,这样形参中的指针才有所指向的地址。在swap函数中,它操作的内容是在内存中此地址指向的数据,因此它确确实实交换了main中的变量。在swap结束后,存放实参地址的形参被销毁,但这不影响我们已经交换了的数据,注意区分与第一种的区别。
//使用异或的概念来交换数组
template<class T>
void swap(T &a,T &b)
{
a^=b;
b^=a; //相当于b=a
a^=b; //相当于a=b
}
//使用加法的概念来交换数组
template<class T>
void swap(T &a,T &b)
{
a=a+b;
b=a-b; //相当于b=a
a=a-b; //相当于a=b
}
当传递的是数组时,不用引用却可以交换,具体分析如下:
#include <iostream>
using namespace std;
template<class T>
void swap_self(T data[])
{
T tem = data[1];
data[1] = data[0];//可以理解成*data+1=*data,取data首地址,即data[0],
//送到地址data[1]所指向的元素上,因此,虽然地址形参
//在函数结束后会销毁,但其操作的是地址指向的元素,
//交换之后不会随着地址形参的销毁而恢复
data[0] = tem;
}
int main() {
int data[] = { 2,8,6,1,10,15,3,12,11 };
swap_self(data);//传递的是数组的首地址过去
printdata(data, 9);
system("pause");
return 0;
}
new使用方法:
1 int *x = new int; //开辟一个存放整数的存储空间,返回一个指向该存储空间的地址(即指针) 2 int *a = new int(100); //开辟一个存放整数的空间,并指定该整数的初值为100,返回一个指向该存储空间的地址 3 char *b = new char[10]; //开辟一个存放字符数组(包括10个元素)的空间,返回首元素的地址 4 float *p=new float (3.14159);//开辟一个存放单精度数的空间,并指定该实数的初值为//3.14159,将返回的该空间的地址赋给指针变量p
1. int *a = new int;
delete a; //释放单个int的空间
2.int *a = new int[5];
delete [] a; //释放int数组空间
3.指针删除与堆空间释放。删除一个指针p(delete p;)实际意思是删除了p所指的目标(变量或对象等),释放了它所占的堆空间,而不是删除p本身(指针p本身并没有撤销,它自己仍然存在,该指针所占内存空间并未释放),释放堆空间后,p成了空指针,注意,此时p成为了野指针!!delete指针p后必须设置指针p的地址为NULL方可。
有关new的良好变成习惯
1、指针变量没有被初始化
声明一个指针但未分配内存空间时,最好将其置为NULL。 char *pc = NULL;
2、指针超过了变量的作用范围。char *pc = NULL; pc = new char[5];
此时不但可以给p[0]到p[4]赋值,还可以给后面的地址赋值,如p[5],p[10]等。
这就是C/C++完美之余的一个历史遗留缺陷,不进行越界检查,导致编译没有任何问题,运行阶段有时一不小心也察觉不出,这就要求程序员养成良好的习惯:new后面必出现delete。
若我们在给p[5]或之后的地址赋过值,在运行到delete [] p;语句时,会报错,程序强制中断。错误原因如提示的信息:damage:after normal block。
这就是上面遗留的第二个问题*2:对同一个指针变量指向的内存释放两次,与释放一个没有成功分配内存或引用越界的指针变量类似,都是不允许的。
3、指针指向的内存被释放了,而指针本身没有置NULL。
指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针.
char *p=new char[10]; //指向堆中分配的内存首地址,p存储在栈区 cin>> p; delete []p; //p重新变为野指针
还需加上一句p=NULL;