每一个标量都有一个内存位置,每一个内存位置都定义了可使用&运算符访问的地址,它表示了在内存中的一个地址
#include<stdio.h>
int main()
{
int var_runoob=10;
int *p; //定义指针变量
p=&var_runoob;
printf("var_runoob变量的地址: %p\n",p);
//其中%p的p是指针的缩写,%p输出十六进制的值
return 0;
}
什么是指针?
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:
type *var-name;
//type 是指指针的基类型 *用来指定一个变量是指针
如何使用指针
使用指针时会频繁进行以下几个操作:1.定义一个指针变量、2.把变量地址赋值给指针、3.访问指针变量中可用地址的值
这些都是通过使用一元运算符*来返回位于操作数所指定地址的变量的值
#include<stdio.h>
int main()
{
int var=20; //实际变量的声明
int *ip; //指针变量的声明
ip=&var //在指针变量中存储var的地址
printf("var变量的地址:%p\n",&var);
//在指针变量中存储的地址
printf("ip变量存储的地址:%p\n",ip); //ip=&var;
//使用指针访问值
printf("*ip变量的值:%d\n",*ip);//20
return 0
}
C中的NULL指针
在变量声明的时候,如果没有确切的地址可以复制,为指针变量赋一个NULL值是一个良好的编程习惯。赋给NULL值得指针被称为空指针
在大多数的操作系统上,程序不允许访问地址为0的内存,因为该内存是操作系统保留的。然而,内存地址0有特别重要的意义,它表明该指针不指向一个可访问的内存位置,但是按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。
//如需检查一个空指针,可以使用if语句
if(ptr) //如果p非空,则完成
if(!ptr) //如果p为空,则完成
C指针的算术运算
C指针是一个用数值表示的地址。因此,可以对指针执行算术运算。可以对指针进行四种算术运算:++、–、+、-
假设ptr是一个指向地址1000的整型指针,是一个32位的整数,让我们对该指针执行下列的算术运算:
ptr++
在执行上述的运算之后,ptr将指向位置1004,因为ptr每增加一次,它都将指向下一个整数位置,即当前位置往后移4字节(int类型为4个字节)。这个运算会在不影响内存位置的实际值的情况,移动指针到下一个内存位置。如果ptr指向一个地址为1000的字符(ptr不再是指针),上面的运算会导致指针指向位置1001,因为下一个字符位置是在1001.
总结:
指针的每一次递增,它其实会指向下一个元素的额存储单元
指针的每一次递减,它都会直向前一个元素的存储单元
指针在递增和递减时跳跃的字节数取决于指针所指向变量数据类型长度,比如int就是4个字节
递增一个指针
在程序中可以使用指针代替数组,因为变量指针可以递增,而数组不能递增,数组可以看成一个指针常量。
#include<stdio.h>
const int MAX=3;
int main()
{
int var[]={10,100,200};
int i,*ptr;
//指针中的数组地址
ptr=var;
for(i=0;i<MAX;i++)
{ printf("存储地址: var[%d]=%p\n",i,ptr);
printf("存储值:var[%d]=%d\n",i,*ptr);
//指向下一个位置
ptr++;}
return 0;
}
递减一个指针
和递增一个指针一样,对指针进行递减运算,即把值减去其数据类型的字节数
指针的比较
指针可以用关系运算符进行比较,如==、<和>.如果p1和p2指向两个相关的变量,比如同一个数组中的不同元素,则可对p1和p2进行大小比较
#include<stdio.h>
const int MAX=3;
int main()
{
int var[]={10,100,200};
int i,*ptr;
//指针中第一个元素的地址
ptr=var;
i=0;
while(ptr<=&var[MAX-1])
{
printf("存储地址:var[%d]=%p\n",i,ptr);
printf("存储值:var[%d]=%d\n",i,*ptr);
//指向下一个位置
ptr++;
i++;
}
return 0;
}
C指针数组
int *ptr[MAX];
在这里,把ptr声明为一个数组,由MAX个整数指针组成。因此,ptr中的每个元素,都是一个指向int值的指针。
#include <stdio.h>
const int MAX=3;
int main()
{
int var[]={10,100,200};
int i,*ptr[MAX];
for(i=0;i<MAX;i++)
{
ptr[i]=&var[i];
}
for(i=0;i<MAX;i++)
{
printf("Value of var[%d]=%d\n",i,*ptr[i]);
}
return 0;
}
也可以用一个指向字符的指针数组来存储一个字符串列表
C指向指针的指针
指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针,第二个指针指向包含实际值的位置。
一个指向地址的指针变量必须如下声明,即在变量名前放置两个星号。
int **var;
当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符
#include<stdio.h>
int main()
{
int V;
int *Pt1;
int **Pt2;
V=100;
Pt1=&V; //获取V的地址
Pt2=&Pt1; //使用运算符&获取Pt1的地址
printf("var=%d\n",V);
printf("Pt1=%p\n",Pt1);
printf("*Pt1=%d\n",*Pt1);
printf("Pt2=%p\n",Pt2);
printf("*Pt2=%d\n",*Pt2);
return 0;
}
C传递指针给函数
C语言允许传递指针给函数,只需要简单地声明函数参数为指针类型即可。
//传递一个的无符号的long型指针给函数,并在函数内改变这个值
#include<stdio.h>
#include<time.h>
void getSeconds(unsigned long *par);
int main()
{
unsigned long sec;
getSeconds(&sec);
printf("Number of seconds:%ld\n",sec);//输出实际值
return 0;
}
void getSeconds(unsigned long *par)
{
*par=time(NULL);//获取当前的秒数
return 0;
}
能接受指针作为参数的函数,也能接受数组作为参数
#include<stdio.h>
double getAverage(int *arr,int size);
int main()
{
int balance[5]={1000,2,3,17,50};//带有5个元素的整型数组
double avg;
avg=getAverage(banlance,5);//传递一个指向数组的指针作为参数
//函数声明如果需要指针就必须写指针变量,main函数中引用函数,需要写指针指向的地址,而数组本身可以看做指针常量 例如:int *ptr;int v; int r[3]={1,2,3}; ptr=&v; ptr=r;
printf("Average value is:%f\n",avg);
return 0;
}
double getAverage(int *arr,int size)
{
int i,sum=0;
double avg;
for(i=0;i<size;+i)
{
sum+=arr[i];
}
avg=(double)sum/size;
return avg;
}
C从函数返回指针
声明一个返回指针的函数
int *myFunction()
{
.
.
}
另外,C语言不支持在调用函数时返回局部变量的地址,除非定义局部变量为static变量
现在,让我们来看下面的函数,它会生成10个随机数,并使用表示指针的数组名(即第一个数组元素的地址)来返回它们,具体如下
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
/* 要生成和返回随机数的函数 */
int * getRandom( )
{
static int r[10];
int i;
/* 设置种子 */
srand( (unsigned)time( NULL ) );
for ( i = 0; i < 10; ++i)
{
r[i] = rand();
printf("%d\n", r[i] );
}
return r;
}
/* 要调用上面定义函数的主函数 */
int main ()
{
/* 一个指向整数的指针 */
int *p;
int i;
p = getRandom();
for ( i = 0; i < 10; i++ )
{
printf("*(p + [%d]) : %d\n", i, *(p + i) );
}
return 0;
}
函数指针
函数指针是指向函数的指针变量
通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数
函数指针可以像一般函数一样,用于调用函数、传递函数
函数指针变量的声明:
typedef int(*fun_ptr)(int,int)//声明一个指向同样参数、返回值的函数指针类型
#include<stdio.h>
int max(int x,int y)
{
return x>y?x:y;
}
int main(void)
{
int (*p)(int,int)=&max;//p是函数指针 &可以省略
int a,b,c,d;
printf("请输入三个数字:");
scanf("%d %d %d",&a,&b,&c);
d=p(p(a,b),c);//与直接调用函数等价,d=max(max(a,b),c)
printf("最大的数字是:%d\n",d);
return 0;
}
回调函数
函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。
#include <stdlib.h>
#include <stdio.h>
// 回调函数
void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
{
for (size_t i=0; i<arraySize; i++)
array[i] = getNextValue();
}
// 获取随机值
int getNextRandomValue(void)
{
return rand();
}
int main(void)
{
int myarray[10];
/* getNextRandomValue 不能加括号,否则无法编译,因为加上括号之后相当于传入此参数时传入了 int , 而不是函数指针*/
populate_array(myarray, 10, getNextRandomValue);
for(int i = 0; i < 10; i++) {
printf("%d ", myarray[i]);
}
printf("\n");
return 0;
}