指针(初阶)

简介: 指针的学习是C语言中的一个重要部分,对于指针我们必须认真对待并加以理解,接下来就让小编给大家细细讲解一下

前言


指针的学习是C语言中的一个重要部分,对于指针我们必须认真对待并加以理解,接下来就让小编给大家细细讲解一下


1.指针是什么

所谓指针大家可以理解为是地址,在我们的内存中我们会对内存的每个进行编号,以进行合理管理。



image.png

而在我们的口语中的指针其实是指针变量,用来存放内存空间地址的变量。这里我给大家演示一下


#include <stdio.h>
int main()
{
  int a = 0;
  int* p = &a;//这里我们定义了一个指针p指向了变量a
  //接下来我们分别对a和p的地址进行打印
  printf("%p\n", &a);
  printf("%p", p);
  return 0;
}

结果如下:



image.png

这里我们可以看到,指针变量p存储的的确是变量a的地址。


而对于该编号,往往是机器决定的,比如说你的电脑是32位机器,那么该编号便从00000000


00000000000000000000000000000000开始到11111111111111111111111111111111结束,这也就导致了此时指针变量的大小是4字节的,对于64位机器,同理,此时的指针变量大小就是8字节


2.指针类型和指针的使用

2.1指针的使用

对于指针的使用我需要和大家简单的介绍两个操作符&和*


&操作符又叫做取地址符,该作用就是取出变量的地址,而*操作符又叫做解引用操作符, 该作用和取地址符作用相反,它的作用是得到一个变量的地址,通过解引用从而得到该变量的存储内容。通过这两个操作符,我们就可以对指针变量进行初步的使用


#include <stdio.h>
int main()
{
  int a = 0;
  int* p = &a;//这里我们定义了一个指针p指向了变量a
  *p = 2;//通过解引用,改变a的值
  printf("%d", a);
  return 0;
}

这里我们得到的结果是:

image.png


2.2 指针类型

指针变量和普通变量一样,都有着不同的类型,但是对于指针类型它的意义和普通变量却有着极大的不同。


a.指针类型决定了,指针解引用操作的时候一次访问几个字节, 比如char*指针只访问一个字(其他解引用与其类型大小一致)


b.指针类型决定了指针的步长(指针+1跳过几个字节)(字符指针+1跳过一个字节,int类型跳过4个字节)


  总的来说指针类型的意义在于:指针的不同类型,其实提供了不同的视角去观看和访问内存


可能直接讲概念大家可能有点不理解,下面我给大家实操一下,大家可以结合理解一下


#include <stdio.h>
int main()
{
  int a = 0x11223344;
  char* p = (char*)&a;//这里我们用p指向变量a
  * p = 02;
  printf("%x", a);
  return 0;
}

结果如下:


image.png


这里由于是小端存储,所以改变的是最后一个字节,这里我们就可以充分看出指针类型的作用。还有之前给大家介绍的大小端的判断方式,这里我给大家讲解一下

int main()
{
  int b = 1;
  char* q = (char*)&b;
  if (*q == 1)
  {
  printf("小端");
  }
  else
  {
  printf("大端");
  }
  return 0;
}

这里我们采用的就是char类型的指针来达到对应的效果,对于常量1,在大端存储中以16进制存储达到的效果是00000001,而在小端存储中则是01000000,这就导致了我们用char类型指针访问的第一个字节的不同,这样就可以很好的判断大小端存储


3.野指针

野指针就是指针指向的位置是不可知的(随机的,不确定的,没有限制的)


成因:1.指针未初始化(里面是随机值;*P=20)


           2.指针越界访问


           3.指针指向空间的释放


野指针的存在是具有巨大的危害的,而对此我们要尽量规避,以免此类事件的发生


规避方法:1.指针初始化(指针未使用的时候可以把它指向空指针NULL,但是不可以直接使用)


                  2.小心越界


                 3.指针指向空间释放,及时指向NULL


                 4.避免返回局部变量地址


                 5.指针使用前检查其有效性


4.指针运算

4.1  指针+-整数

 指针+-整数一般是在一段连续的空间使用的,不然该解引用就会导致非法访问


实例展示:


#include <stdio.h>
int main()
{
  int arr[10] = { 0 };
  int* p = arr;
  int i = 0;
  for (i = 0; i < 10; i++)
  {
  *(p + i) = i;//这里指针p还是指向数组首地址
  }
  for (i = 0; i < 10; i++)
  {
  printf("%d ", arr[i]);
  }
}


结果如下:

image.png



4.2 指针-指针

指针-指针的绝对值(前提:指针指向同一个空间)得到的是两个指针之间的元素个数,这里我们可以封装一个判断字符串长度的函数,给大家演示一下


#include <stdio.h>
int my_strlen(char* str)
{
  char* first = str;//首先定义一个指针指向数组首元素
  while (*first != '\0')
  {
  first++;
  }//让指针first到达字符串数组的最后一个元素
  return first - str;
}
int main()
{
  char arr[] = "abcdefg";
  int ret = my_strlen(arr);
  printf("%d", ret);
  return 0;
}

结果如下:

image.png


4.3 指针的关系运算

对于指针关系运算该前提仍然要求该在连续的一块空间中


实例展示:

#include <stdio.h>
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9 ,10};
  int* p = arr;
  for (p = arr; p < &arr[10]; p++)
  {
  printf("%d ", *p);
  }
}

结果如下:



image.png

对于指针关系的比较这里,我们可以分别对指向第一个元素之前的那个内存位置的指针进行比较


也可以与指向数组最后一个元素后面那个内存位置比较,这样都可以到达打印数组的效果,但该在C语言中是有规定要求的。


标准规定:允许指向数组元组的指针与指向数组最后一个元素后面那个内存位置比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较(越界但是没有访问)


5.二级指针

这里对于二级指针该概念,我给大家举例说明


int a=10;

int *pa=&a;*表示pa是指针

int * *ppa=&pa;int *一起表示ppa的类型,*表示ppa是指针

这里ppa就是二级指针,*ppa就是pa

而二级指针的作用则是存储一级指针的地址,以到达对一级指针变量的控制。


这里我给大家演示一下二级指针的具体用法


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

结果如下:


image.png


6. 指针数组

指针数组指的是存储指针的数组


该类型声明如下:


指针类型 数组名[数组大小]={存储的是地址};


这里我们使用指针数组进行一维数组模拟二维数组,用法如下:


#include <stdio.h>
int main()
{
  int a[] = { 1,2,3,4 };
  int b[] = { 2,3,4,5 };
  int c[] = { 3,4,5,6 };
  int* arr[3] = { a,b,c };
  int i = 0;
  for (i = 0; i < 3; i++)
  {
  int j = 0;
  for (j = 0; j < 4; j++)
  {
    printf("%d ", arr[i][j]);
  }
  printf("\n");
  }
  return 0;
}

结果如下:


image.png

相关文章
|
网络协议 网络架构
UDP包的大小与MTU
在进行UDP编程的时候,我们最容易想到的问题就是,一次发送多少bytes好?当然,这个没有唯一答案,相对于不同的系统,不同的要求,其得到的答案是不一样的,我这里仅对像ICQ一类的发送聊天消息的情况作分析,对于其他情况,你或许也能得到一点帮助:首先,我们知道,TCP/IP通常被认为是一个四层协议系统,包括链路层,网络层,运输层,应用层.
4110 0
|
缓存 Linux
linux swap交换分区详解
linux swap交换分区详解
链表oj题讲解(2)之链表中间节点
首先拿到题目,小编的第一个思路是先求得链表的长度,然后将寻求长度的2分之一加1处,这里我们就需要我们用单个指针实现了。
154 1
|
存储 算法
数据结构之堆的介绍(二)
今天小编给大家带来的是数据结构中有关与堆的介绍,但由于堆的结构实际上是一棵完全二叉树,所以在介绍堆前小编需要给大家先讲解一下树以及二叉树的基本概念,以便大家后续理解。
235 0
数据结构之堆的介绍(二)
|
JavaScript 前端开发
Vue 2.x折腾记 - (17) 基于Ant Design Vue 封装一个配置式的表单组件
写了个类似上篇搜索的封装,但是要考虑的东西更多。 具体业务比展示的代码要复杂,篇幅太长就不引入了。
538 0
|
存储
数据结构之队列的实现(一)
在介绍栈之后,这里小编就给大家带来的另外一种特殊的线性结构——队列。
180 0
|
存储 安全 编译器
C++之引用的介绍
相信大家都看过水浒传,里面的英雄人物除了自己的名字外都有自己的称号,比如:花和尚——鲁智深,豹子头——林冲……,这里我们发现我们人都有自己的别名,那么变量呢,其实变量也有给自己取别名的功能,那么小编今天就给大家介绍一下这个功能。
215 0
|
编译器
类和对象(三)
今天小编给大家介绍的就是类最后的相关内容,希望大家好好学习理解,以加深大家对类的理解。
82 0
|
存储 算法 Java
Java之GC机制(2)
Java之GC机制(2)
135 0
Java之GC机制(2)
Confluence 6 包括从其他 Confluence 服务器上来的通知
Confluence workbox 可以显示从 Confluence 服务器上发送过来的消息。 让我们假设你有 2 个 Confluence 服务器, ConfluenceChatty 和 ConfluenceQuiet。
1195 0