指针
视频资源
【嵌入式C语言】 【精准空降到 11:22】 https://www.bilibili.com/video/BV1RW411G7cr/?p=29&share_source=copy_web&vd_source=8af85e60c2df9af1f0fd23935753a933&t=682
视频传送门
指针+修饰符
const
默认使用的char *
或者int *
这样的指针类型是可以修改内容的
为了限制这样修改内容的操作,所以C语言又加了一些修饰符,便于程序员之间的沟通~
- const称之为常量、只读类型的关键字【不能变】
我们可以用const修饰指针变量,如下所示,有很多种方式
#include <stdio.h>
typedef char *pchar;
int main()
{
printf("Code Begin\n");
// 一个字节、只读方式
// 1 == 2
const char *p1 = NULL; // 1 【建议】
char const *p2 = NULL; // 2 【不建议】
// 3 == 4
char *const p3 = NULL; // 3 【建议】
char *p4 const = NULL; // 4 【不建议】
const char *const *p5 = NULL;
printf("Code End\n");
return 0;
}
const char * 指针常量
- 指针常量是指向一个常量的指针。这意味着通过这个指针不能修改它所指向的数据,但是指针本身可以改变,也就是说可以将其指向另一个地址。
那我们现在定义一个
const char *p1 = NULL;
怎么分析这个p1呢?
-
先看核心节点
p1
,再看左边有个*
,说明p1是个指针类型的变量,什么类型呢?再看左边,哦~是char *
类型的指针,这个指针的指向位置是可以任意变的,内容也是可以修改的,再往左看,多了个const
修饰,意思就是,指向的内存地址可以改变,但是只能读取,不能修改!!! -
理论上是这样,只读不能修改!但是这是个建议字符,如果要强制修改的话,也是有方法进行修改内容的。
-
我们认为const修饰的内容不能变即可。
我们就可以把const char *
类型的变量理解成一个字符串,就理解成双引号定义的字符串常量。
在这个例子中,*p 是不可更改的(即不能通过 p 来改变它所指向的值),但是你可以改变 p 使其指向其他的整数变量。
char *const 常量指针
- 常量指针是一个指针,一旦初始化后,就不能再改变它所指向的地址,但是可以通过这个指针来修改它所指向的数据(除非该数据本身被声明为常量)。
注意这个变量是 char *const p
先看const p,这样定义的是p不可变,指向位置是不变的,不能就是
在这个例子中,p 必须始终指向在初始化时指定的同一位置,但是如果你不使用 const 修饰 *p,你仍然可以改变 *p 的值。
- 这种类型,在单片机嵌入式领域一般用作硬件资源的定义,比如说LCD,或者显卡
总结
- 指针常量:指针可变,内容不变
- 常量指针:指针不变,内容可变
理解这两者之间的区别对于正确使用C/C++中的指针非常重要,尤其是在需要确保某些数据不会被意外修改的情况下。
CPU与缓存之间的数据传输一样,就比如CPU去访问LCD的一个固定的地址,然后这个地址的内容是不断进行刷新的,屏幕上显示的内容就是不断刷新的
const char *const *
指向内容不能变,指向地址也不能变。
- 一般用于ROM
const举例
代码
#include <stdio.h>
typedef char *pchar;
int main()
{
printf("Code Begin\n");
const unsigned char *p1 = "hello world!"; // 双引号是什么,在内存的表现形式是什么?就是在内存空间中展开每一个字符
printf("the one is 0x%x\n", *p1);
printf("ASCII 'h' is 0x%x\n", 'h'); //'h'是字符,在内存中是ASCII码,也就是一个整数
*p1 = 'a'; // 修改 //这里出现了段错误,指针指向的内容被非法访问了
printf("changed string is %s\n", p1);
printf("Code End\n");
return 0;
}
终端输出:
把const去掉,其实是发生了段错误,指针指向的内容被非法访问了。
- 双引号是整形常量,是不能变的,不能被写,在操作系统中,如果要改变这个内容,就是非法的,产生段错误
Segmentation fault
我们看看这两种定义方式有什么不同
const unsigned char *p1 = "hello world!";
char buf[] = {"hello world!"};
char *指向的是某一段
的,不能修改
数组buf就是连续的空间,是可变的,可以修改
#include <stdio.h>
typedef char *pchar;
int main()
{
printf("Code Begin\n");
const unsigned char *p1 = "hello world!"; // 双引号是什么,在内存的表现形式是什么?就是在内存空间中展开每一个字符
char buf[] = {"hello world!"};
printf(buf);
printf(" string is %s\n", buf);
char *p2 = buf;
*p2 = 'H'; //第一个字符修改成大写H
printf("changed string is %s\n", p2);
printf("Code End\n");
return 0;
}
输出
PS E:\CProject> cd 'e:\CProject\output'
PS E:\CProject\output> & .\'pointer_const.exe'
Code Begin
hello world! string is hello world!
changed string is Hello world!
Code End
PS E:\CProject\output>
这样就没有问题了,第一个字符修改成大写H
const内存属性
- 内存操作的大小
- 内存的变化性,可读可写
man 3 printf
在linux下有个printf的手册
在终端输入man 3 printf
就能看到
volatile
volatile
防止优化指向内存地址,对指针指向内容的修饰
volatile char *p;
typedef
typedef char *name_t; // name_t是一个指针类型的名称,指向了一个char类型的内存空间
name_t abc;