指针基础

该文有以下内容:

1.指针是什么2.指针具有指针类型的2个意义3.野指针
4.指针运算5.指针与数组6.二级指针
7.指针数组

1.指针是什么?

官方语言:

在计算机科学中,指针( Pointer )是编程语言中的一个对象,利用地址,它的直接指向( points to )存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针"。意思是通过它能找到以它为地址的内存单元

简而言之,指针就是一个变量,而变量里面存储的地址,因此,指针就是**“地址”**,下面以代码为例子,进行演示官方语言的意思;

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

/*运行结果
20
*/

指针变量p就可以叫做指针,他就是一个对象,p的值指向存在 电脑存储器中另一个地方的值(a),所以当解引用p时,*p= 20,p利用地址,使其所指向的a变成了20;

所以p可以叫做指针,p的值是地址;

另外这里强调一下,在内存中一块空间大小是一个字节,并且用16进制编号方便人类观看(在讲述指针第二个意义时候会用到)

2.指针具有类型的2个意义

引言:

#include <stdio.h>
int main()
{
    int a = 20;
    int* intp = &a;
    char* charp = &a;
    double* doublep = &a;
    printf("%d\n", sizeof(intp));
    printf("%d\n", sizeof(charp));
    printf("%d\n", sizeof(doublep));
    return 0;
}
/*运行结果:
4
4
4
*/

你会看到结果全是4,为何?因为sizeof()测量的是指针大小,其实在32位平台上,指针的大小是4字节,在64位上,指针的大小是8字节**,其中指针大小和指针类型无关,全部是一样**,因为指针是地址,地址就是4个字节或者8个字节;

问:既然指针大小都是一样?为何要弄出这么多指针类型?下面用几个例子进行演示

意义1:指针类型决定了指针解引用操作的时候有多大的权限(能操作几个字节)
#include <stdio.h>
int main()
{
	int a = 0x44332211;/*a存储的是一个16进制的数字,16进制中的一个数字代表4位,2个数字代表一个字节*/
	int b = 0x44332211;
	int c = 0x44332211;
	int* intp = &a;
	char* charp = &b;
	short* shortp = &c;
	*intp = 0;
	*charp = 0;
	*shortp = 0;
	printf("%#x\n", a);
	printf("%#x\n", b);
	printf("%#x\n", c);
	return 0;
}
/*
运行结果:
0
0x44332200
0x44330000
*/

a变为0 ,改变了四个字节

b变为0x44332200,变了一个字节

c变为0x44330000,变了两个字节;

  • 因为指针intp是整型指针,具有四个字节权限.charp是字符指针,具有一个字节权限,shortp是短整型指针,具有两个字节权限;
意义2:指针类型决定了指针向前向后能走多远的路程;

举例:

#include <stdio.h>
int main()
{
    int a = 10;
    int* intp = &a;
    char* charp = &a;
    double* doublep = &a;
    printf("%p",&a);
    printf("%p",intp+1);
    printf("%p",charp+1);
    printf("%p",doublep+1);
    return 0;
}
/*运行结果:
00B6FB44
00B6FB48
00B6FB45
00B6FB4C
*/

可以看到整型指针intp加一以后地址向后移动了4个字节;

​ 字符指针charp加一以后地址向后移动了一个字节;

​ 双精度指针doublep加一以后向后移动了八个字节;

所以这就是不同的指针类型的意义,决定了你可以网前后移动的路程,这主要用于数组的访问,以及数据结构

3.野指针

野指针形成有三个原因:

  • 指针指向的位置不可知;
  • 指针访问数组时候越界;
  • 指针指向的空间被释放;
原因1: 指针指向的位置不可知;

我们知道在创建一个变量的时候如果不初始化,那么变量是一个随机值

  • 例如; int a; 那么a是一个随机值;同理,创建指针的时候如果不初始化就会是一个随机地址(这很危险,以为你不知道他会指向谁)

代码示例:

#include <stdio.h>
int main()
{
 int a = 10;
 int* p;
 *p = 20;  /*这就很恐怖,因为你不知道指针p是个什么*/
 return 0;
}
这个时候就是野指针
原因2:指针访问数组时候越界;

|----|----|----|----|----|----|----|----| (假设左边是数组格子,可以看到有8个格子,索引是0 1 2 3 4 5 6 7)

代码演示:

#include <stdio.h>
int main()
{
 int num[8] = {2,4,5,8,4,3,13,31};
 int* p = num;    /*数组名是首元素地址*/
 for (int i = 0;i<=8;i++)
 {
     printf("%d\n", *(p+i));    /*利用解引用指针访问数组*/
 }
 return 0;
}

/*
运行结果:
2
4
5
8
4
3
13
31
-858993460
*/

我们可以看到,会多出一个负数,这是就是因为当i = 8的时候,p+i就代表往后移动32个字节,即8个整形,但是往后移动7个后就到达终点了;当i是8就会越界;越界以后,就会指向我们不知名的一个随机地址

因此现在的p也是野指针
原因3:指针指向的空间被释放;

我们在学习指针之前,知道在创建函数的时候,函数一旦被调用完毕,函数的所有参数就会被销毁;创建的空间就会被释放

#include <stdio.h>
int* test()
{
    int a = 20;
    return &a;
}

int main()
{
    int* p = test();
    printf("%d", *p)    
	return 0;
}

这样子写就会有问题.因为test()参数里面的a在出函数时候就被销毁了,a的空间就还给系统了;这里执行很可能还是20,这个空间的内容还没有清除;在c语言陷阱中有这个解释(有的时候可能已经清除),总之不建议这样写

因此现在的p也是野指针

总结: 如何避免野指针?

  • 指针最好初始化
  • 小心指针越界
  • 指针指向空间被释放或者不知道怎么初始化指针就赋值NULL
  • 指针使用之前检查有效性

4.指针运算

1.指针+ - 运算(前面也演示过一部分)
#include <stdio.h>
int main()
{
	int num[15] = { 21,2,1,4,5,3,6,9,5,4,7,8,2,1,12 };
	int i = 0;
	int* p = num;
	for (i = 0; i < 15; i += 2)
	{
		printf("%d ", *(p + i));     /*因为p是整型指针,所以加2就是跳8个字节*/
	}
	return 0;
}

/*
运行结果:
21 1 5 6 5 7 2 12
*/
2.指针减去指针(在同一块空间)
#include <stdio.h>
int main()
{
    int arr[10] = {12,30,14,12,2,3,4,32,12,16};
    printf("%d", &arr[9] - &arr[2]);
    return 0;
}

/*
运行结果:
7
*/

image-20210323165946655

所以指针减去指针就是指针间的元素个数

出题: 自己编写函数,用三种方法测量字符串的长度,不准用strlen();

image-20210323172355704

方法一是计数法 方法二是函数递归,但是也运用了指针加减整数 方法三是指针相减

3.指针关系运算

通过大小比较进行运算

#include main()
{
    float values[5];
    float* p = NULL;
    for(p = &values[5];p>&values[0];)
    {
        *--p = 0;
    }
    return 0;
}

image-20210323173709546

当指针p等于 &values[5]时候已经越界了,但是不怕,因为我们只是比较的地址,并没有弄值出来

所以按照代码意思就可以全部初始化为0了

也有人这样写

#include main()
{
    float values[5];
    float* p = NULL;
    for(p = &values[5];p>&values[0];p--)
    {
        *p = 0;
    }
    return 0;
}

但是这样不推荐,因为最后终止条件是p的地址小于等于&values[0],也就是说最后p跑到数组前面去了,
他们的过程是这样:
在这里插入图片描述

而C语言标规定:

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

5.指针与数组

一般来说数组名就是首元素地址,但是有例外;

  1. &arr_name & + 数组名形式代表的是整个数组,只是显示的时候,显示的是首元素地址,一下代码可以区分

    1. #include <stdio.h>
      int main()
      {
          int arr[10] = {0};
          printf("%p", arr);
          printf("%p", arr+1);
          printf("%p", &arr);
          printf("%p", &arr+1);
          return 0;
      }
      
      003AFE90
      003AFE94
      003AFE90
      003AFEB8
      
      
      • 运行结果:
      • 003AFE90
        003AFE94
        003AFE90
        003AFEB8

      image-20210323175059314

可以看到&arr+1地址增加了40个字节,也就是数组大小

  1. sizeof(arr_name)时候代表的是整个数组;
#include <stdio.h>
int main()
{
    int num[20] = {0};
    printf("%d", sizeof(num));
    return 0;
}

/*
运行结果:
80
*/

6.二级指针

存放变量地址的是以及指针,那么类推,存放指针地址的就是二级指针

那么二级指针表示方法是什么呢?

就是两颗* 后面还有三颗 * ,四颗 * …依次类推

#include <stdio.h>
int main()
{
    int a = 20;
    int* p = &a;
    int** pp = &p;
    return 0;
}

7.指针数组

注意我说的是指针数组,是数组,是存放指针的数组

#include <stdio.h>
int main()
{
    int a = 20,b = 30,c = 10,d = 40;
    int* num[4] = {&a,&b,&c,&d};
    for(int i = 0;i<4;i++)
    {
        printf("%d\n",*num[i]);
    }
    return 0;
}

image-20210323180353001

  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
以下是C语言指针基础知识点: 1.指针的定义和初始化 指针是一个变量,其值为另一个变量的地址。定义指针变量时需要指定指针指向的变量类型,然后使用*运算符来声明指针变量。指针变量的初始化可以为其赋值为另一个变量的地址,也可以将其初始化为NULL。 2.指针的运算 指针可以进行加、减、自增、自减等运算。指针加上一个整数n时,指向的地址会增加n个存储单元的大小,减去一个整数n时,指向的地址会减少n个存储单元的大小。 3.指针的比较 指针可以进行相等、大于、小于等比较运算。当两个指针指向同一个变量时,它们相等;当两个指针指向同一数组中的不同元素时,它们可以进行大小比较。 4.指针的类型转换 指针可以进行类型转换,但需要注意转换后的指针类型必须与指向的变量类型相同,否则可能会导致程序出错。 5.指针和数组 数组名本身就是一个指针指向数组的第一个元素。可以使用指针来访问数组中的元素,也可以将指针作为函数参数来传递数组。 以下是一个例子,演示了指针的定义、初始化、运算和比较: ```c #include <stdio.h> int main() { int a = 10; int *p = &a; // 定义指向整型变量a的指针p printf("a的值为:%d\n", a); // 输出:a的值为:10 printf("p指向的变量的值为:%d\n", *p); // 输出:p指向的变量的值为:10 printf("p的值为:%p\n", p); // 输出:p的值为:0x7ffeeb5f8a2c printf("p+1的值为:%p\n", p+1); // 输出:p+1的值为:0x7ffeeb5f8a30 printf("p的地址为:%p\n", &p); // 输出:p的地址为:0x7ffeeb5f8a28 printf("p和&a的比较结果为:%d\n", p == &a); // 输出:p和&a的比较结果为:1 return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

捕获一只小肚皮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值