一、指针的概念
1.1变量和地址
二、变量的指针与指针变量
指针为变量的地址,而专门用来存储另一个变量的地址的变量就是指针变量。
2.1、指针变量的定义及使用
(1)、指针变量的定义
定义指针变量的符合为*,如下定义了三个指针变量。它们的变量名为pi、pj、pf,而不是*pi、*pj、*pf。*号在此只用来声明。
//声明了两个整型变量和一个浮点型变量
int i, j;
float f;
//声明三个指针变量
int *pi, *pj;
float *pf;
(2)、指针变量的使用
//声明一个变量i,初始化值为10
int i = 10;
//利用取地址符&获取i的地址
printf("%d", &i);
//定义一个指针变量pi,指向i的地址
int *pi = &i;
//利用指针运算符*获取pi指向的内存,即为i的值
printf("%d", *pi);
注:在C语言中,所有变量的声明都必须放在最前面,但是有些编译器你没放前面也可以通过,这里注意一下
(3)、&和*运算符的结合方向
“&”和“*”两个运算符优先级相同,但按从右至左方向结合。可理解为从右开始运算
//声明一个变量i
int i = 10;
//声明一个指针变量pi,指向i
int *pi = &i;
//输出i的地址
printf("%d", &*pi);
上面的代码定义了一个指向i的指针变量pi,而输出i的地址使用了“&*pi”。首先,pi是一个指针变量,pi的内容为i的地址。因为运算符是右结合,则先是运算*pi。即为pi地址中的内容,就是10。然后再取地址,&*pi即为i的地址。
2.2、指针变量的初始化
void main(){
int a = 10;
//利用取地址符&,获取变量a的地址,给指针变量pa赋值
int *pa = &a;
}
2.3、指针运算
(1)赋值运算
void main(){
int *px, *py, *pz, x;
//1、指向某个地址
px = &x;
//2、赋予空指针
py = NULL;
//3、赋予指定地址
pz = 4000;
}
(2)指针与整数的加减运算
(3)关系运算
接下来来一个小练习:
//声明函数
void invert(int *a, int start, int end);
/**
* 采用递归法对a数组的元素进行逆序
*/
void main(){
}
/**
* 实现函数
* a为数组首地址
* i位起始逆序元素
* j为逆序结尾元素
*/
void invert(int *a, int start, int end){
//临时变量,用于交换
int temp;
//当起始逆序元素小于逆序结尾元素时,说明还没有逆序到中间元素
if(start < end){
//将起始元素和结尾元素交换
temp = a[start];
a[start] = a[end];
a[end] = temp;
//交换后再次调用invert,将其余元素逆序,此时start和end要同时向中间移动
invert(a, start + 1, end - 1);
}
}
三、指针与数组
3.1、指向数组的指针
3.2、字符指针和字符数组
在C语言中,系统本身没有提供字符串数据类型,但可以使用两种方式存储一个字符串:字符数组方式和字符指针方式。
(1)字符数组方式
也就是我们比较常用的方式
void main(){
//定义一个字符数组
char sentence[] = "Do not go gengle into that good night!";
printf("%s", sentence);
}
来个小练习:
/**
* 用数组将字符串sentence复制到字符串copy
*/
void mian(){
char *sentence = "Do not go gentle into that good night!", copy[50];
int i;
//当没有遇到结束符时,一直循环
for(i = 0; sentence[i] != '\0'; i++){
//将数据复制到copy中
copy[i] = sentence[i];
}
printf("复制后的copy是:%s", copy);
}
3.3、多级指针及指针数组
(1)多级指针
简单来说就是指针的指针,指针变量作为一个变量,也有自己的存储空间。而这个存储空间也有一个地址:
void main(){
//定义一个普通变量
int a = 10;
//定义一个指针变量,指向a
int *p = &a;
//定义另一个指针变量,指向指针变量p,此时pp就是二级指针
int **pp = &p;
//输出两个指针
printf("一级指针pa为:%d\n", p);
printf("二级指针ppa为:%d", pp);
//指针的指针和普通指针操作一样,可以用*pp获取pp指向地址中的内容,即p存储的内容
printf("p存储的内容为:%d", *pp);
}
注:因为一级指针和二级指针性质不一样,所以一级指针和二级指针之间不能赋值,如p = pp在编译时会报错(这是书中写的,但是在我实际测试当中,可以赋值,可能是编译器的问题)。
(2)指针数组
即一个元素为指针的数组,定义如下:
int *a[10];
用一个练习熟悉指针数组,解释全在注释当中:
void main(){
//定义并初始化一个int数组
int a[5] = {1, 3, 5, 6, 8}, i;
//定义一个指针数组,与a数组中元素对应
int *p[5];
for(i = 0; i < 5; i++){
p[i] = &a[i];
}
//定义一个二级指针,存放指针数组的首地址。指针数组和普通数组一样,数组名为数组首地址
int **pp = p;
//利用指针数组首地址输出数据
for(i = 0; i < 5; i++){
//数组a中第零个元素地址为p,而p的的地址为pp,所以**pp = a[0]
//数组a中第一个元素地址为p + 1,而p + 1的地址为pp + 1,所有**(pp + 1) = a[1]
//以此类推,**(pp + n) = a[n]
printf("%d\t", **(pp + i));
}
}
3.4、指针与多维数组
(1)多维数组的地址
假设有个二维数组a[4][2],那么可以分两个维度来理解这个数组。
(2)多维数组的指针
举个例子方便理解
void mian(){
//创建一个普通二维数组
int num[5][5] = {
{1, 3, 4, 5, 6},
{4, 5, 7, 8, 8},
{6, 8, 9, 0, 1},
{3, 4, 2, 1, 2},
{4, 5, 6, 3, 2}
};
//声明一个指针数组
int *p_num[5];
int i, j;
//初始化指针数组,每个元素分别指向num[0][0]、num[1][0]、、、
for(i = 0; i < 5; i++){
p_num[i] = &num[i][0];
}
//利用指针数组p_num输出num数组中的元素
for(i = 0; i < 5; i++){
printf("这是第%d轮数组\n", i+1);
for(j = 0; j < 5; j++){
//将p_num[i]作为一个数组首地址,数组存储的内容为*(p_num+j)
printf("%d\t", *(p_num[i] + j));
}
printf("\n");
}
}