C语言——初阶指针

目录:
1. 指针是什么 
2. 指针和指针类型 
3. 野指针 
4. 指针运算 
5. 指针和数组 
6. 二级指针 
7. 指针数组

1.指针是什么:
1. 指针是内存中一个最小单元的
编号,也就是
地址
2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。
总结:指针就是地址,口语中说的指针通常指的是指针变量。
内存被划分为一个个小的内存单元。
一个基本的内存单元的大小时一个字节。
内存单元的编号-->地址。
          内存
地址(内存单元的编号)
       一个字节
0✖FFFFFFFE
             ...
...
      一个字节
0✖00000000
内存单元的编号是如何产生的呢?

机器分为30位或64位:                      (
电信号转换为数字信号
32位 - 32跟地址线 - 物理电线 - 通电 - 高电频/低电频 - 1/0
64位......
电信号产生的数字信号二进制序列作为内存单元的编号-->内存单元的地址:
00000000000000000000000000000000
00000000000000000000000000000001
......
11111111111111111111111111111111
共有2^32个地址,每个地址标一个字节,可以给4G的空闲内存编址(32位下)(2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB)。
所以:
内存单元 -- 编号 -- 地址 -- 指针
补充:
4个二进制位表示一个十进制位
比如:10
二进制:00000000 00000000 00000000 00001010
十进制:00 00 00 0a

指针变量:是用来存放地址的变量(
存放在指针中的值都被当成地址处理)。
int a = 10;
int *pa = &a;
//pa是指针变量,pa的类型是int*
//pa中存放的是a的地址

解析:
(指针变量是一个变量,里面存放的是a的地址)
因为a是int型,所以&a拿到的是a的4个字节中的第一个字节的地址(每个字节都有自己对应的地址)
对int *的理解:
pa的类型是int*,对于int*可分开理解,*告诉我们pa是个指针变量,而前面的int告诉我们pa指向的变量a的类型是int(pa指向一个整型变量)。
前面说到一个地址占32bit=4byte,所以一个指针变量pa占4个字节。

因为一个地址占4个字节,要存起来这个地址,需要一个4字节的内存空间。(32位下,64位需要8个字节)
总结:
指针是用来存放地址的,地址是唯一标示一块地址空间的。 
指针的大小在32位平台是4个字节,在64位平台是8个字节。

2.指针和指针类型
指针类型的大小都是一样的,都占4个字节(double*、int*、short*......)
那么指针类型的意义是什么呢?
int a = 0x11223344;
int *pa=&a;
*pa = 0;
//得出的结果是让a中的4个字节存的数都变为0;
//11 22 33 44-->00 00 00 00

但是,当为:
int a = 0x11223344;
char *pa = &a;
*pa = 0;
//得出的是让a中的4个字节中的1个字节内存的数变为0;
//11 22 33 44-->11 22 33 00

总结:
1.指针类型的意义:
指针的解引用:
指针类型决定了在解引用的时候一次能访问几个字节(指针的权限)
int* --> 4
char* --> 1
couble* --> 8
2.指针类型决定了指针向前走一步或向后走一步,走多大距离(单位字节)
int a = 10;
int *pa = &a;
char *pc = &a;
printf("%pn",pa);
printf("%pn",pa+1);//打印的地址+1跳过4个字节(int型)
printf("%pn",pc);
printf("%pn",pc+1);//打印的地址+1跳过1个字节(char型)

强制转换:
int arr[10]={0};
char* p=(char*)arr;
return 0;

强制类型转换后大小并没有变,还是4个字节,只是一次访问由4字节改为1字节。

3.野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

野指针的成因:
1.指针未初始化
int *p;//局部变量未初始化,是随机值
*p = 20;

补充:全局变量未初始化默认为0,局部变量未初始化为随机值。
2.指针越界访问
int arr[10] = { 0 };
int *p = arr;
int i = 0;
for(i = 0;i <= 10;i ++)
{
     *p = i;
      p ++;
}

  arr
   0
   1
   2
   ...
   9
数组下标
   0
   1
   2
   ...
   9
  10
当i=10的时候,*p=10,但是数组中一共只有0~9这10位,i=10时多出一位,这一位就造成数组越界,会出现野指针。

3. 指针指向的空间释放
int* test()
{
   int a=100;
   return *a;
}
int main()
{
   int *p=test();
   printf("%d",*p);
   return 0;
}

函数运行时开辟内存存放100,有一个对应的地址,返回的时候返回的就是当时存放100的那个内存地址,p指向的就是当时存放a的地址,但是a有生命周期,函数调用完出去的时候,a就销毁了(不是说这块孔家不在了,而是
这一块内存空间的使用权限
不再属于
test函数了),但是p里面还是存了这一块空间的地址,p就成为了一个野指针。(如果这个空间里存的东西没改过,去访问的时候里面还是a的值,但是如果被用过了,就不是a的值了。)
4.如何规避野指针
1. 指针初始化 
(当p不知道指向谁的时候就给它附一个空指针 int *p = NULL ;)
2. 小心指针越界 
3. 指针指向空间释放即使置NULL 
4. 避免返回局部变量的地址 
5.
指针使用之前检查有效
if(pa != NUULL)
{
}
//检查指针pa的有效性,当pa不等于空指针的时候再去使用它


4.指针运算
4.1指针+-运算
指针+-整数i跳过i个相应的类型个数。
for(i=0;i<sz;i++)
{
    printf("%d",*(p+i));
}
//sz为数组中元素的个数
//用指针依次打印数组中的元素个数
int *p = arr + sz - 1;//p指向arr的最后一个元素
for(i=0;i<sz;i++)
{
    printf("%d",*p--);
}
//反向打印数组中的元素

4.2指针-指针
int main()
{
    int a[10]={0};
    printf("%dn",&a[9]-&a[0]);
    printf("%dn",&a[9]-&a[0]);
    return 0;
}

输出结果为
                  9
                 -9
解析:
a
0
0
...
0
0
0
下标
0
1
...
7
8
9
地址由低到高排列,当两个地址相减的绝对值,得到的是两个地址之间元素的个数(不是字节个数)

大地址-小地址--正数;小地址-大地址--负数

注:指针-指针的前提是:两个指针指向同一块空间。
补充:
实现my_strlen的
三种方法
1.计数器
2.递归
3.指针-指针
这次展开讲指针-指针的方法
int my_strlen(char *s)
{
    char *strart = s;//strart指向首元素
    while(*s !='')
    {
     s++;
    }
    return s-start;//指针的减法,课算出两地址之间元素个数
}

4.3指针的关系运算
#define N_VALUES 5 
float values[N_VALUES]; 
float *vp;
for(vp = &values[N_VALUES]; vp > &values[0];) 
{    
    *--vp = 0; 
}
//把数组中的元素都赋值为0;

values:                                               vp⬇(通过for循环中--向左赋值)
0
0
0
0
0
下标
0
1
2
3
4
5

这里for中的vp指针并未越界,只是指向数组外下标为5的空间,并没有访问这个空间。(vp中只是存了上图中下标为5的地址)

上面的代码可以进行简化:
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--) 
{    
    *vp = 0; 
}

注意:
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

5.指针和数组
指针——地址(4字节/8字节)
数组——一组相同类型的数据(多大自己创建)
数组和指针是两种概念
我们可以通过指针访问数组。

6.二级指针
int main()
{
    int a = 10;
    int *pa = &a;
    //pa中存放a的地址,占用4字节,而pa本身也有自己的地址
    int **ppa = &pa;
    //ppa就是二级指针,指向pa,ppa也有自己的地址
    int ***pppa = &ppa;
    //pppa是三级指针

对二级指针中*的
理解
int *pa=&a;  中 * 表示pa是一个指针,由于a的类型是int,pa指向的对象是int型,所以用int。
int **ppa=&pa;  中  **中的第二个*表示ppa是指针变量,第一个*和int组成int*,由于pa的类型是int*,ppa指向pa,所以这里是int*。
(指针指向谁,定义的时候就写什么类型,指向int就写int,指向int*就写int*)
*ppa找到的是pa,所以**ppa找到的就是a,可以通过**ppa修改a的值
**ppa=20;//把a中的值改为20

(*ppa=pa,**ppa=*pa=a)
总结:
二级指针是用来存放一级指针变量的地址的。

7.指针数组
指针数组是数组——存放指针的数组
int a = 10;
int b = 20;
int c = 30;
int *arr[5] = { &a , &b , &c };//存放整型指针的数组

arr中存放的都是int*型。
上面arr中不完全初始化,未定义的初始化为0,是空指针。
*(arr[0]) = a;
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>