指针详解

指针是什么?
指针是编程语言中的一个对象,利用地址,它的值直接指向存在电脑中的另一个地方的值,由于通过地址能找到所需的变量单元。可以说地址指向该变量单元。因此,将地址形象化的称为“指针”。
为什么存在指针?
举个例子:我们的土地管理方式是,国家分为n个省,每个省又划分为m个市,每个市又划分为k个县。。。这样做可以很好的管理,通过一个地址就找到对应的位置。
同样,计算机的内存可以分成许多小的单元,每个单元就对应一个独一无二的地址,一个地址标识一块空间。
指针的大小?
对于32位的机器,在这里就有2的32次方个地址。

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000001
...
11111111 11111111 11111111 11111111

一个地址是由32个0或者1组成的二进制序列,8个位需要一个字节,所以一个地址需要4个字节来存储,即一个指针变量的大小应该是4个字节。
同理,在64位机器上,一个指针变量的大小应该是8个字节。

指针的类型?
变量有不同的类型,指针也同样。

char *pc = NULL;//char *类型的指针是为了存放char类型变量的地址。
int  *pi = NULL;//int  *类型的指针是为了存放int类型变量的地址。
short*ps = NULL;//short*类型的指针是为了存放short类型变量的地址。

指针的类型决定了对指针解引用的时候有多大的权限。

指针的算数运算

char a[20];  
int *ptr=(int *)a; //强制类型转换并不会改变a 的类型
ptr++; 

指针ptr 的类型是int*,它指向的类型是int,它被初始化为指向整型变量a。最后一句中,指针ptr被加了1,编译器是这样处理的:它把指针ptr 的值加上了sizeof(int),在32 位程序中,是被加上了4,因为在32 位程序中,int 占4 个字节。由于地址是用字节做单位的,故ptr 所指向的地址由原来的变量a 的地址向高地址方向增加了4 个字节。由于char 类型的长度是一个字节,所以,原来ptr 是指向数组a 的第0 号单元开始的四个字节,此时指向了数组a 中从第4 号单元开始的四个字节。

&和*?
&是取地址运算符,&a的运算结果是一个指针,指针的类型是a的类型加上加上*,指针所指向的类型是a 的类型,指针所指向的地址嘛,那就是a 的地址。
*是间接运算符,*p的结果是p所指向的东西,它的类型是p指向的类型,它所占用的地址是p所指向的地址。

int a=12; int b; int *p; int **ptr;  
p=&a; //&a 的结果是一个指针,类型是int*,指向的类型是  
//int,指向的地址是a 的地址。  
*p=24; //*p 的结果,在这里它的类型是int,它所占用的地址是  
//p 所指向的地址,显然,*p 就是变量a。  
ptr=&p; //&p 的结果是个指针,该指针的类型是p 的类型加个*,  
//在这里是int **。该指针所指向的类型是p 的类型,这  
//里是int*。该指针所指向的地址就是指针p 自己的地址。  
*ptr=&b; //*ptr 是个指针,&b 的结果也是个指针,且这两个指针  
//的类型和所指向的类型是一样的,所以用&b 来给*ptr 赋  
//值就是毫无问题的了。  
**ptr=34; //*ptr 的结果是ptr 所指向的东西,在这里是一个指针,  
//对这个指针再做一次*运算,结果是一个int 类型的变量。  

NULL指针
它作为一个特殊的指针变量,表示不指向任何东西,要使一个指针变量为NULL,你可以给它赋一个零值。NULL指针概念非常有用,它提供了一种方法表示某个特定的指针目前并未指向任何东西。对指针进行解引用可以获得它指向的值,NULL指针并未指向任何东西,因此对一个NULL指针进行解引用操作是非法的。

指针和数组
联系:(1)当一个指针变量被初始化成数组名时,就说该指针变量指向了数组(数组名就是该数组的首元素地址)
(2)数组的下标访问操作可以通过指针实现:a[i]转换为*(a+i)

int arr[10]={0,1,2,3,4,5,6,7,8,9},data;  
data=arr[0]; //也可写成:data=*array;  
data=arr[3]; //也可写成:data=*(array+3);  
data=arr[4]; //也可写成:data=*(array+4);  

区别:(1)数组名在传参时会退化为指针,指针不会。
(2)内存中数组是一块连续开辟的空间,指针只占有一个指针类型的大小空间。
(3)数组可以通过下标直接访问,指针需要进行计算间接访问。
(4)数组名具有常性,不能进行++操作,指针可以。

复习指针和数组相关运算

    一维数组
    int a[] = {1, 2, 3, 4};
    printf("%d\n", sizeof(a));          //16  表示整个数组大小      
    printf("%d\n", sizeof(a+0));        //4   数组首元素
    printf("%d\n", sizeof(*a));         //4   数组首元素
    printf("%d\n", sizeof(a+1));        //4   数组第二个元素的地址
    printf("%d\n", sizeof(a[1]));       //4   第二个元素
    printf("%d\n", sizeof(&a));         //4   数组的地址
    printf("%d\n", sizeof(&a+1));       //4   数组后的地址
    printf("%d\n", sizeof(&a[0]));      //4   第一个元素的地址
    printf("%d\n", sizeof(&a[0]+1));    //4   第二个元素的地址
    字符数组
    char arr[] ={'a','b','c','d','e','f'};
    printf("%d\n", sizeof(arr));        //6     首元素地址
    printf("%d\n", sizeof(*arr));       //1     首元素
    printf("%d\n", sizeof(arr[1]));     //1     第二个元素
    printf("%d\n", sizeof(&arr));       //4     数组地址
    printf("%d\n", sizeof(&arr+1));     //4     数组后的地址
    printf("%d\n", sizeof(&arr[0]+1));  //4     第二个元素地址
    printf("%d\n", strlen(arr));        //随机值    遇到\0才停下来
    printf("%d\n", strlen(arr+0));      //随机值     
    printf("%d\n", strlen(*arr));       //err      a的ASCII码97 不允许访问
    printf("%d\n", strlen(arr[1]));     //err        b
    printf("%d\n", strlen(&arr));       //随机值
    printf("%d\n", strlen(&arr+1));     //随机值
    printf("%d\n", strlen(&arr[0]+1));  //随机值

   *char *p = "abcdef";
   printf("%d\n", sizeof(p));           //4       字符a的地址    
   printf("%d\n", sizeof(p+1));         //4       字符b的地址
   printf("%d\n", sizeof(*p));          //1       p是字符指针,对p解引用可以访问一个字节
   printf("%d\n", sizeof(p[0]));        //1        p[0]==*(p+0) 第一个元素
   printf("%d\n", sizeof(&p));          //4
   printf("%d\n", sizeof(&p+1));        //4
   printf("%d\n", sizeof(&p[0]+1));     //4       b的地址

   printf("%d\n", strlen(p));           //6 
   printf("%d\n", strlen(p+1));         //5       p+1为b的地址
   printf("%d\n", strlen(*p));          //err
   printf("%d\n", strlen(p[0]));        //err
   printf("%d\n", strlen(&p));          //随机值
   printf("%d\n", strlen(&p+1));        //随机值
   二维数组
   int a[3][4] = {0};         
   printf("%d\n", sizeof(a));           //48   整个数组大小
   printf("%d\n", sizeof(a[0][0]));     //4    第一个元素大小
   printf("%d\n", sizeof(a[0]));        //16   第一行的大小
   printf("%d\n", sizeof(a[0]+1));      //4    第一行第二个元素
   printf("%d\n", sizeof(a+1));         //4    第二行地址
   printf("%d\n", sizeof(&a[0]+1));     //4    第二行地址   a[0]为第一行数组名
   printf("%d\n", sizeof(*a));          //16   第一行的地址解引用  假想为一唯数组 第一行的大小
   printf("%d\n", sizeof(a[3]));        //16   sizeof内部的表达式不参与运算  第四行数组名虽不存在但不影响计算 因为不会放入

一级指针

#include <stdio.h>
int main()
{
    int a = 10; 
    int *p = &a; 
    printf("%p\n",p);
    return 0;
}

此处p为一个一级指针,p中存储的是变量a的地址
这里写图片描述
由此可见,指针就是变量, 用来存放地址的变量。
二级指针

int main()
{
    int a=10;
    int *pa=&a;
    int **ppa=&pa;
    printf("%p\n",a);
    printf("%p\n",pa);
    return 0;
}

指针类型
通过指针引用一个变量,必须知道该数据的类型,才能按存储单元的长度以及数据的存储形式正确的取出该数据。即在定义指针变量时必须指定基类型。
这里写图片描述
指针表达式解析
一般左值代表空间,右值代表内容,左值不一定能够放在等号左边
这里写图片描述

&ch;     //不能放在等号左边,能放在等号右边
cp;      //能放在等号左边,能放在等号右边
&cp;     //不能放在等号左边,能放在等号右边(表示cp的地址)
*cp+1;   //不能放在等号左边(常量不能被赋值),放在等号右边相当于字符b
*(cp+1) ;//表示a后面的空间,能放在等号左边
++cp;    //在c语言中,一个变量进行前置加加或者后置加加都不能放在等号左边
cp++;  
*++cp;  //和表达式*(cp+1)效果一样
*cp++;  //能放到等号左边,能放到等号右边
++*cp;  //能放到等号右边,相当于字符b
(*cp) ++;//能放到等号右边,相当于字符a,但此时ch指向b
++*++cp; //表示ch下一块地址的东西自增

指针数组
指针数组是数组, 是一个存放指针的数组。
eg:

int *arr1[10];
char *arr2[4];
char **arr3[5];

数组指针
eg:int (*p)[10];
p先和*结合, 说明p是一个指针变量, 然后指着指向的是一个大小为10个整型的数组。 所以p是一个指针, 指向一个数组, 叫数组指针。
函数指针

void(*P)(int char)=test1;
void(*P)(int char)=test1;//函数指针
void(*parr[3])(int char)=(test1,test2,test3);//函数指针的数组
void(*(*ptr[3])(int char)=&arr;//指向函数指针的指针
#include<stdio.h>
int ADD(int a,int b)
{
    return(a+b);
}

int main()
{
    int (*p)(int,int)=ADD;//定义一个返回值类型为整形,有两个参数,且参数为整形的函数指针*p
    printf("%d\n",p(1,3));//通过函数指针来访问其所指向的函数Add
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值