C语言——指针详解(0基础适用,3分钟熟练)

一、什么是指针

        学习 C 语言的指针既简单又有趣。通过指针,可以简化一些 C 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的。所以,想要成为一名优秀的 C 程序员,学习指针是很有必要的。

        指针也就是内存地址,指针变量是用来存放内存地址的变量。

        每个变量都会占据一定空间,存在一个地址,&运算符可以访问地址。

eg:

int a = 3;
int *p = &a;
printf("a的地址为:%p \n",p);
//结果为:a的地址为:0x7ffeeaae08d8
  • 地址:按字节为单位进行编号;
  • 指针:就是地址
  • 指针变量:用来保存地址的变量;
  • 指针的目标变量:指针指向的变量;

二、指针的一般形式

<存储类型><数据类型>  * < 指针变量名>
存储类型 : 存储类型和指针变量自身有关,和指针的目标变量无关 ;
数据类型 : 由指针的目标变量来确定 ;
指针变量名 : 保存目标变量的地址 ;
eg
        int var ;
        int * p ;         // 一个可以指向整型地址的指针变量 ;
        p = & var ;          // p 指向了 var
        p ---> 指针变量
        & var --> 指针
        var --> 指针的目标变量 ;
        ( 在不影响理解的情况下,地址、指针、指针变量统称为指针 )
  • &: 在一个变量的基础上取地址可以得到变量的地址;
  • *:在地址的基础上取*可以得到这个地址上的内容;
  • & * 互为逆运算

三、指针的初始化

<存储类型><数据类型>  * < 指针变量名>  = < 地址量 > ;
eg
        int var = 10 ;
        int * p = & var ;
        & var == p == & ( * p )
        var == * p == * ( & var )        
        & p : 表示指针变量自身的地址,和目标变量无关 ;

四、指针的大小

指针所占的字节数和操作系统有关,和数据类型没有关系 ;
        32 位 指针变量占 4 字节;
        64 位 指针变量占 8 字节;

五、空指针和野指针

  • 空指针:是指指针变量的内容为零的状态;          int *p = NULL;
  • 野指针:一个没有固定指向的指针;要避免野指针的出现;

六、指针的运算

算术运算

+ - ++ --
p + 1 --> 表示指针变量向地址值大的方向偏移一个单位
p++
++p
p-1 --> 表示指针变量向地址值小的方向偏移一个单位
p--
--p
p+q //error
p - q --> 表示两个指针之间相差几个单位
指针偏移的实际地址量:
p +/- n * (sizeof(数据类型))

关系运算

>  <  ==  !=  >=  <=
在比较的时候要保证指针的类型要一样;

赋值运算

1.将一个变量的地址赋值给一个具有相同类型的指针变量;
    eg:
        char ch;
        char *p;
        p = &ch;
2.将一个指针变量赋值给一个具有相同类型的指针;
        char *p = &ch;
        char *q = p; //p 和 q 都指向ch
3.将一个数组的首地址赋值给一个具有相同类型的指针变量;
        int arr[5] = {0};
        int *p = arr; //p指向了数组的首地址

七、指针和一维数组、字符串的关系

一维数组

int arr[32] = {0};
int *p = arr;
//数组第i个元素的地址
p + i == &arr[i] == arr + i
//数组的第i个元素
arr[i] == *(p+i) == *(&arr[i]) == *(&p[i]) == p[i] == *(arr+i)
//arr++ // error
p++ //表示p指向第二个元素的地址;

二维数组

eg:
用一级指针访问二维数组;
二维数组的理解:
int arr[2][3];
//可以看成是一个由arr[0] 和 arr[1] 两个一维数组组成;
arr[0] == *(arr + 0) //数组名 +下标 表示每个元素的地址
arr[1] == *(arr + 0)
arr[0] 是第一个一维数组:
元素: 数组名[下标] *(数组名 + 下标)
arr[0][0]==*(arr[0] + 0)== *(*(arr + 0)+0)==(*
(arr+0))[0]
arr[0][1]==*(arr[0]+1)==*(*(arr+0)+1)==(*(arr+0))[1];
arr[0][2]==*(arr[0]+2)== *(*(arr+0)+2)==(*(arr+0))
[2];
arr[1] 是第二个一维数组;
总结:
arr[i][j]:
== *(arr[i]+j) == *(*(arr+i)+j) == (*(arr+i))[j];

字符串

1.当一个指针要指向字符串的时候,字符串应该有一个存储空间来保存,
    将字符串的首地址赋值给指针变量;
    eg:
        char buf[] = "hello world";
        char *p = buf;
    eg:
        char buf[32] = {0};
        char *p = buf;
        // strcpy(p, "hello world");
        scanf("%s", p);
        //将字符串存储到p指向的buf空间中;
    eg:
        /******* error ***************
            char *p;
            scanf("%s", p);
            //相当于将输入的字符串存储到一片没有申请的空间中
            //应该让p先指向一片空间
            puts(p);
        */
        char buf[32] = {0};
        char *p = buf;
        scanf("%s", p);
2.一个指针变量初始化为字符串常量时,
    用指针来访问字符串的时候只允许读不允许修改;
    eg:
        const char *p = "hello";
        //char *p; p = "hello";
        *p = 'H'; //error
        //相当于指针p指向字符串常量"hello"的首地址, *p == 'h';
        //这个字符串保存在静态数据区的常量区,不允许修改只允许读

八、二级指针、行指针

一个存储指针的指针;
eg :
        int   var   =   10 ;
        int  * p   =  & var ;
        int  ** q   =  & p ;
        q   ==  & p ;
        * q   ==   p   ==  & var ;
        ** q   ==  * p   ==   var ;
        int  var1 ;
        * q   =  & var1 ;        
        通过指针修改目标的值 ( p 的值 )
二维数组名叫做行地址,二维数组名加一表示偏移一行的元素;
注意:
二维数组不能用二级指针指向,数据类型不一样;
二维数组可以被行指针指向;
行指针的一般形式:
<存储类型> <数据类型> (*指针变量名)[列数];
eg:
int arr[3][2] = {0};
int (*p)[2] = arr;
第i行的第j列的元素:
arr[i][j] == *(arr[i]+j) == *(*(arr+i)+j)
== (*(arr+i))[j]
== p[i][j] == *(p[i]+j) == *(*(p+i) + j)
== (*(p+i))[j]

九、指针数组

指针数组本质是数组,由若干个相同类型的指针变量组成的集合;
一般形式:
<存储类型> <数据类型> * <指针数组名>[数组的元素个数];
eg:
    int arr[3][2];
    int *parr[3] = {arr[0], arr[1], arr[2]}
    每个元素 arr[i][j]:
    *(*(parr + i) + j)
    parr: 数组名,地址常量;
    parr[i] 表示每个指针变量,占8字节;
    指针数组的大小:
    8 * 数组元素个数; //64位    
    访问指针数组可以用二级指针来访问;
eg:
    int **pp = parr;
    arr[i][j] == *(*(parr + i) + j) = *(*(pp + i) + j);

十、const和void指针

const int var;
const 修饰变量,让变量常量化;
const 修饰的局部变量可以通过指针间接修改;
eg1:
    1.不能通过指针修改目标的值,但可以修改指针的指向;
        int var = 10, var2 = 20;
        const int *p = &var; //int const *p = &var;
        p = &var2; //true
        // *p = 100; //false
    2. 不能修改指针的指向,但可以通过指针修改目标的值;
        int var = 10, var2 = 20;
        int * const p = &var; //必须初始化
        //p = &var2; //false
        *p = 100;
    3.既不能修改指针的指向,也不能通过指针修改目标的值;
        int var = 10, var2 = 20;
        //int const * const p = &var;
        const int * const p = &var;
        //p = &var2; //false
        // *p = 100; //false
一般形式为:
void *<指针变量名称> ;
对于void型的指针变量,实际使用时,一般需通过强制类型转换才能使void型指针变量得到具体变量或数组地址。在没有强制类型转换之前,void型指针变量不能进行任何指针的算术运算。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值