C语言指针详解-上
- 前言
- 1.指针的基本概念
- 1.1指针是什么
- 1.2指针的声明与初始化
- 1.3取地址符`&`和解引用符`*`
- `&` 运算符用于**获取变量的地址**
- `*` 运算符用于访问指针指向的值
- 2.指针的类型
- 常见数据类型的指针
- 指针与数组、字符串
- 数组指针
- 结构体指针
- 函数指针
- 二级指针
- void指针
- 3.指针运算
- 3.1指针的解引用
- 3.2指针 + 整数、指针 - 整数
- 3.3指针和指针的比较
- 3.3指针-指针
前言
指针是C语言中一个绕不开的点,也是解题必备的工具,本文将围绕指针这几点进行详解:
- 指针的基本概念
- 指针类型
- 指针运算
- 指针的用途
- 指针的安全隐患和解决方法
- 常用工具
1.指针的基本概念
1.1指针是什么
在C语言中,任何变量的创建都需要在内存中申请空间,而这些空间都有特定的编号,即
地址
,也称作指针
。
地址
也是一串数字,用于存放地址的变量就是指针变量
,我们平常说的指针就是这个指针变量
。
1.2指针的声明与初始化
- 在C语言中,声明指针主要分两步:
- 使用解引用符(
*
)表明声明的变量是个指针- 指定一个指针指向的数据类型
如:
int* p1,p2;
*
表明p1
是个变量,int
表明p1
可以存储指向整型变量的地址
对于p2
,*
和它没有关系,所以p2
是个整型变量
- 而在声明指针时,给指针一个具体的地址或NULL就是初始化
如:
int x = 10;
int *p = &x;
这里创建了变量x
,并使用取地址符(&
)将x
的地址存入p
1.3取地址符&
和解引用符*
&
运算符用于获取变量的地址在之前的学习中,我们应该多次使用了
&
:int x; scanf("%d", &x);
是的,当使用
scanf
函数读取变量,我们其实传递了变量的地址,这样scanf
函数才能正确地将读取到的数据存储到那个变量中。
*
运算符用于访问指针指向的值如:
int x = 10; int *p = &x; printf("%d",*p);
p
是一个指向x
的指针,那么*p
就是x
的值
我们也可理解为*
获取了p
中存放的地址,并通过这个地址找到了变量x
2.指针的类型
指针的类型大多取决它所指向的数据类型:
常见数据类型的指针
int *p1; double *p2; char *p3;
p1
指向整型
p2
指向双精度浮点型
p3
指向字符
指针与数组、字符串
数组名大多情况下是指向数组首元素的指针:
int arr[10]={1,2,3,4,5,6,7,8,9,10}; for(int i=0;i<10;i++)printf("%d ",*(arr+i)); int* pa=arr; for(int i=0;i<10;i++)printf("%d ",*(pa+i));
字符串实际上是一个字符数组,后面跟着一个空字符’\0’作为结束标志
因此,字符串的名字也是第一个字符的地址:char ch[] = "Hello World!\n"; printf(ch); char *pc = ch; printf(pc);
数组指针
数组指针是指向一个具有固定数量元素的数组的指针。
因为只有元素数量固定,数组类型才能被确定:int arr[10]; arr是个数组,元素个数10,元素类型为整型 int (*p1)[10]; p1是个指针,指向数组元素个数10,元素类型为整型 int (*p2)[10] = &arr;这是数组指针的初始化 p1 = &arr;这是数组指针的赋值
注:
&数组名
取出的是整个数组的地址
arr
/&arr[0]
&arr
数组首元素的地址 整个数组的地址 类型 int* int(*)[10]
结构体指针
声明和初始化的过程大同小异:
struct Stu { int a; char c[20]; }; struct Stu S1; struct Stu* p1; 声明 struct Stu* p2 = &S1; 初始化
需注意,当通过指针访问结构体成员时,可使用箭头运算符
->
,也可使用解引用符*
:struct Stu S2={1,"Hello world!"}; p1 = &S2; printf("%d\n",(*(p1)).a); printf("%s\n",p1->c);
函数指针
函数名
与&函数名
都是其地址,存放函数地址的变量就是函数指针:int Add(int a,int b){return a + b;} int (*p1)(int,int); 声明 int (*p2)(int,int) = &Add;初始化 int (*p3)(int,int) = Add; p2与p3相同
可以看见,函数指针声明也分两步:
- 使用解引用符(
*
)表明声明的变量是个指针- 指定一个指针指向的函数类型
函数类型就是:返回类型(参数类型1,参数类型2…)
如int(int,int)
其函数指针类型就是int(*)(int,int)
二级指针
指针变量也是一种变量,创建时会在内存开辟空间,而存储指针变量的地址的变量就是二级指针
int *p1; int* *p2 = &p1;
其中,
*p2
的*
表明p2
是个指针,int*
表明p2
指向指针变量,= &p1
将p1
的地址存入p2
void指针
即以
void
声明的指针,无具体类型,可根据需要强制类型转换成所需的类型。
需注意,不强转就不能解引用:int a = 0; void* p = &a; *p = 10;
但是可以直接使用其存储的地址,但可能有警告:char c[20] = "Hello World!"; void* p = c; printf("%s\n", p);
3.指针运算
前面我讲了指针的类型,而指针的类型在指针运算中起了关键作用
指针运算主要包括:
- 指针的解引用
- 指针 + 整数、指针 - 整数
- 指针和指针的比较
- 指针-指针
3.1指针的解引用
指针类型决定了其在解引用时访问几个字节,以及访问的方式:
int n1 = 0x66666666;
int n2 = 0x66666666;
int n3 = 0x66666666;
int* p1 = &n1;
*p1 = 0;
char* p2 = (char*) & n2;
*p2 = 0;
float* p3 = (float*) & n3;
*p3 = 0;
printf("int:%x\nchar:%x\nfloat:%x\n", n1,n2,n3);
3.2指针 + 整数、指针 - 整数
指针加整数n,其储存的地址加(n* 所指类型的大小),单位字节
可简单验证:
char ch;
int a;
int arr[4] = { 0 };
char* pc = &ch;
int* pa = &a;
int(*parr)[4] = &arr;
printf("pc=%p\npa=%p\nparr=%p\n", pc, pa, parr);
printf("pc=%p\npa=%p\nparr=%p\n", pc+1, pa+1, parr-1);
3.3指针和指针的比较
在两个指针指向同一块空间的不同位置,我们可以进行指针间的比较:
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int*p1=arr;
int*p2=&arr[9];
while(p1<=p2)
{
printf("%d ",*p1);
p1++;
}
3.3指针-指针
相同类型指针可相减,得到相距的元素个数
下面代码使用指针-指针得到字符串长度:
char ch[10] = "Hahaha";
char* pc1 = ch;
char* pc2 = ch;
while (*pc2 != '\0')pc2++;
printf("%d\n", pc2 - pc1);
希望本篇文章对你有所帮助!
当然,本人仅仅是个C语言初学者,如有任何意见,欢迎各位提出!
C语言指针详解-下