指针

指针

什么是指针

初学者可以简单理解为指针就是变量的地址

怎么获取变量的地址呢?只要在变量的前面加上&,就表示变量的地址

#include <stdio.h>
int main(){
    int a = 1;
    printf("%d, %d\n", &a, a);
    return 0;
}

输出结果:

2686748, 1

前面那一个数字是指变量a存储的地址,后面指的是变量a的值

指针实际上是一个整数。

指针就是一个unsigned类型的整数。

指针变量

  • 指针变量用来存放指针

  • 指针变量的定义:通过在某种数据类型后面加星号*来表示这是一个指针变量,如下

int* p;
double* p;
char* p;

注意:星号“*”的位置在数据类型之后或者是变量名之前都是可以的,编译器不会对此进行区分

如果一次有好几个同种类型的指针变量要同时定义,星号只会结合于第一个变量名,例如下面的定义中,只有p1是int*类型的,而p2是int型的

int* p1, p2;

如果要让后面的变量也是指针变量,需要在后面的每个变量名之前都加上星号;

int* p1, *p2, *p3;
  • 给指针赋值的方法一般是把变量的地址取出来,让后赋给对应类型的指针变量:
int a;
int* p = &a;
//也可以写成
int a;
int* p;
 p = &a;

注意:地址&a是赋值给p而不是*p的。(星号是类型的一部分,不属于变量)

  • 如何得到指针变量存放的地址所指的元素呢?还是用星号*,示例如下

    #include<stdio.h>
    int main(){
    	int a;
    	int* p = &a;
    	a = 233;
    	printf("%d\n", *p);
    	return 0;
    }
    //输出结果
    233
    

    在上面的代码运行过程中,定义了int类型的a后,并没有对其进行初始化。a被赋值233后,指针变量的地址没有变,但是指针变量所一定的元素已经改变了。

    *p保存的是地址, *p是这个地址中存放的元素,那么如果直接对 *p进行赋值,也是可以起到改变那个保存的元素的功能的,如下面的例子

    #include <stdio.h>
    int main(){
    	int a;
    	int* p = &a;
    	*p = 233;
    	printf("%d, %d/n", *p ,a);
    	return 0;
    }
    //输出结果
    233, 233
    
  • 指针变量也可以进行加减法,其中减法的结果就是两个地址偏移的距离。

    对于一个int*类型的指针变量p来说,p+1就是指p所指的int类型变量的下一个int类型的变量地址。所以这个下一个是指跨越了一整个int型(即4Byte)。

    指针变量也支持自增的自减操作,所以可以用p++等同于p = p+1;

  • 对于指针变量来说,把其存储的地址的类型称为基变量。

    例如:int* p;其中的int就是它的基类型

指针与数组

  • 在C语言中,数组名称也作为数组的首地址使用,因此,有a == &a[0];
#include<stdio.h>
int main(){
	int a[10] = {1};
	int* p = a;
	printf("%d\n", *p);
	return 0;
}
//输出结果
1

在上述代码中,a作为数组a的首地址&a[0]而被赋值给指针变量p,因此输出*p其实就是在输出a[0]

所以,a+i == &a[i]; 注意:a+i其实只是一个地址,如果想要访问其中的元素a[i],还需要加上星号,使其变成*(a+i)后才可以和a[i]等价

由此可以得到一种输入数组元素新颖的写法;

scanf("%d", a+i);

原先在a+i的位置填写的是&a[i],但是a+i和&a[i]等价,所以这样写是合适的

下面是读取一整个数组并输出的例子:

#include<stdio.h>
int main(){
	int a[10];
	for(int i = 0; i < 10; i++){
		scanf("%d", a+i);
	}
	for(int i = 0; i < 10; i++){
		printf(“%d”, *(a + i));
	}
	return 0;
}
  • 另外,由于指针变量可以使用自增操作,因此可以这样枚举数组中的元素:
#include<stdio.h>
int main(){
	int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	for(int* p = a; p < a + 10; p++){
		printf("%d ", *p);
	}
	return 0;
}
  • 最后是指针的减法,如下:
#include<stdio.h>
int main(){
	int a[10] = {1, 4, 9, 16, 25, 36, 49};
	int* p = a;
    int* q = &a[5];
    printf("q = %d\n", q);//2686708
    printf("p = %\nd", p);//2686688
    printf("q - p = %d\n", q - p);//5
    return 0;
}

上面的代码中,q和p中的具体数值和运行环境有关,因此可能会得到跟上面不同的结果,但是两者之间一定是相差20的。但是p-q的输出却是5?因为q-p就是指两个地址之间的距离。两个int型的指针相减,等价于在求两个指针之间差了几个int

使用指针变量作为函数参数

#include<stdio.h>
 void change(int* p){
 	*p =233;
 }
 
 int main(){
	int a = 1;
	int* p = &a;
	change(p);
	printf("%d", a);
	return 0;
}
//输出结果
233

在上面代码中,把int*类型的指针变量p赋值为a的地址,让后通过change函数把指针变量p作为参数传入。此时传入的其实是a的地址。在change函数中,使用 *p 修改地址中存放的数据,也就是改变了a的本身。当最后输出a时,就已经改变了的值。这种传递方式被称为地址传递

看下面例子:使用指针作为参数,交换两个数

首先回顾如何交换两个数

#include<stdio.h>

int main(){
	int a = 1; b = 2;
	int temp = a;
	a = b;
	printf("a = %d, b = %d\n", a, b);
	return 0
}

如果想要把交换功能写成函数,要怎么做呢?首先需要指出,下面这种写法是做不到的:

#include<stdio.h>

void swap(int a , int b){
	int temp = a;
	a = b;
	b = temp;
}

int main(){
	int a = 1, b = 2;
	swap(a,b);
	printf("a = %d, b = %d\n", a, b);
	return 0;
}//输出结果
a = 1, b = 2

这是因为函数在接受参数的过程中是单向一次性的值传递,也就是说,在调用swap(a,b)时只是把a和b的值传进去了,这样相当于产生了一个副本,对这个副本的操作不会影响到main函数中a和b的值。接下来介绍使用指针的方法

指针存放的是地址,那么使用指针变量作为参数时传进来的也是地址。只有在获取地址的情况下对元素进行操作,才能真正地修改变量

#include<stdio.h>

void swap(int* a, int* b){//把&a 和&b作为参数传入,使得swap函数中的指针变量a存放&a,指针变量b存放&b。这时,在swap函数中的a和b都是地址,而*a和*b就是地址中存放的元素。
	int temp = *a;
	*a = *b;
	*b = temp;
}

int main(){
	int a = 1, b = 2;
	int* p1 = &a, *p2 = &b;
	swap(p1,p2);
	printf("a = %d, b = %d\n", *p1, *p2);
	return 0;
}
//输出结果
 a = 1, b = 2;

错误写法一:

void swap(int* a, int* b){
	int* temp;
	*temp = *a;
	*a = *b;
	*b = *temp;
}

问题在于定义指针变量temp时,temp没有被初始化,指针变量temp中存放的地址是随机的,如果该随机地址指向的是系统工作区间,那么就会出错(而且这样的概率特别大)

错误写法二:

void swap(int* a, int* b){
	int* temp = a;
	a = b;
	b = temp;
}

这种写法的思想在于直接把两个地址交换,认为交换地址之后元素就交换了。其实这种想法产生于很大的误区:swap函数里交换完地址之后main函数里的a与b的地址也被交换。因为函数参数传送方式是单向一次性的,main函数传给swap的“地址”其实是一个“无符号整型”的数,其本身也跟普通变量一样只是“值传递”,swap函数对地址本身进行修改并不能对main函数里的地址修改,能够使main函数数据发生变化的只能是swap函数中对地址指向的数据进行的修改。

引用

  • 引用的含义:不使用指针打到修改传入参数的目的,不产生副本,而是给原变量起了一个别名,且对引用变量的操作就是对原变量的操作。

  • 引用的方法:只需要在函数的参数类型后面加一个&就可以了(&加在int后面或者变量名前面都可以,一般写在变量名前面)示例如下

    #include<stdio.h>
    void change(int&x){
    x = 1;
    }
    int main(){
    	int x = 10;
    	change(x);
    	printf("%d\n", x);
    	return 0;
    }
    //输出结果
    1
    

    注意:要把引用的&和取地址运算符&区分开来,引用并不是取地址的意思。

  • 指针的引用

    #include<stdio.h>
    
    void swap(int* &p1,int* &p2){
    	int* temp = p1;
    	p1 = p2;
    	p2 = temp;
    }
    
    int main(){
    	int a = 1, b = 2;
    	int *p1 = &a, *p2 = &b;
    	swap(p1, p2);
    	printf("a = %d, b = %d\n", *p1, *p2);
    	return 0;
    }
    

    需要强调的是引用是产生变量的别名,因此常量不可以使用引用。于是上面的代码中不可以写成swap(&a,&b),而必须用指针变量p1和p2存放&a、&b。让后把指针变量作为参数传入。

上一篇:【算法】排序算法


下一篇:冒泡排序算法