一、指针的定义
(1)指针的概念
要知道指针的概念,要先了解变量在内存中如何存储的。在存储时,内存被分为一块一块的。每一块都有一个特有的编号,这个编号可以暂时理解为指针;指针从根本上来看是一个值为内存地址的变量或者数据对象,指针变量的值是地址。例如一个指针的变量名为pr,通过 pr = &a;将a的地址赋值给pr,通过对pr进行操作,对a进行修改操作。
例如:
char a=10;char pr*;
pr=&a;
pr*=20;
printf("*pr=%d,a=%d \r\n", pr*,a);
输出结果为 *pr=20, a=20
(2)指针的类型
int *pr; 从pr 处开始,先与*结合,pr 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以pr是一个返回整型数据的指针
int *pr[3]; 从pr 处开始,先与[]结合,因为其优先级比*高,所以pr 是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以pr 是一个由返回整型数据的指针所组成的数组
int (*pr)[3]; 从pr 处开始,先与*结合,pr 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以pr 是一个指向由整型数据组成的数组的指针
int **pr; 从pr 开始,先与*结合,pr 是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针.
int (*pr)(int); 从pr 处开始,先与指针结合,P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以pr是一个指向有一个整型参数且返回类型为整型的函数的指针
int *(*pr(int))[3]; 可以先跳过,不看这个类型,过于复杂从pr 开始,先与()结合,pr 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的*结合,说明函数返回的是一个指针,,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与*结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据.所以pr 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数.
(3)指针的内存地址值
指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32 位程序里,所有类型的指针的值都是一个32 位整数,因为32 位程序里内存地址全都是32 位长。指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX 为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。
二、指针的运算
(1)指针的赋值
指针变量可以互相赋值,也可以赋值某个变量的地址,或者赋值一个具体的地址
int *px, *py, *pz, x = 10;
赋予某个变量的地址
px = &x;
相互赋值
py = px;
赋值具体的地址
pz = 6000;
(2)指针的加减
指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的,以单元为单位。例如:
char a[10];
int *ptr=(int *)a;
ptr++;
在上例中,指针ptr 的类型是int*,它指向的类型是int,它被初始化为指向整型变量a。接下来的第3句中,指针ptr被加了1,编译器是这样处理的:它把指针ptr 的值加上了sizeof(int),在32 位程序中,是被加上了4,因为在32 位程序中,int 占4 个字节。由于地址是用字节做单位的,故ptr 所指向的地址由原来的变量a 的地址向高地址方向增加了4 个字节。由于char 类型的长度是一个字节,所以,原来ptr 是指向数组a 的第0 号单元开始的四个字节,此时指向了数组a 中从第4 号单元开始的四个字节。
可以用一个指针和一个循环来遍历一个数组,例如:
int array[20]={0};
int *ptr=array;
for(i=0;i<20;i++)
{
(*ptr)++;
ptr++;
}
这个例子将整型数组中各个单元的值加1。由于每次循环都将指针ptr加1 个单元,所以每次循环都能访问数组的下一个单元。
三、指针的运算符&和*
“&”和“*”都是右结合的。假设有变量 x = 1,则*&x 的含义是,先获取变量 x 的地址,再获取地址中的内容。因为“&”和“*”互为逆运算,所以 x = *&x。
例如:
声明两个普通变量
int x, y;
声明两个指针变量
int *px, *py;
声明一个临时变量,用于交换
int t;
输入两个值,赋值给 x、y
scanf("%d", &x);
scanf("%d", &y);
给指针变量 px、py 赋初值(关联变量 x、y)
px = &x;
py = &y;
利用指针来对比 x、y 的值,如果 x 的值比 y 的值小,就交换
if(*px < *py){
t = *px;
*px = *py;
*py = t;
}
printf("x = %d, y = %d", *px, *py);
输入:12 18
输出结果为:x = 18, y = 12
四、指针的表达式
一个表达式的结果如果是一个指针,那么这个表达式就叫指针表式。
例如:
int a,b;
int array[20];
int *pa;
pa=&a;
int **ptr=&pa;
*ptr=&b;
pa=array;
pa++;
char *arr[30];
char **parr=arr;
char *str;
str=*parr;
str=*(parr+1);
str=*(parr+2);
以上的&a、&pa、*ptr 、&b、pa++ 、arr、*parr 、*(parr+1)、*(parr+2)都是一个指针表达式。
由于指针表达式的结果是一个指针,所以指针表达式也具有指针所具有的四个要素:指针的类型,指针所指向的类型,指针指向的内存区,指针自身占据的内存。
五、指针和数组
通过下标访问数组元素,学习了指针之后,可以通过指针访问数组的元素。在数组中,数组名即为该数组的首地址,结合指针和整数的加减,就可以实现指针访问数组元素。
(1)数组名做指针访问数组
例如:
int array[10]={0,1,2,3,4,5,6,7,8,9},value;
value=*array;
value=*(array+3);
value=*(array+4);
数组名array 代表数组本身,类型是int[10],把array 看做指针的话,它指向数组的第0 个单元,类型是int* 所指向的类型是数组单元的类型即int。array+4 是一个指向数组第4 个单元的指针,所以*(array+4)等于4。其它依此类推。
(2)指针直接访问数组
例如:
int nums[3] = {4, 5, 3};
int *p = nums, i;
for(i = 0; i < 3; i++){
printf("nums[%d] = %d\r\n", i, *(p + i));
}
输出结果为:
nums[0]=4
nums[1]=5
nums[2]=3
(3)指针访问字符数组
例如:
char music[] = "good night!", mc[60];
char *pr = mc;
int i;
for(i = 0; music[i] != '\0'; i++){
*(pr + i) = music[i];
}
printf("pr = %s, mc = %s", pr, mc);
输出结果为:pr = good night!, mc = good night!
(4)指针直接作为数组访问字符
例如:
char *pr = "good night!";
printf("%s", pr);
printf("%c", pr[0]);
printf("%d", strlen(pr));
(5)二维指针访问数组
例如:
int num[3] = {2, 3, 4}, i;
int *pr[3];
int **ppr;
for(i = 0; i < 3; i++){
pr[i] = &num[i];
}
ppr = pr;
for(i = 0; i < 3; i++){
printf("%d", **ppr);
ppr++;
}
(6)多维指针访问多维数组
例如:
定义一个二维数组
int Num[2][2] = {
{2, 0},
{3, 6}
};
此时 Num[0]、和 Num[1]各为一个数组
int *pr[2] = {Num[0], Num[1]};
用指针数pr操作一个二维数组
pr为数组 pr 的首地址,pr[0] = Num[0] = *pr,**pr = Num[0][0]
printf("Num[0][0] = %d", **pr);
指针 + 整数形式,pr+1 移动到 Num 的地址,*(pr +1) = Num[1],则**(pr + 1) = Num[1][0]
printf("Num[1][0] = %d", **(pr + 1));
*pr = Nums[0],再*pr + 1 = &Num[0][1],最后获取内容*(*pr + 1)即为 Num[0][1]
printf("nums[0][1] = %d", *(*pr + 1));
六、指针与数据结构
例如:
struct member
{
int a;
int b;
int c;
};
struct member As={20,30,40};
struct member *pr=&As;
通过指针变量改变数值
pr->a=10;
pr->b=20;
pr->c=30;
七、指针与函数
(1)指针作为函数的形参
例如:
int fun(char *);
Int a;
char str[]="good lucky";
a=fun(str);
int fun(char *s)
{
int num=0;
for(int i=0;str[i] != '\0';i++)
{
num+=*s;
s++;
}
return num;
}
(2)指针作为函数的返回值
例如:
int As;
int *sums(int x, int y){
As = x + y;
return &As;
}
int *r = sums(1, 2);
printf("*r=%d", *r);
(3)函数指针
例如:
#include <string.h>库中的函数,使用之前需要声明该函数。字符串比较函数
void check(char *x, char *y, int (*p)());
void main(){
int strcmp();
char x[] = "good";
char y[] = "lucky";
int (*p)() = strcmp;
check(x, y, p);
}
void check(char *x, char *y, int (*p)()){
if(!(*p)(x, y)){
printf("相等");
}else{
printf("不相等");
}
}
八、指针类型转换
初始化一个指针或给一个指针赋值时,赋值号的左边是一个指针,赋值号的右边是一个指针表达式。前面所举的例子中,绝大多数情况下,指针的类型和指针表达式的类型是一样的,指针所指向的类型和指针表达式所指向的类型是一样的;当指针的类型和指针表达式的类型是不一样的,指针所指向的类型和指针表达式所指向的类型是不一样的,这时就需要强制类型转换。
例如:
float f=12.3;
float *fptr=&f;
int *p;
p=(int*)&f;("强制类型转换")