1、指针&地址
有一个介绍的很详细的博文可以参考:
C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)
- 指针就是记录变量地址的工具。
- C++中指针可以直接根据其指向的地址来存取变量,也可用于动态分配一维、二维数组等。
- 使用指针也会造成内存访问问题。
(1)认识指针(Pointer)
- 内存中每个字节都有一个内存编号,即地址(这一地址通常采用16进制数来表示)。
- 我们不可能直接使用每个内存的地址,因此大多数高级语言提供声明变量和使用变量来解决这个问题;
- 既然可以通过使用变量来访问,那我们何必非要去知道内存的地址呢?
- C++使用指针的优点 这篇给出了回答,因为在某些时候使用指针真的会非常方便。
- 其实,指针本身就是一种变量类型,存储的内容就是内存地址。通过指针变量,可以直接存取该指针指变量所指向的地址的内容。
(2)变量地址的存取
- C++中定义变量之后,系统就会为其分配内存空间。
- 需要使用某一数据时,存取那一地址的内存空间就可以了。
- 如果想更清楚的了解这一变量所在内存的地址,就可以通过取地址符(&)来得到变量所在地址:
&变量名
(3)指针变量的声明
- 指针变量即:用来存储内存地址的变量。
- 指针变量指向目标地址后,就可以通过移动指针,取得该地址所代表内存区块的数据值。
- 因为指针也是一种变量,其命名规则与一般变量相同。
- 程序声明指针时,内存分配情况与一般变量也相同。
- 声明:
int* p;
int *p;
意思是:声明一个指向int的指针类型变量,其名称为p;
注意:p中存的是数据地址;而 *p存的才是数据本身; - !!!!指针变量必须指向合法地址(系统分配给程序的地址),或者指针变量在声明时先赋值为0或NULL;
- 指针声明演示:类型* 指针变量;
① int* p=0; or int* p=NULL;
②int value =5; int* p = &value; // 变量value的地址放到p里,,也就是 * p=5
③int value=5; int* p=0; p=&value; //先让p存0(初始化),再指向value的地址;
④int* p=10; //z这种定义方式是错误的!!!
⑤int* p; *p=10; //也是错的,因为指针变量定义以后没有赋初值
⑥但是如果指针变量事先已经指向了定义或声明过的地址那 *p=10就是正确的(即改变哪个地址中的数据)。
(4)指针运算
int value = 10;
int* p =&value; // &value = 0x2004; 也就是变量value的内存地址的 16进制的2004
p++; //此时 p=2x2008; 因为int变量占4字节。
(5)多重指针
- 由于指针变量是指向内存地址;
- 而指针变量自己所占用的内存空间也拥有一个地址。
- 可以声明“指针的指针”,即“指向指针变量的指针变量”或叫“多重指针”
int num = 10;
int* p=#
int** p1 = &p;
(6)指针和数组
- 在数组中,数组名就是指向该数组第一个元素的内存地址,或说是数组在内存中的其实地址。
- 因此,已定义好的数组,其数组名就是一个指针变量;
- int a[5]={0,5,2,3,4};
a[0] = *a = 0;
a[1] = *(a+1); = 5;
a[2] = *(&a[2]); - int a[5]={0,5,2,3,4};
int* p=a; 或 int* p = &a[0];
(7)指针和字符串
- 之前说过的定义字符串的形式:
用字符数组或指针形式:
char a[]=“STRING”;
或:
char a[7]={‘S’,‘T’,‘R’,‘I’,‘N’,‘G’,’\0’}; - 如同指针处理数组一样,c++中字符串也可以通过指针来声明和操作:
- 用字符串指针变量指向字符串常量
char *a = “STRING”; // *a = S; a = STRING;
声明完成后,系统分配内存存储字符串常量,并设置指针变量 *p 来指向次变量的起始位置;
(8)指针数组
- 类型 *数组名[元素数];
- 二维数组的定义:
char name[4][10]={“Tom”,“Mom”,“lihua”,“hanlei”}; - 用数组指针:
char *name[4] = {“Tom”,“Mom”,“lihua”,“hanlei”}; //此时 name[0] = Tom; *namae[0] = T;
相当于定义了四个字符串常量,分别用四个字符串指针变量指向他们;
2、动态分配
(1)动态分配数组
- C++可以用 new 和 delete运算符在程序运行期间动态分配与释放内存。
- new运算符: 根据要求大小在内存分配足够空间,并返回分配内存的指针值,即内存地址。分配失败返回NULL。
- 不用时用 delete删除分配的控件
- 声明格式: 数据类型 *指针变量 = new 数据类型(初值); delete 指针名
int *p = new int(77);
float *p_f = new int; //未赋初值;
delete p;
delete p_f;
#include<iostream>
#include<cstdlib>
using namespace std;
int main()
{
int *p1 = new int;
int *p2 = new int;
cout <<"被加数:"<<endl;
cin>>*p1;
cout<<"加数:"<<endl;
cin>>*p2;
cout<< *p1<<"+"<<*p2<<"="<<*p1+*p2;
cout<<endl;
delete p1;
delete p2;
system("pause");
return 0;
}
(2)动态分配数组
- 与动态分配变量相同,根据要求寻找合适的内存,并返回内存区段的起始地址。
- 数据类型 *指针变量名 = new 数据类型[元素个数];
- 分配动态数组,必须在中括号内预分配数组元素个数
- 不用时delete [] 指针数组名; //固定格式 就这么写,中括号里没东西;
3、引用类型
- 用来给变量、常数、或对象取别名;
- 引用类型的重要特征:对变量或对象(B)取了别名(A)后,所有作用于A的运算效果同样会在B上产生。
- 声明格式: 数据类型 &引用名称 = 初值;
int j = 20;
int &refj = j; //refj是j 的别名。
4、函数
- 函数是一段程序语句的集合,并有一个名称代替次程序集合。函数可以视为一种独立的模块。需要某项功能时,只需要调用编写完成的函数即可。这样可以提高代码的可重用性,更容易维护。
- c++中函数有两类:系统本身提供的标准函数、用户自定义函数。
- 使用标准函数只需导入相关函数的头文件即可
(1)自定义函数
- 格式:
返回值类型 函数名(参数表)
{
函数体;
return 返回值;
}
(2)函数声明
- 调用函数的程序代码在自定义函数之后,不需要声明。
- 反之需要在调用函数的程序代码中对自定义函数进行声明。
- 声明格式:
返回值类型 函数名(数据类型 参1,数据类型 参2,…);
或
返回值类型 函数名(数据类型 ,数据类型 ,数据类型,…);
int ADD_FUN(int a,int b);//函数声明
main(void){
ADD_FUN(a,b);
...
}
int ADD_FUN(int a, int b){....}
(3)!函数传值!
Ⅰ、传值调用:
- 主程序调用自定义函数时,系统会为形参分配内存地址,并将实际参数的数值传递并复制给函数中对应的形参。
- 在函数内的形参执行完毕后并不修改主函数中变量的内容。【这量个地方暂时先理解,也许有问题】
- 传值调用时的函数声明和函数调用形式:
- 函数声明
返回值类型 函数名(数据类型 参1,数据类型 参2,…);
或
返回值类型 函数名(数据类型 ,数据类型 ,数据类型,…); - 函数调用
函数名(参1,参2,参3…);
Ⅱ、传址调用
- 传址调用在 调用函数时并不会为形参分配实际的地址,而是将实参的地址直接传递给所对应的形参。
- 由此,函数的形参将于所传递的实参共享同一地址,所以,函数内形参在执行完毕时将通过指针方式指向实参的变量地址,更改原先调用函数内的变量内容。【这一句意思就是,实参和形参共享同一地址,所以形参改变了,实参会随之改变】
- 也就是说,C++是以 分配指针变量的形式参数来存放实际参数传入的变量地址,实际上也就是一种传递指针变量的功能。
- 函数声明:
返回值类型 函数名(数据类型 *参1,数据类型 *参2,…);
或
返回值类型 函数名(数据类型 *,数据类型 *,数据类型 *,…); - 函数调用
函数名(&参1,&参2,&参3…);
Ⅲ、参数默认值
可以在声明时为参数设置默认值,但是注意要放在右侧,因为C++编译程序会假设要省略的参数对应到参数行右侧(也就是,调用函数传参时实参个数可以小于形参个数,前提是右侧的几个形参设置了默认值)
Ⅳ、数组参数
之前说过数组的数组名就是该数组的首地址,所以传址调用的方式也可以应用在数组参数的传递上,可以直接使用传址调用的方式把一个数组传递个另一个函数【也就是,数组传递的时候传递的就是数组地址,或者说是指向数组地址的指针】
- 在函数中该变数组内容,主程序中的数组内容也会随之改变。
- 函数声明:
返回值类型 函数名(数据类型 数组名[ ],数据类型 变量名,…){函数体}
或
返回值类型 函数名(数据类型 *数组名,数据类型 变量名,…){函数体} - 函数调用:
函数名(数组名,变量名,…);
(4)、指针返回值
返回值除了是一般类型以外,也可以是指针类型。
-函数声明:
返回值类型 *函数名(参数表){函数体}
5、函数的高级应用
函数指针这一块,还没完全理解,暂时不写,但是有一篇博文可以参考
C++函数指针学习
6、变量种类
- 局部变量
在一个函数内部定义的变量(包括函数形参),它只在本函数范围内有效,也就是说只有在本函数内才能使用它们,在此函数以外是不能使用这些变量的,这种类型的变量就称为“局部变量”。局部变量存储在栈内存,在函数结束后自动销毁。- 全局变量
在函数体外定义的变量,可以为本源文件中其它函数所公用,有效范围为从定义变量的位置开始到本源文件结束,这种类型的变量就称为“全局变量”。全局变量存储在静态存储区域(静态内存)。全局变量可以被同一工程项目中其他文件用extern声明后调用,对其每次进行修改都会被保存。- 静态全局变量:
静态全局变量就是在原先的全局变量前面加上了static进行修饰。静态全局变量依然存储在静态存储区。跟全局变量最大的不同在于,静态全局变量不能被其他源文件使用,只能被本源文件使用,对其每次进行修改都会被保存。- ** 静态局部变量**
静态局部变量就是在原先的局部变量前面加上了static进行修饰。静态局部变量是静态变量因此存储在静态存储区内,等到整个程序结束才会被销毁。但是它的作用域依然在函数体内部。但是自从静态局部变量被定义后,每次对其进行修改都会被保存下来。
7、函数重载问题
- 同一函数名可以定义多个不同函数体,c++在调用函数时会根据形参个数或参数类型来决定调用的是哪个函数。
- 在c语言中就需要钉以不同函数名(Fun1,Fun2…)
- 函数重载规则:
(1)函数名必须相同。
(2)各重载函数间的参数行类型与个数不能完全相同。