一、什么是指针
从根本上看,指针是一个值为内存地址的变量,如int类型的值是整数
char类型的值是字符
指针变量的值就是内存地址
二、 指针的用法
把指针作为函数参数使用,以及为何这样用
假设一个指针变量名是ptr,可以编写如下语句:
ptr = &pooh; //把pooh的地址赋给ptr
对于这条语句,我们说ptr“指向”pooh。
ptr和&pooh 的区别是ptr是变量,而&pooh是常量。或者,ptr是可修改的左值,而&pooh是右值。
当然还可以把ptr指向别处:
ptr = &bah; //把ptr指向bah,而不是pooh
现在ptr的值是bah的地址。
要创建指针变量,先要声明指针变量的类型。假设想把ptr声明为储存int类型变量地址的指针,就要使用下面的新运算符。
2.1 间接运算符 *
假设已知 ptr指向bah,
如下所示:
ptr = &bah;
然后使用间接运算符* (indirection operator)找出储存在 bah 中的值,该运算符有时也称为解引用运算符(dereferencing operator)。不要把间接运算符和二元乘法运算符(*)混淆,虽然它们使用的符号相同,但语法功能不同。
val = *ptr;//找出ptr指向的值
语句ptr = &bah;和val = *ptr;放在一起相当于下面的语句;
val = bah
我的理解:
指针变量里面存放的是内存地址,既然是存放,那就有对于的取出操作,怎么取出?就在指针变量前面加上*即可!
小总结:
地址运算符:&
后面跟一个变量名时,&给出该变量的地址
示例:
&nurse表示变量nurse的地址
地址运算符:*
后跟一个指针名或地址时,*给出存储在指针指向地址上的值
示例:
nurse = 22;
ptr = &nurse; //指向nurse的指针
val = *ptr; //ptr指向的地址上的值赋给val
以上三条语句的结果是把22赋给val
2.2 声明指针
声明指针变量时,必须指定指针所指向变量的类型。咋听着这么绕呢?
用我的理解来说就是:
指针的变量类型要和指针所指向变量的类型一样,指针值的变量是啥类型,指针变量就是啥类型
例如:
int * pi; // pi是指向int类型变量的指针
char * pc; // pc是指向char类型变量的指针
float * pf, * pg; // pf、pg都是指向float类型变量的指针
类型说明符表明了指针所指向对象的类型,星号(*)表明声明的变量是一个指针。int * pi;声明的意思是pi是一个指针,*pi是int类型
这个图是从上往下看,能简单理解下使用指针的过程
*和指针名之间的空格可有可无。通常,程序员在声明时使用空格,在解引用变量时省略空格。
2.3 使用指针在函数间通信
#include <stdio.h> void interchange(int * u,int * v);//声明在函数里传入一个int类型指针u和int类型指针v int main(void) { //解决交换函数的问题 int x = 5, y = 10; printf("Originally x = %d and y = %d.\n", x, y); interchange(&x,&y); //把地址发送给函数 printf("Now x = %d and y = %d.\n",x ,y); return 0; } void interchange(int * u, int * v) { int temp; temp = *u; *u = *v; *v = temp; }
程序分析
首先看函数调用,
interchange(&x,&y);这里的两个参数,不是x和y的值,而是x和y的地址
后来就是通过一个temp变量进行变量的地址交换
小总结:
变量:名称、地址和值
编写程序时,可以认为变量有两个属性:名称和值(还有其他性质,如类型,暂不讨论)。
计算机编译和加载程序后,认为变量也有两个属性:地址和值。地址就是变量在计算机内部的名称。
简而言之,普通变量把值作为基本量,把地址作为通过&运算符获得的派生量,而指针变量把地址作为基本量,把值作为通过*运算符获得的派生量。
三、指针和数组
指针提供一种以符号形式使用地址的方法。因为计算机硬件非常依赖于地址,指针在某种程度上把程序员想要传达的指令以更尽皆机器的方式表达。
使用指针能让程序更有效率
flizny == &flizny[ 0]; //数组名是该数组首元素的地址
flizny和sflizny[0] 都表示数组首元素的内存地址(&是地址运算符)。两者都是常量,在程序的运行过程中,不会改变。但是,可以把它们赋值给指针变量,然后可以修改指针变量的值
#include <stdio.h> #define SIZE 4 int main(void) { short dates[SIZE]; short * pti; short index; double bills[SIZE]; double * ptf; pti = dates;//把数组地址赋值给指针 ptf = bills; for (index = 0;index < SIZE; index++) { printf("pointers+%d : %p %p\n",index,pti + index,ptf+index); } return 0; } pointers+0 : 000000000061FE00 000000000061FDE0 pointers+1 : 000000000061FE02 000000000061FDE8 pointers+2 : 000000000061FE04 000000000061FDF0 pointers+3 : 000000000061FE06 000000000061FDF8
我们的系统中,地址按字节编址,short类型占用2字节,double类型占用8字节。在C中,指针加1指的是增加一个存储单元。
对数组而言,**这意味着把加1后的地址是下一个元素的地址,而不是下一个字节的地址,这是为什么必须声明指针所指向对象类型的原因之一。**只知道地址不够,因为计算机要知道储存对象需要多少字节(即使指针指向的是标量变量,也要知道变量的类型,否则*pt就无法正确地取回地址上的值。
对于上图的理解:
在C语言中,指针加1是增加一个存储单元,因为pti指针的类型是short类型,所以指针+1,其值每次递增两个字节
指针的值是它所指向对象的地址。地址的表示方式依赖于计算机内部的硬件。许多计算机(包括PC和 Macintosh)都是按字节编址,意思是内存中的每个字节都按顺序编号。这里,一个较大对象的地址(如double类型的变量)通常是该对象第一个字节的地址。
在指针前面使用*运算符可以得到该指针所指向对象的值。
指针加1,指针的值递增它所指向类型的大小(以字节为单位)。
data + 2 == &data[2]; //相同地址
*(data + 2) == data[2]; //相同的值
容易混淆的概念
*(dates + 2) //dates第3个元素的值
*dates + 2 //dates的第一个元素的值加2
实例
#include <stdio.h> #define MONTH 12 int main(void) { int days[MONTH] = {31,28,31,30,31,30,31,31,30,31,30,31}; int index; for (index = 0; index < MONTH; index++) { printf("Month %2d has %d days.\n",index+1,*(days + index)); } return 0; }
四、函数、数组和指针
假设要编写一个处理数组的函数,该函数返回数组中所有元素之和,待处理的是名为marbles的int类型数组。应该如何调用该函数?也许是下面这样:
total = sum(marbles);//可能的函数调用
那么,该函数的原型是什么?记住,数组名是该数组首元素的地址,所以实际参数marbles是一个存储int类型值的地址,应把它赋给一个指针形式参数,即该形参是一个指向int的指针
int sum(int * ar); //对应的原型
sum()从该参数获得了什么信息?它获得了该数组首元素的地址,知道要在该位置上找出一个整数,注意,该参数并未包含数组元素个数的信息。第一种方法是,在函数代码中写上固定的数组大小;
实例
#include <stdio.h> #define SIZE 10 int sum(int ar[],int n); int main(void) { int marbles[SIZE] = {20,10,5,39,4,16,19,26,31,20}; long answer; answer = sum(marbles,SIZE); printf("The total number of marbles is %ld.\n",answer); printf("The size of marbles is %zd bytes.\n",sizeof marbles); return 0; } int sum(int ar[], int n) //数组的大小是? { int i; int total = 0; for ( i = 0; i < n; i++) total += ar[i]; printf("The size of ar is %zd bytes.\n", sizeof ar);//ar是一个指向 return total;//sum函数会返回一个数组所有元素之和 }
输出结果为:
The size of ar is 8 bytes. The total number of marbles is 190. The size of marbles is 40 bytes.
注意,marbles的大小是40字节。这没问题,因为marbles内含10个int类型的值,每个值占
4字节,所以整个marbles的大小是40字节。
但是,ar才8字节。这是因为ar并不是数组本身,它是一个指向marbles数组首元素的指针。
我们的系统中用8字节储存地址,所以指针变量的大小是8字节(其他系统中地址的大小可能不是8字节)。
ar是一个指向marbles数组首元素的指针,利用C中数组和指针的特殊关系,可以用数组表示法来表示指针ar。