指针(pointer)在C类语言里是很重要的概念。类比的话,就像是你电脑桌面上的快捷方式,和文件实际所在的位置。指针指向了变量所在的地址。
当你声明一个变量的时候,计算机就会帮你分配一个空间储存它,为了确保之后你调用值的时候计算机都可以准确地找到它,这个空间会拥有一个唯一独特的编号,地址。指针是储存地址的变量(当然它被声明的时候,也会被分配一个地址[套娃!])。
声明指针:
声明一个指针用*:
int* ptr;
char* pptr;
可以声明任何类型的指针(毕竟变量都需要储存,都有地址),指向指针的指针也可以(用两个**),指向数组也可以,指向结构体也可以。
引用:
在C/C++里, 为了好记可以把引用和指针看成差不多的东西, 因为都和地址有关(其实不准确)。
int a = 1;
//引用
int &ref = a;
//指针
int *ptr = &a;
对于引用来讲:
ref 相当于 a 的别名(绰号,比如李红[a]小名叫小红[ref]),对 ref 的任何操作就是对a的操作。
所以 ref 既不是a的拷贝,也不是指向 a 的指针,其实ref就是 a 它自己(声明ref并不会多开辟一块内存)。
引用的规则:
- (1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化,但指针最好也初始化,否则容易变成野指针)。
- (2)不能有 NULL 引用,引用必须与合法的存储单元关联(指针则可以是 NULL)。
- (3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。
常用符号:
说实话,我刚学的时候,* & ->这些符号老弄混
在声明的时候:
int variable;
int* b;//*表示声明一个指针
int &c = variable;表示声明一个引用
但在声明过后,符号意思会有些微变化:
int a = 1;
int* b;
b = &a;//此时&a是获取a的地址,赋值给b
cout << "都是值: " << *b << a << endl;
cout << "都是地址: " << b << &a << endl;
cout << "这是指针的地址: " << &b <<endl;
在代码内:*表示解引用符([]同样也是解引用哦,所以b[0]也可以打印出a的值{b[0]相当于*(b+0)})
&成了取址符,获取变量的地址。
(extra:->这个符号一般用在访问动态内存中的结构体数据)比如:
struct people {
string name;
int age;
};
int main(){
people* FirstPerson = new people();//在堆里申请了一个结构体内存空间
FirstPerson -> name = "Adam";//相当于(*FirstPerson).name = "Adam";
FirstPerson -> age = 23;
people SecondPerson;
SecondPerson.name = "Bill";
SecondPerson.age = 10;
return 0;
}
我在堆里申请了一块空间,计算机返回我了一个地址,我将它存到people* FirstPerson 这个指针里。而访问结构体成员,一般使用'.'访问符(比如SecondPerson.name)这样去访问它,所以'->'箭头符号等同于(*FirstPerson).name {先把指针解引用'*',再访问成员'.'}。
总之,在代码里,常用的就是' * '和' & '
指针用在函数里:
就是指函数传参,当指针作为参数传递时:
void PassPtr(int* a) {
(*a)++;//记得是要对a指向的值做修改,不要写成a++
}
void PassVar(int b) {
b++;
}
int main() {
int x = 1;
int y = 1;
PassPtr(&x);
PassVar(y);
cout << x << " " << y << endl;
return 0;
}
函数传参进去的时候,都是拷贝了一份原数据进去的,只不过b拷贝的是值,a拷贝的是地址。之前说地址是独特的,对a解引用后,就可以对它指向的变量x直接做修改,而b只是对那个拷贝的值做了修改,也没有return出来,所以y是不会变的。
而如果你打印*a, b, x, y的地址也会看到,a=&(*a)=&x。
指针也能被函数返回:
int* func(int a) {
a++;
int c = a;
return &c;
}
int* func1(int a) {
a++;
int* b = new int();
return b;
}
再来两个函数,在返回指针时有一个特别要注意的点:
- 如果返回的指针指向的变量是在函数内被创建的,要用动态内存!
以上两种函数,func没用,func1用了,可以看一下打印结果:
func返回的指针里的变量,变成乱码了,而申请了堆内存的保存完好。
这是因为:代码运行的时候,内存被分为四个区域:堆,栈,静态区,代码区。而main()调用的函数和local variable都会在栈(stack)中运行,运行完成后,自动收回内存(函数运行占的那部分被回收了!)
在func里声明的变量c,虽然并不会立刻被抹除,但在运行其他函数时,可能会被占用从而把数据抹除掉(就像我运行了一个print),那么从函数传回来的指针被打印时,就会出现乱码了。
堆里的数据:
就在刚才,我使用了动态内存 new了一个int *b。注意,动态内存申请的空间无法自动释放!需要手动去free一下。不过,在整个程序运行完后,计算机也会自动收回堆上的内存,但如果你使用了循环去不停的申请堆内存,却没有手动释放的话,代码会运行的越来越卡。还有一种情况就是,你在函数内申请完内存,但是忘了把地址传出来了,地址丢失,会造成内存泄露。
记得初始化
避免你的指针变成野指针指到奇怪的地方,请务必初始化
int* ptr = NULL;//初始化指向NULL
至少不要让程序一运行就报一些奇奇怪怪的错。