C语言的指针(一)

创作不易,希望大家点点小赞和关注。接下来让我带领大家开始进行C语言难题(指针)的学习,让我们努力学习,向着美好的生活,跑步前进吧~

ccd38f4ca79f42d3b3058137b7022f11.webp

指针的简介

        首先我们需要了解指针是什么?

        我为大家总结一下两个要点来理解指针:

        1. 指针是内存中一个最小单元的编号,也就是地址

        2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量

        通俗的说:指针就是地址,但是我们经常说的指针通常指的是指针变量

一、内存和地址

        我们把内存划分为一个个的内存单元,每个内存单元的大小取1个字节(bit)。计算机能够识别、存储、运算的时候都是二进制的。

计算机中的常见单位如下:

1byte8bit
1kb1024byte
 1MB1024KB
1GB1024MB
1TB1024GB
1PB1024TB

        C语言中把地址起了新的称号:指针。

        我们可以理解为,每个内存单元,相当于一个教学楼,一个byte空间里面能放8个bit,就如同一个教室里有八个同学,每个同学是一个bit。

        每个内存单元也都有一个编号(这个编号就相当于我们平时教学楼的门牌号),有了这个内存单元的编号,CPU就可以快速找到一个内存空间。

        我们可以理解为:

        内存单元的编号==地址==指针                     

        所以我们也可以这样理解:

一个字节0xFFFFFFFF
一个字节0xFFFFFFFF
一个字节0xFFFFFFFF
..................
..................
..................
..................
一个字节0x00000002
一个字节0x00000001
一个字节0x00000000

         而CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,而因为内存中字节很多,所以需要给内存进行编址,计算机中的编址,并不是把每个字节的地址记录下来,而是通过硬件设计完成的

         计算机内有很多的硬件单元,硬件单元之间相互协调。它们是通过线来建立联系的。

ab8e5471cfac4339bb5a6084da0d14e9.png

3aeb20d1a4554df6b1a942cd71e9bc00.png

32位     --------------    32根地址线                     每根只有两态表示

64位     --------------    64根地址线                     (0,1)【电脉冲有无】

        其中2根线4种含义   32根则有2的32次方中含义(而每一种含义就是一个地址)。地址信息被下达给内存,在内存上,就可以找到该地址对应的数据,将数据在通过数据总线传入CPU内寄存器。                                          

二、指针变量

        1、取地址操作符  &

        我们可以通过&(取地址操作符)取出变量的内存起始地址,把地址可以存放到一个变量中,这个 变量就是指针变量。 

int a = 10;
int* p = &a; //这里我们对变量a,取出它的地址,可以使用&操作符。
             //a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量中,p就是一个之指针变量

&a取4个字节中的第一个字节的地址,地址较小的字节。

补充:我们打印地址使用%p进行打印,是十六进制打印的。并且前面有一个0,我们可以使用%x进行地址打印,前面则没有0.

        2、指针变量和解引用操作符  *

        那么整形指针: int * pint; 能够指向整形数据的指针。浮点型指针则是: float * pf; 能够指向浮点型数据的指针。那么这里的*是什么意思呢?

int main()
{
    int a= 0;
    int* p = &a; 
    return 0;
}

        这个*是用来说明变量p是一个指针变量。当p是一个指针变量时,在其前面加上一个*,这个*表示解引用操作符。

        我们先来看这行代码:int* p = &a;就是创建一个变量p,使他的值等于p指向的空间里的内容,也就是a的值,这时,a的值就是0。这里的*p,用的是p指向的内容中的

        这里我们通常称之为对p这个指针变量所指向的地址进行解引用,就是*p就等于它所指向的地址所表达的这个内容(这里的*p这是表示a的值也就是0),小编通常称*p为:解引用p(地址所指向地址的内容)。

        举例如下:

int main()
{
	int a = 20;
	int* p = &a;
	printf("%d\n", *p);  //20
	printf("%p\n", &a);  //0000002010B2F9E4
	printf("%p\n", p);   //0000002010B2F9E4
	return 0;
}

        3、指针变量的大小

        我们知道P(指针)也是变量,也是需要向内存申请一块空间的,这样才能存放地址。

        那么P空间大小是多少?

        答:在32平台上是4个字节,在64平台上是8个字节,这里的空间大小与P的类型无关。

        指针变量大小是多少?

        答:指针变量需要多大的空间,取决与存放的地址,地址的存放需要多大的空间,指针变量的大小就是多大。所以指针类型决定了指针进行解引用操作的时候,访问多大的空间,例如:int* 的指针解引用访问4个字节;char*的指针解引用访问的就是1个字节。

        总结就是:指针的内省决定了,对指针解引用时有多大的权限(一次能操作几个字符)。

char* 一个字节;int* 4个字节;short* 2个字节。

 三、指针+ - 整数

        在指针的+-整数中,指针类型决定了指针的步长,也就是向前或者向后走一步具体有多大的距离。我们来观察下面的代码。    

#include<stdio.h>
int main()
{
    int n = 0;
    char *pc = (char*)&n;
    int *pi = &n; 

    printf("%p\n", &n);
    printf("%p\n", pc);
    printf("%p\n", pc+1);
    printf("%p\n", pi);
    printf("%p\n", pi+1);
    return 0;
}

       运行结果如下:aa2feda235f7442b98b6eed2776aebff.png

        我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。 这就是指针变量的类型差异带来的变化。
 
        结论如上所说: 指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)
        例如:
int main()
{
	int arr[5] = { 1,2,4,5,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = &arr[0];
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

13bff57511d146a7ad20ed9febd64229.png

        我们知道数组在内存中是连续存放的,我们可以通过地址找到了数组的首地址。也就是指针指向了这个数组的首地址。通过循换i=0的时候,指向数组的首地址通过解引用来打印数组的第一个元素并且以此类推。

四、void*指针

        在指针类型中有⼀种特殊的类型是 void* 类型的,可以理解为⽆具体类型的指针(或者叫泛型指 针),这种类型的指针可以⽤来接受任意类型地址

        但是void*指针也有局限性, void* 类型的指针不能直接进⾏指针的+-整数解引⽤的运算。举例说明:

#include <stdio.h>
int main()
{
     int a = 10;
     int* pa = &a;
     char* pc = &a;
     return 0;
}
        在上⾯的代码中,将⼀个int类型的变量的地址赋值给⼀个char*类型的指针变量。编译器给出了⼀个警 告(如下图),是因为类型不兼容。⽽使⽤void*类型就不会有这样的问题。
c15705b5d1b84983af703d7eec37c9b0.png
        使用void*指针接受地址:
#include <stdio.h>
int main()
{
	int a = 10;
	void* pa = &a;
	void* pc = &a;

	*pa = 10;
	*pc = 0;
	return 0;
}
        VS编译代码的结果:
9a492bd99aaf49308f015c5ca2be5c42.png
        这⾥我们可以看到, void* 类型的指针可以接收不同类型的地址,但是⽆法直接进⾏指针运算。那么 void* 类型的指针到底有什么⽤呢?
        ⼀般 void* 类型的指针是使⽤在函数参数的部分,⽤来接收不同类型数据的地址,这样的设计可以 实现泛型编程的效果。使得⼀个函数来处理多种类型的数据。

五、const修饰指针

        变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。 但是如果我们希望⼀个变量加上⼀些限制,不能被修改,怎么做呢?这就是const的作用。                                                                                                                                                             我们需要知道,const修饰变量时,const是给予变量一个常属性(可以理解为常量属性,也就是不可修改)。

代码说明如下:

#include <stdio.h>
int main()
{
     int m = 0;
     m = 20;//m是可以修改的
     const int n = 0;
     n = 20;//n是不能被修改的
     return 0;
}
        上述代码中n是不能被修改的,其实n本质是变量,只不过被const修饰后,在语法上加了限制,只要我 们在代码中对n就⾏修改,就不符合语法规则,就报错,致使没法直接修改。(注意:C++中const修饰的n就是常量)
        但是如果我们绕过n,使⽤n的地址,去修改n就能做到了。如下:
961f2a447ccc4c2895e9821f40e81ced.png
 
        我们可以看到这⾥⼀个确实修改了,但是我们还是要思考⼀下,为什么n要被const修饰呢?就是为了 不能被修改,如果p拿到n的地址就能修改n,这样就打破了const的限制,这是不合理的,所以应该让 p拿到n的地址也不能修改n,那接下来怎么做呢?
       
        这里首先我们需要再复习一下指针知识:1、p是指针变量,里面存放了其他的变量地址;2、*p是指针所指向的对象;3、p是指针变量,p也有自己的地址&p。
        接下来,我们来看以下代码:
#include <stdio.h>
//代码1
void test1()
{
	int n = 10;
	int m = 20;
	int* p = &n;
	*p = 20;//ok?
	printf("%d ", n);  //20
	p = &m; //ok?
	printf("%d ", *p); //20
}
void test2()
{
	//代码2
	int n = 10;
	int m = 20;
	const int* p = &n;
	*p = 20;//ok?   
	printf("%d ", *p); //报错
	p = &m; //ok?
	printf("%d ", *p); //20
}
void test3()
{
	int n = 10;
	int m = 20;
	int* const p = &n;
	*p = 20; //ok?
	printf("%d ", *p); //20
	p = &m; //ok?
	printf("%d ", *p); //报错
}
void test4()
{
	int n = 10;
	int m = 20;
	int const* const p = &n;
	*p = 20; //ok? 
	printf("%d ", *p); //报错
	p = &m; //ok?
	printf("%d ", *p); //报错
}
int main()
{
	//测试⽆const修饰的情况
	test1();
	//测试const放在*的左边情况
	test2();
	//测试const放在*的右边情况
	test3();
	//测试*的左右两边都有const
	test4();
	return 0;
}
76fd7afe58f54f5bbc230072508c9398.png
 
        结论:const修饰指针变量的时候
        • const如果放在 *的左边,修饰的是 指针指向的内容,保证指针指向的 内容不能通过指针来改变。但是 指针变量本⾝的内容可变
        • const如果放在 *的右边,修饰的是 指针变量本⾝,保证了 指针变量的内容不能修改,但是指针 指向的内容,可以 通过指针改变
 

希望对大家有所帮助,有什么不了解的可以私信或者评论我哟~~~

                        蟹蟹大家的来访,你们的点赞关注都会是我接下来创作更优质文章的动力~✌🏻️

 

  • 29
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值