C语言中的指针
文章目录
- C语言中的指针
- 一、变量和地址
- 二、指针变量
- 三、函数地址
- 四、在函数中交换变量内容
- 五、const 修饰的指针
- 1.指针常量
- 2.常量指针
- 3.指向常量的常指针
一、变量和地址
1.C语言中,每次新定义一个变量,都会在相应的存储空间中为该变量分配一块空间,用于存储对应的数据。
例如,定义 int a = 10; 便会在栈空间中划出一个物理空间用于存储整型数字10。
下面以STM32 Cortex3的环境为例,新建一个变量a的汇编代码如下:
MOVS r0,#0x0A ; 将0x0A赋值给R0寄存器中。
STR r0,[sp,#0x08] ; 将R0寄存器中的数据存储到地址为sp+0x14的存储空间中,sp为栈底地址。
因此 整型变量a对应的是 0x200010BC地址存储空间内存放的整数10。
可以使用 “&” 取地址符,来获取变量a对应的地址,此处&a的值为:0x200010BC。
图1 变量和地址
上图中的打印内容:
[main] &a = 200010bc, p = 0x200010bc, &p = 0x200010b0
[main] &b = 200010b8, &c = 200010b4
在该例子中,SP寄存器保存的值为 0x200010A8。
总结:每定义一个变量,都会在内存中开辟一块存储空间用于存放变量的内容,这块存储空间有一个唯一的访问地址。
二、指针变量
指针变量:是指保存的地址的变量,该变量所对应的存储空间保存的是地址。
指针和指针变量是一个意思。
例如声明并定义一个指向整型的指针变量:
int* p = NULL;
p = &a;
指针变量和普通的变量一样,只是指针变量保存的是地址;我们定义一个指针变量,该指针变量也会在内存中开辟一块存储空间用于保存变量的内容。结合图1中所展示的那样,指针变量p保存的是整型变量a的存储地址,而变量p的地址是0x200010B0。同整型变量a那样,使用 “&p” 可以得到p的地址为:0x200010B0。
在图1中,我们定义了整型变量a,赋值a = 10,于是我们便可以使用a代替整型数字10。对于指针变量也是如此,既然我们已经给指针变量p赋值了,明确此处使用变量p表示a的地址,也就是0x200010bc。可以发现,指针(变量)所表示的就是地址,也可以通常所说的指针就是地址。
当我们知道一个变量的地址之后,便可以获取到这个地址中所表示的内容。
在上面的例子中,我们定义了指向整型的指针p,p为a的地址,可以使用 “ * ” 解引用符号进行解引用操作,可以理解为对这个指针进行解码,解码得到该地址里保存的内容。
printf("*p = %d \r\n",*p); // 可以得到*p = 10。
指针可以指向各种数据类型,包括结构体,也可以是函数,指针变量等。其中,指向指针变量的指针被称为二维指针,因为该指针变量中保存的是另一个指针变量的地址。
int** q = &p; // 定义指针变量q二维指针,q保存的内容是p的地址,而p是指向int类型的指针变量。
以下面该程序段为例:
int a = 19;int *p = NULL;p = &a;int** q = NULL;q = &p;printf("&a = %p, a = 0x%x\r\n", &a,a);printf("&p = %p, p = 0x%x, *p = 0x%x \r\n", &p,p,*p);printf("&q = %p, q = 0x%x, *q = 0x%x \r\n", &q,q,*q);
打印结果如下:
&a = 200010b8, a = 0x13
&p = 200010b4, p = 0x200010b8, *p = 0x13
&q = 200010b0, q = 0x200010b4, *q = 0x200010b8
其中,q的值为指针变量p的地址,而p的值则是变量a的地址。
三、函数地址
函数的地址:函数地址是指函数代码在内存中的位置,可以通过函数名直接获取。
在编译之后,函数代码会保存在内存中的特定地址上,当需要使用到该函数时,会跳转到该函数的位置执行,函数也是存在地址的。在C/C++中,函数名就表示函数的地址。同样的,也可以定义一个变量来保存函数地址,该变量被称为函数指针。
例如声明一个函数指针pFunc,该函数指针指向型如 void a(int a, int b) 类型的函数。
void (*pFunc)(int, int); // 不使用别名
typedef void(*FUNC)(int,int); // 使用typedef时
下面以图2为例,swap_para函数的函数名就为函数开始地址。
图2 函数地址
四、在函数中交换变量内容
通过值传递的函数:
void swap(int a, int b)
{int c = 0; c = b;b = a;a = c;
}
我们知道,使用上面的函数来交换两个参数的值是不能实现的。
因为在调用swap(c,d)函数时,是将实际参数c和d的值传递给形式参数a和b,在函数swap中新定义两个变量a和b用于保存实参的值,并进行a和b值的交换。swap函数结束之后,栈空间会被回收,形式参数a和b所在的空间便会被回收。因此,实际的参数,c和d的值也不会发生给改变。如果,要改变c和d的值,只能通过改变c和d地址内容来操作,也就是采用指针的方式。
下面是采用指针方式更改后的函数:
void swap(int* addr_a, int* addr_b)
{int c = 0;c = *addr_a;*addr_a = *addr_b;*addr_b = c;
}
在调用的时候,需要传入要交换的变量c和d的地址,如:swap(&c, &d); 在这个函数中,由于交换了变量c和变量d中地址的内容,因而,这种指针方式便能成功更改参数的内容。
五、const 修饰的指针
使用const修饰变量时,用于保护变量的值不能被修改。
const int a = 10; // 必须初始化,之后a的值不能被更改
// const int a; // 错误!!!,a没有初始化。
const int arr[5] = {1,2,3,4,5};
修饰指针时,需要区分是限定指针本身为 const 还是限定指针指向的值为 const。
可以使用助记的方法:把一个声明从右向左读
1.指针常量
特点: 指向常量的指针,指向的值不能被改变,指针p本身的值可以改变。
const char *p; ( * 读成 pointer to )
/* p is a pointer to const char */
此处表明:p指向的值不能被改变,但是p的值可以更改。换句话说,不能使用*p来改变p中的内容。
因为C++里面没有const*的运算符,所以const只能属于前面的类型。C++标准规定,const关键字放在类型或变量名之前等价的,
上面的等价与:
char const *p;
2.常量指针
特点:指针p本身的值不能更改,但指向的值可以改变。 p必须指向同一个地址,但是该地址里面的值可以改变。
char * const p;
/* p is a const pointer to char */
此时表明:p是一个常量指针,p的值是固定的,不能被更改;但是可以使用*p来修改p中的值。
3.指向常量的常指针
特点:指针p本身的值不能更改,且所指向的值也不能更改。
const char * const p;
/* p is a const pointer to const char */
简而言之,const 放在 * 左侧任意位置,限定了指针指向的数据不能更改;const 放在 * 右侧,限定了指针本身不能更改。