0
点赞
收藏
分享

微信扫一扫

C语言初阶指针详解《上篇》

目录

一、指针是什么

二、指针和指针类型

1. 指针+-整数

2. 指针的解引用

3.练习

三、野指针

1.野指针成因 

2.规避野指针的有效方法

四、指针运算

1.指针+-整数

2.指针-指针

3.指针的关系运算

五、指针和数组

1.测试通过数组下标和指针访问数组元素 的地址:

2.尝试用指针遍历数组并打印数组元素:

 3.指针访问数组具体的某个元素

六、二级指针

七、指针数组 

八、结语


 

一、指针是什么

理解指针的两个点:

什么是内存?

什么是指针变量?

#include<stdio.h>

int main()
{
    int a = 10;//在内存种开辟一块空间
    //将a的地址存放到怕中
    int* pa = &a;//拿到的是a的4个字节中第一个字节的地址
    *pa = 20;//通过pa访问a的值并将a的值改为20
    return 0;
}

一颗*说明pa是指针变量

int说明pa指向的对象a是int类型的

 ⚠️ 

注意:指针就是变量,用来存放地址的变量(存放在指针中的值,都被当成地址处理)

现在有问题:

对于32位的机器,假设有32分地址线,那么,假设每根地址线在寻址的时候产生一个电信号,正电/负电,也就是1或者0。

那么32根地址线产生的地址就会是:

那64位的机器,64根地址线产生的地址会有多大空间?

可以自己尝试计算一下!

⚠️ 

注意:

        1️⃣指针是用来存放地址的,地址是唯一标识一块地址空间的。

        2️⃣指针的大小在32位平台是4个字节,在64位平台是8个字节。


二、指针和指针类型

1. 指针+-整数

#include<stdio.h>

int main()
{
    int arr[10] = { 0 };
    //数组名是数组首元素的地址
    int * p = arr;
    char * pc = arr;
    printf("%p\n", p);//打印p的地址
    printf("%p\n", p + 1);//打印p+1的地址
    printf("%p\n", pc);//打印pc的地址
    printf("%p\n", pc + 1);//打印pc+1的地址
    return 0;
}

         字符指针加1,相当于跳过了1个字符。

         整形指针加1,相当于跳过了1个整型。

         因为变量p和变量PC的类型不同,才会产生加4和加1的区别。

2. 指针的解引用

1️⃣char类型的指针

int main()
{
    //a是16进制数字
    int a = 0x11223344;//4个字节
    char * pc = &a;
    *pc = 0;
    return 0;
}

2️⃣ int 类型的指针

int main()
{
    //a是16进制数字
    int a = 0x11223344;//4个字节
    int * pa = &a;
    *pa = 0;//将a的值改为0
    return 0;
}

例如:char*的指针解引用就只能访问一个字节,而int*的指针解引用可以访问4个字节。

3.练习

int *指针的写法

#include<stdio.h>

int main()
{
    int arr[10] = { 0 };
    int* p = arr;
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        //指针变量+1指向数组后一个元素
        *(p + i) = 1;//将数组所有元素改为1
    }
    return 0;
}

 如果是整形的指针,一个整型的操作完之后跳过一个整形。(也就是一个整形)

char *指针的写法

#include<stdio.h>

int main()
{
    int arr[10] = { 0 };
    char *p = arr;
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        *(p + i) = 1;
    }
    return 0;
}

如果是char类型的指针,一个字节操作完之后跳过一个字节,而且操作的时候是一个字节,一个字节初始化的 


三、野指针

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

1.野指针成因 

  • 指针未初始化
#include<stdio.h>

int main()
{
    //这里的p就是一个野指针
    int* p;//p是一个局部的指针变量,局部变量不初始化的话,默认的是随机值
    //申请一块空间才可以去访问,而p并没有申请一块空间
    *p = 20;//非法访问内存
    return 0;
}
  • 指针越界访问
#include<stdio.h>

int main()
{
    int arr[10] = { 0 };
    int* p = arr;
    int i = 0;
    for (i = 0; i <= 10; i++)
    {
        //当指针指向的范围超出了数组arr的范围时,就是越界访问,p就是野指针
        *(p + 1) = i;
    }
    return 0;
}

  • 指针指向空间释放
#include<stdio.h>

int* test()
{
    int a = 10;
    return &a;
}

int main()
{
    int* p = test();
    *p = 20;
    return 0;
}

2.规避野指针的有效方法

  • 指针初始化
#include<stdio.h>

int main()
{
    //如果不知道p应该初始化为什么地址的时候,直接初始化为NULL
    int* p = NULL;
    //明确知道初始化的值
    int a = 10;
    int* ptr = &a;
    return 0;
}

  • 小心指针越界

C语言本身是不会检查数据的越界行为的,所以我们自己要保证它不会越界。

  • 指针指向空间释放及时置NULL

  • 指针使用前检查有效性

检查指针有效性的办法:

#include<stdio.h>

int main()
{
    int* p = NULL;
    //判断p是不是空指针
    if (p != NULL)
    {
        *p = 10;
    }
    return 0;
}

四、指针运算

1.指针+-整数

#include<stdio.h>

#define N_VALUES 5//定义符号

int main()
{
    float values[N_VALUES];//值是5
    float* vp;
    //指针+-整数:指针的关系运算
    for (vp = &values[0]; vp < &values[N_VALUES];)
    {
        //语法支持可以省略调整部分
        //vp<5
        *vp++ = 0;
    }
    return 0;
}

打印数字元素 

#include<stdio.h>

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;//数组名是首元素的地址
	int* pend = arr + 9;
	while (p <= pend)
	{
		printf("%d ", *p);
		p++;//指向下一个元素
	}
}

2.指针-指针

#include<stdio.h>

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    printf("%d\n", &arr[9] - &arr[0]);
    return 0;
}

指针-指针的前提:

两个空间指向同一空间

#include<stdio.h>

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    char c[5];
    //指针和指针相减的前提是:
    //两个指针指向同一空间
    printf("%d\n", &arr[9] - &c[0]);
    return 0;//会有警告
}

用指针-指针求字符串长度

#include<stdio.h>

int my_strlen(char * arr)
{
	char* strat = arr;
	while (*arr != '\0')
	{
		arr++;
	}
	return arr - strat;
}
int main()
{
	char arr[] = "hehe";
	my_strlen(arr);
	printf("%d\n", my_strlen(arr));
	return 0;
}

3.指针的关系运算

#include<stdio.h>

#define N_VALUES 5

int main()
{
	float values[N_VALUES];
	float* vp;
	for (vp = &values[N_VALUES]; vp > &values[0];)
	{
		*--vp = 0;
	}
	return 0;
}

简化写法:

#include<stdio.h>

#define N_VALUES 5

int main()
{
	float values[N_VALUES];
	float* vp;
	for (vp = &values[N_VALUES]; vp >= &values[0]; vp--)
	{
		*vp = 0;
	}
	return 0;
}

⚠️

注意:

        简化写法实际上,在绝大部分的编译器上是可以顺利完成任务的,然而,我们还是应该避免这样写,因为标准并不保证它可行。


五、指针和数组

  • 一个简单的例子说明数组名是数组首元素的地址

1.测试通过数组下标和指针访问数组元素 的地址:

#include<stdio.h>

int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%p <==> %p\n", &arr[i], p + i);
	}
	return 0;
}

 

可以看到通过数组下标访问的地址和指针访问的地址打印出来的是一样的 

2.尝试用指针遍历数组并打印数组元素:

#include<stdio.h>

int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		//将指针指向的下标元素置为i
		*(p + i) = i;
	}
	for (i = 0; i < 10; i++)
	{
		//打印数组元素
		printf("%d ", *(p + i));
	}
	return 0;
}

 3.指针访问数组具体的某个元素

#include<stdio.h>

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int* p = arr;//数组名
    printf("%d\n", arr[2]);
    printf("%d\n", p[2]);//p[2]-->*(p+2)
    //[] 是一个操作符 2和arr是两个操作数
    //a+b
    //b+a
    printf("%d\n", 2[arr]);
    printf("%d\n", arr[2]);

    //arr[2]-->*(arr+2)-->*(2+arr)-->2[arr]

    //arr[2]<==>*(arr+2)<==>*(p+2)<==>*(2+p)<==>*(2+arr)==2[arr]
    //2[arr]<==>*(2+arr)
    return 0;
}


六、二级指针

指针变量也是变量,是变量就有地址。

那指针变量的地址存放到哪里? 这就是二级指针

#include<stdio.h>

int main()
{
    int a = 10;
    int* pa = &a;//pa是指针变量,一级指针
    //后一颗*说明ppa是指针
    //前一颗*说明ppa指向的对象是pa,而pa的整体叫int*
    //ppa就是一个二级指针变量
    int** ppa = &pa;//pa也是变量,&pa取出pa在内存中的起始地址
    return 0;
}

 a的地址存放在pa中,pa的地址存放在ppa中。

pa是一级指针,而ppa二级指针。


七、指针数组 

指针数组是指针还是数组?

答案是数组,是存放指针的数组。

#include<stdio.h>

int main()
{
    int arr[10];//整形数组 - 存放整形的数组就是整形数组
    char ch[5];//字符数组 - 存放的是字符
    //指针数组 - 存放指针的数组
    int* arr[10];//整形指针数组
    char* ch[10];//字符指针指针数组
    return 0;
}


八、结语

 肝了3天终于是完成了✌️ ✌️ ✌️ 

        这次总结了初阶指针的内容,大家可以在评论区给我的文章提提意见,好坏都可以。有什么不懂得问题也可以在评论区问我,只要是我会的都会依次解答。这次的文章涉及的数组,如果感兴趣的话大家可以去我的主页浏览,绝对不会让你们失望的。本篇文章还是比较长的,感谢大家可以看到这里,愿大家都能拿到想要的offer。

       最后我脸皮稍微厚一下下,希望大家可以给我的文章点赞并收藏我,小小的一个赞,对我是很有帮助的。原创不易,还请三连,期待下次与各位在知识的海洋里一起畅游。

 

 

举报

相关推荐

0 条评论