前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C语言—指针(1)

C语言—指针(1)

作者头像
_孙同学
发布2024-10-21 20:51:20
650
发布2024-10-21 20:51:20
举报
文章被收录于专栏:技术分享

1.内存和地址

1.1 内存

计算机中CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放到内存中。那么内存中的数据是如何高效管理的呢? 计算机中的内存如同酒店中每个房间的编号,也是把内存划分为一个个的单元,每个单元的大小取1个字节.

代码语言:javascript
复制
1byte(字节) = 8bit(比特)
1kb = 1024byte
1mb = 1024kb
1gb = 1024mb
1tb = 1024gb
1pb = 1024tb

每个内存单元相当于一个学生宿舍,每个内存单元为1字节,存放8个bit位。就好比如同学们住的是8人间,每个同学表示1个比特位。1个bit位可以储存二进制的0或1.

每个内存单元都有一个编号(这个编号就相当于每个宿舍的门牌号),有了这个编号,CPU就能很快的找到一块内存空间。

生活中我们把门牌号称作这个房间的地址,计算机中每个内存编号也是地址。 我们把这个地址叫做:指针 内存单元的编号 == 地址 == 指针

1.2 编址

CUP访问内存中的某块字节空间,就必须知道这个字节空间在内存中的哪块位置,因为内存中的字节有很多,所以就要给内存进行编址(就如同宿舍有很多需要给每个宿舍编号) 计算机内有很多的硬件单元,而硬件单元之间是要协同工作的,互相之间要进行数据的传递。 硬件与硬件是相互独立的,那么 如何进行数据的传递呢?答案是用线连起来。 而CPU和内存之间也有大量的数据进行交互,所以两者也必须用线连起来。

控制总线: 输入要干什么(比如读数据,写数据) 地址总线: 通过地址线找到内存单元中数据 数据总线: 进行数据传递

2. 指针变量和地址

2.1 取地址操作符(&)

C语言中创建变量就是向内存中申请空间 比如:

&: 取地址操作符,是个单目操作符 &a:表示取出a的地址 比如:

int 型的变量占4个字节,所以创建a变量向内存中申请了4个字节的空间 然而&a取出的是00EFFF950 &a取出的是a所占4个字节中地址较小的字节的地址

2.2 指针变量

指针变量: 指针变量是用来存放地址的,存放在指针变量中的值都会被理解为地址. 比如:

2.2.1拆分指针类型

p的类型是"int * ",我们如何理解指针的类型呢?

代码语言:javascript
复制
int a = 10;
int* p = &a;

p的左边是int* ,*说明p是指针变量,int说明p指向的类型是整型(int)类型的对象。

2.2.2解引用操作符

将地址保存起来如何使用呢? 现实生活中我们通过门牌号可以找到某个房间,即可以拿到房间中的某个东西。 C语言中我们拿到了地址(指针),就可以找到地址(指针)指向的对象。 这里我们就要介绍一种操作符解引用操作符:*

*p的意思是通过p中存放的地址找到指向的空间。 *p就是表示a变量,*p=0;就是把a改成了0;

2.3指针变量的大小

指针变量的大小取决于地址的大小 32位平台下的指针变量的大小为32个bit位,即4字节 64位平台下的指针变量的大小为64个bit位,即8个字节

比如:32位平台下

64位平台下

所以:指针变量的大小和类型是无关的,相同的平台下 指针变量的大小是相同的.

3. 指针±整数

先上一段代码,调试观察地址变化

代码语言:javascript
复制
#include<stdio.h>
int main()
{
	int a = 10;
	char* p =(char*)&a;
	int* pc = &a;
	printf("%p\n",&a);
	printf("%p\n",p);
	printf("%p\n",p+1);
	printf("%p\n",pc);
	printf("%p\n",pc+1);
	return 0;
}

可以看到代码运行结果如下:

char *类型的指针+1跳过一个字节 int * 类型的指针+1跳过4个字节 结论:指针的类型决定了指针+1向前或者向后走多大的距离

4. void * 指针

void * 类型的指针是一种特殊类型的指针,可以叫作无具体类型指针,也可以叫做泛型指针 优点: 可以接受任何类型的地址 局限性: 不能进行±处理和解引用运算

代码语言:javascript
复制
#include<stdio.h>
int main()
{
	int a = 10;
	void* p = &a;
	void* pc = &a;

	*p = 20;
	*pc = 0;

	return 0;
}

用VS运行上述代码会报错,无法直接进行指针运算

那么void * 类型的指针有什么用呢? 其实void * 类型的指针一般是用在函数的参数部分,用来接收不同类型数据的地址。

5. const 修饰指针

5.1 const修饰变量

变量是可以被修改的,如果我们不希望变量被修改该如何做呢? 先上一段代码:

代码语言:javascript
复制
#include<stdio.h>
int main()
{
	int a = 10;
	a = 20;   //a是可以被修改的
	const int p = 20;
	p = 30;  //p是不能被修改的

	return 0;
}

上述代码中的p是不能被修改的,p本身是变量,只是被const修饰加上的语法限制,我们若对p进行修改,就不符合语法规则,编译就会报错。

5.2 const 修饰指针变量
代码语言:javascript
复制
#include<stdio.h>
int main()
{
	int a = 10;
	a = 20;   //a是可以被修改的
	const int p = 20;
	p = 30;  //p是不能被修改的

	return 0;
}

如果我们非要将p的值进行修改怎么办呢? 答案是: 我们可以创建一个指针变量,将p的地址传过去,然后通过解引用对p进行修改。 这就相当于高启强对老默说:“我想吃鱼了”,高启强不会主动出手,而是让老默出手。 下来我们拿一段代码演示一下:

代码语言:javascript
复制
#include<stdio.h>
int main()
{
	int a = 10;
	a = 20;
	const int p = 20;
	printf("%d\n", p);
	 
	int* pc = &p;
	*pc = 30;
	printf("%d\n",p);
	return 0;
}

上述代码运行的结果:

这样做确实修改了p,但const修饰的本质就是希望变量不能被修改,所以我们也应该让pc拿到p的地址后对p也不能修改,该怎么做呢?

代码语言:javascript
复制
#include<stdio.h>
void test1()
{
	int n = 10;
	int m = 20;
	int* p = &n; //ok
	p = &m;  //ok
}
void test2()
{
	int n = 10;
	int m = 20;
	const int* p = &n;
	*p = 20; //err
	p = &m;  //ok
}
void tset3()
{
	int n = 10;
	int m = 20;
	int* const p = &n;
	*p = 20; //ok
	p = &m; //err
}
void test4()
{
	int n = 10;
	int m = 20;
	int const* const p = &n;
	*p = 20; //err
	p = &m; //err
}
int main()
{
	//测试无const修饰的情况
	test1();
	//测试const放在*左边的情况
	test2();
	//测试const放在*右边的情况
	test3();
	//测试*的左右都有const
	test4();

	return 0;
}

结论:const修饰指针变量的时候 (1)const放在* 的右边限制的是指针变量本身,指针变量不能再指向其他变量了,但是可以通过指针变量修改指针变量所指向的内容 (2)const放在* 的左边限制的是指针指向的内容,不能通过指针修改指针指向的内容,但是可以修改指针变量本身的值(修改的指针变量的指向)

6.指针运算

指针的基本运算有三种: 指针±整数 指针-指针 指针的关系运算

6.1 指针±整数

我们都知道数组在内存中是连续存放的,只要知道首元素的地址就可以顺藤木瓜知道后面元素的地址

代码语言:javascript
复制
int a[10] = {1,2,3,4,5,6,7,8,9,10};

先上代码:

代码语言:javascript
复制
#include<stdio.h>
int main()
{
	int arr[10]={1,2,3,4,5,6,7,8,9,10};
	int* p =&arr[0];
	int sz = sizeof(arr) / sizeof(arr[0]);
	for(int i = 0;i < sz; i++)
	{
		printf("%d ",*(p + i));//p +i 就是指针+整数
    }

	return 0;
}
6.2指针 - 指针

我们都知道日期-日期得到的是两个日期之间的天数 所以指针-指针的绝对值得到的是两指针之间的元素个数 指针-指针的前提条件是:指针与指针在同一块内存空间 应用一:

代码语言:javascript
复制
#include<stdio.h>
int main()
{
	int arr[10] = {0};
	printf("%zd\n",&arr[9]-&arr[0]);
	printf("%zd\n",&arr[0]-&arr[9]);
	return 0;
}

上述代码运行后的结果为:

应用二: 模拟strlen函数的实现

代码语言:javascript
复制
#include<stdio.h>
int my_strlen(char* str)
{
	char* start = str;
	while(*str!='\0')
	str++;
	return str - start;
}

int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);//数组名表示数组首元素的地址
	printf("%d\n",len);
	return 0;
}
6.3指针的关系运算

指针的关系运算其实就是指针和指针比较大小即地址和地址比较大小 比如:

代码语言:javascript
复制
int main()
{
	int arr[10] = {1,2,3,4,5,6,7,8,9,10};
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = &arr[0];
	while(p < arr + sz) //p小于起始地址+sz
	{
		printf("%d ",*p);
		p++;
	}
	return 0;
}

本节就讲到这吧!欲知后事如何,请听下回分解

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-10-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.内存和地址
    • 1.1 内存
      • 1.2 编址
      • 2. 指针变量和地址
        • 2.1 取地址操作符(&)
          • 2.2 指针变量
            • 2.2.1拆分指针类型
            • 2.2.2解引用操作符
          • 2.3指针变量的大小
          • 3. 指针±整数
          • 4. void * 指针
          • 5. const 修饰指针
            • 5.1 const修饰变量
              • 5.2 const 修饰指针变量
              • 6.指针运算
                • 6.1 指针±整数
                  • 6.2指针 - 指针
                    • 6.3指针的关系运算
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档