前言
指针是C和C++的灵魂,这是毋庸置疑的。但对初学者来说这是比较吃力的,这里我分享我学习指针的笔记和见解。
一、指针是什么?
指针是什么呢?
我给出答案是:指针就是地址,地址也就是指针。这时,你或许还不知道何为地址。我们用更加通俗的语言来概括,地址:就是内存单元的编号。
指针变量?
指针变量就是存放地址的变量
通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样
为什么叫指针?
我们知道int 变量存的是 int 类型的值,char 变量存的是 char 类型的值,而指针,它是一种特殊的变量,存的是内存地址,按照这个模板可以把它理解为:“内存地址变量” 存的是 “内存地址”,等价于:“指针变量” 存的是 “内存地址”
操作系统进行资源调度时,会根据这些变量存的地址去<请求和使用>那个地址代表的内存区域,这就像是这个变量存的地址指向了某片内存,人们用 “指针” 来统称所谓的 “内存地址变量”
1.1 指针的声明
int* p; /* 一个整型的指针 */
double* p; /* 一个 double 型的指针 */
float* p; /* 一个浮点型的指针 */
char* p; /* 一个字符型的指针 */
- 以int* p为例,int*代表整型变量的指针(数据类型),p是指针名。
下面我们给出一个小例子:
#include <stdio.h>
int main ()
{
int a = 20; /* 实际整型变量的声明 */
int *p; /* 整型指针变量的声明 */
p = &a; /* 在指针变量中存储 var 的地址 */
printf("a 变量的地址: %p\n", &a );
/* 在指针变量中存储的地址 */
printf("p 变量存储的地址: %p\n", p );
/* 使用指针访问值 */
printf("*p 变量的值: %d\n", *p );
return 0;
}
这里我们得到:
a 变量的地址: 0x7fff679edda4
p 变量存储的地址: 0x7fff679edda4
*p 变量的值: 20
1.2 空指针
在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。
使用方法如下:
int *p = NULL;
在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。
如果要检查一个空指针,可以使用if进行如下操作:
if(p)
if(!p)
1.3 通过指针访问数组元素
观测下面两个程序都可以访问数组元素:
#include <stdio.h>
int main()
{
int i, *p, a[] = {
3,4,2,5,7,3,7,1,4,6};
p = a;
for (i = 0; i <= 9; i++)
{
printf("%d\n", p[i]);
}
return 0;
}
#include <stdio.h>
int main()
{
int i, *p, a[] = {
3,4,2,5,7,3,7,1,4,6};
p=a;
for (i = 0; i <= 9; i++)
{
printf("%d\n", *(p+i));
}
return 0;
}
- 这里:p[i],*(p+i)处,指针 p 的值是使终没有改变。所以
变量指针 p 与数组名 a 可以互换。
两个程序中:p=a;是把数组名赋值给指针,这里的数组名就是这个数组的首地址,所以就是把数组a[]的首地址存到指针变量p里面。 - 那么数组名是否就是指针呢?我们再通过一个程序来辨别数组名和指针的区别:
- 这一段代码依旧能够将数组各元素输出,这是利用了指针:p++操作,我们可以进一步尝试用数组名a++会发现程序会报错。
- 所以指针和数组名是不同的
- 其实这段程序中的p是指针变量,而数组名只是一个指针常量。
- 变量是可以修改的,常量是不可以修改的。
#include <stdio.h>
int main()
{
int i, *p, a[] = {
3,4,2,5,7,3,7,1,4,6};
p=a;
for (i = 0; i <= 9; i++)
{
printf("%d\n", *p);
p++;
}
return 0;
}
- 同样的,我们观测下面程序,我们在int * 和指针变量p中间加入一个const,使p指针成为常量指针。
- 这种情况下,p指针和数组名就没有什么不同的了。
#include <stdio.h>
int main()
{
int i, a[] = {
1,2,3,4,5};
int *const p = a;
for (i = 0; i < 5; i++)
{
printf("%d\n", *p);
p++ ; /*注意这里,指针值被修改*/
}
}
1.4 指针常量和常量指针
- 对于一个整型变量:
如:int i = 0;我们可以重新对i赋值,因为i是一个变量.
那如果声明时就给它一个初始值,而在其它任何地方都不允许修改此值,可以在在int前或后加一个const,如:
const int i =10;或int const i =10;它们表示的意思是一样的。
- 对于指针int *,const在前或在后是不一样的,
我们将int * const p称为指针常量即指针类型的而常量,它的特别之处在于其指向的地址不能改,但其指向的地址存储的变量能够改。
同理,我们将const int *p称为常量指针,它的特别之处在于其指向的地址可以改,但其指向的地址存储的变量不能改。
- 示例:
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int* const p = &a;
//p = &b; /* 指针指向的地址不可以改*/
a = 30; /* 内容能改*/
printf("%d\n", *p);
}
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
const int *p = &a;
p = &b; /* 指针指向的地址可以改*/
//b = 30; /* 内容不能改*/
printf("%d\n", *p);
}