一、指针简介
1、什么是指针?
指针是一个变量,其值为另一个变量的地址,即内存地址的直接地址。做个比喻,假设将你比作变量,那么你身份证上的住址就是指针,根据你身份证上的地址可以找到你,即指针存的内容是你身份证上的住址。
指针有两种含义,一是作为数据类型,二是作为实体。
指针作为实体,是一个用来保存一个内存地址的计算机语言中的变量。指针一般出现在比较底层的程序设计语言中,如C语言。高级语言如Java一般避免用指针,而是引用。指针作为数据类型,可以从一个函数类型、一个对象类型或者一个不完备类型中导出。从中导出的数据类型称之为被引用类型(referenced type)。指针类型描述了一种对象,其值为对被引用类型的实体的引用。
指针可以用来有效地表示复杂的数据结构,可以用于函数参数传递并达到更加灵活使用函数的目的。使C语言程序的设计具有灵活、实用、高效的特点。
2、*与&操作符
取地址运算符(&):取变量的地址,即返回操作数的内存地址。&var 读作"var 的地址"。
间接寻址运算符(*):返回操作数所指定地址的变量的值。
3、如何使用指针?
使用指针时会频繁进行以下几个操作:
- 定义一个指针变量;
- 把变量地址赋值给指针;
- 访问指针变量中可用地址的值。
这些是通过使用一元运算符 * 来返回位于操作数所指定地址的变量的值。
#include <stdio.h>
int main (){
int var = 20; /* 实际变量的声明 */
int *ip;/* 指针变量的声明 */
ip = &var; /* 在指针变量中存储 var 的地址 */
printf("Address of var variable: %x\n", &var );
/* 在指针变量中存储的地址 */
printf("Address stored in ip variable: %x\n", ip );
/* 使用指针访问值 */
printf("Value of *ip variable: %d\n", *ip );
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Address of var variable: bffd8b3c
Address stored in ip variable: bffd8b3c
Value of *ip variable: 20
4、指针运算符
C 指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-。
假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,让我们对该指针执行下列的算术运算:
ptr++
在执行完上述的运算之后,ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 个字节。这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。如果 ptr 指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。
5、NULL 指针
在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。NULL 指针是一个定义在标准库中的值为零的常量。请看下面的程序:
#include <stdio.h>
int main (){
int *ptr = NULL;
printf("ptr 的值是 %x\n", ptr );
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
ptr 的值是 0
二、指针与数组
1、区别
C++/C程序中,指针和数组在不少地方可以相互替换着用,让人产生一种错觉,以为两者是等价的。数组是连续分配一串单元,数目开始定义的时候就必须固定下来,看起来整洁,但是写的程序是死程序,容易浪费内存。指针是存放一个地址值,表示指向某一个单元,可以用指针来索引单元。数组可以完成栈,堆,树等等的操作,它在编程时候的好处是非常的灵活,在构建思路的时候有很大的灵活性。
2、指针数组与数组指针
指针数组:array of pointers,即用于存储指针的数组,也就是数组元素都是指针。
数组指针:a pointer to an array,数组名本身就是一个指针,指向数组的首地址,即指向数组的指针。
int* a[4] ; /* 指针数组
表示:数组a中的元素都为int型指针
元素表示:*a[i] *(a[i])是一样的,因为[]优先级高于*/
int (*a)[4] ;/* 数组指针
表示:指向数组a的指针
元素表示:(*a)[i] */
#include <stdio.h>
int main(void)
{
int c[4]={1,2,3,4};
int *a[4]; //指针数组
int (*b)[4]; //数组指针
b=&c;
//将数组c中元素赋给数组a
for(int i=0;i<4;i++)
{
a[i]=&c[i];
}
//输出看下结果
printf("%d",*a[1]);//输出2就对
printf("%d",(*b)[2]);//输出3就对
return 0;
}
三、指针与字符串
C语言中没有特定的字符串类型,我们通常是将字符串放在一个字符数组中。字符数组归根结底还是一个数组,使用指针的方式来输出字符串:
#include <stdio.h>
int main(){
char str[] = "http://www.jianshu.com/";
char *pstr = str;
int len = strlen(str), i;
//使用*(pstr+i)
for(i=0; i<len; i++){
printf("%c", *(pstr+i));
}
printf("\n");
//使用pstr[i]
for(i=0; i<len; i++){
printf("%c", pstr[i]);
}
printf("\n");
//使用*(str+i)
for(i=0; i<len; i++){
printf("%c", *(str+i));
}
printf("\n");
return 0;
}
四、指针与函数
函数指针:本身是一个指针,指向一个函数入口地址,通过该指针可调用其指向的函数,使用函数指针可实现回调函数。
#include <stdio.h>
void inc(int *val)
{
(*val)++;
}
int main(void)
{
void (*fun)(int *);
int a=3;
fun=inc;//fun是一个函数指针
(*fun)(&a);
printf("%d" , a);
}
指针函数:本身是一个函数,其返回值是一个指针。
#include <stdio.h>
float *find(float(*pionter)[4],int n);//函数声明
int main(void)
{
static float score[][4]={{60,70,80,90},{56,89,34,45},{34,23,56,45}};
float *p;
int i,m;
printf("Enter the number to be found:");
scanf("%d",&m);
printf("the score of NO.%d are:\n",m);
p=find(score,m-1);
for(i=0;i<4;i++)
printf("%5.2f\t",*(p+i));
return 0;
}
float *find(float(*pionter)[4],int n)/*定义指针函数*/
{
float *pt;
pt=*(pionter+n);
return(pt);
}
五、指针与引用
- 引用总是指向一个对象,没有所谓的 null reference 。所有当有可能指向一个对象也有可能不指向对象则必须使用指针。由于C++ 要求 reference 总是指向一个对象所以 reference要求有初值.由于没有所谓的 null reference 所以在使用前不需要进行测试其是否有值,而使用指针则需要测试其的有效性。
- 指针可以被重新赋值而reference则总是指向最初或地的对象。
- 必须使用reference的场合.Operator[]操作符由于该操作符很特别地必须返回 [能够被当做ssignment 赋值对象] 的东西,所以需要给他返回一个 reference。
- 其实引用在函数的参数中使用很经常。格式如下:
void Get***(const int& a){} //这样使用了引用又可以保证不修改被引用的值
区别:
- 指针是一个实体指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名;
- 引用使用时无需解引用(*),指针需要解引用;
- 引用只能在定义时被初始化一次,之后不可变;指针可变;
- 引用“从一而终”;
- 引用没有 const,指针有 const,const 的指针不可变;
- 引用不能为空,指针可以为空;
- sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;typeid(T) == typeid(T&) 恒为真,sizeof(T) == sizeof(T&) 恒为真,但是当引用作为成员时,其占用空间与指针相同(没找到标准的规定);
- 指针和引用的自增(++)运算意义不一样。