C语言 #字符指针

.NET(C#) 中使用指针 在 .NET 中,C# 默认情况下是一种安全的、不支持直接使用指针的语言。然而,C# 支持在特定情况下使用不安全代码(即可以使用指针的代码)。使用不安全代码意味着放弃了 C# 和 .NET 运行时提供的一些安全保障,因此必须谨慎使用。不安全代码可能会增加程序出错的风险,特别是与内存管理相关的错误,如堆损坏、内存泄漏等。指针操作通常只在性能至关重要的情况下使用,或者在需要与系统级别或非托管代码互操作时使用。 阅读详情


前言

路漫漫其修远兮,武将上下而求索;

以例子为导向来分析字符指针的中存的是什么,以及常量字符串在内存中是如何存储的。


一、指针概念简述

想要详细地了解指针的概念可以戳此链接(详细的万字讲解):C语言 底层逻辑详细阐述指针(一)万字讲解 #指针是什么? #指针和指针类型 #指针的解引用 #野指针 #指针的运算 #指针和数组 #二级指针 #指针数组_c语言底层逻辑-优快云博客

本文就简单地回顾指针的一些基础知识:

1、指针本身就是地址,内存单元的编号为地址,而地址又称为指针;指针在口语上认为是指针变量、是用来存放地址的变量,每个地址都唯一地标识内存中一块内存空间;

2、指针的大小是固定的4byte\8byte (32位平台或者64位平台)

3、指针是有类型的,其类型不会决定其在内存中占据的空间,而是决定了当解引用此指针变量时有多少字节的访问内存的权限;以及指针变量+、-整数的步长

4、指针的运算

二、字符指针

顾名思义,字符指针就是指针变量指向的对象是字符类型,即指针变量的类型为 char* ;

单个字符的指针创建,代码如下:

#include<stdio.h>

int main()
{
	char ch = 'a';
	char* p = &ch;//此*代表着变量p 为指针变量
	*p = 'b';

	return 0;
}

字符指针可以存放一个字符变量的地址,那么字符串的地址该如何存放呢?

先来看一下整型的例子: 

int a = 10 ;

int b = a + 2;

"a+2" 是一个表达式的话,那么此表达式就一定有自己的值,然后再将此值赋给b ;

注:表达式有两个属性:值属性 和 类型属性

即 "a+2" 的结果为12,那么12就为其值属性;2 为int 类型,变量a 为int 类型,那么 "a+2"此表达式 的类型为int 类型。

eg. char * p = "abcdefg" ;

若将字符串"abcdefg"当作表达式,那么将它赋给指针变量p 的是什么呢?

  • 在x86环境下,指针变量p 中只能存放大小为4byte 的地址;而显然整个字符串在内存中是连续存放的,即不止占用一个内存单元空间,所以整个字符串会有多个地址;那么指针p是存放不下整个字符串的地址;

字符串"abcdefg"赋给指针p是其首字符 'a '的地址。

而函数printf 的占位符%s 会通过字符串的首字符地址而找到整个字符串;

我们来看一下打印字符串,代码如下:

#include<stdio.h>

int main()
{
	char ch[10] = "abcdefg";
	char* p = "abcdefg";
	char* p1 = ch; //将数组的首元素地址存放起来

	printf("%s\n", "abcdefg");
	printf("%s\n", ch);
	printf("%s\n", p);
	printf("%s\n", p1);

	printf("字符串地址=%p\n", "abcdefg");
	printf("指针p中存放的地址=%p\n", p);
	printf("指针p1中存放的地址=%p\n", p1);

	return 0;
}

代码运行结果如下:

分析:字符串的创建是用字符数组创建的,我们打印字符串一般采用 printf("%s\n", "abcdefg"); 这种方式,或者将字符串存在数组中,然后对此数组进行打印,即printf("%s\n",ch);

这时候你可能就会有疑问了,数组名可以理解为整体的数组也可以理解为其首元素的地址,于是我们就不清楚:

  • 是因为printf函数的占位符%s 只要得到字符串的首字符地址就会一直往后访问直到遇到 '\0' 
  • 还是因为根据的整体字符串"abcdefg"或者整个数组而打印出来整个字符串;

于是我们尝试将数组的首元素地址存放到指针p1中,我们会发现 printf("%s\n", p1);也可以实现打印字符串 "abcdefg" ,那么此时似乎就明显地感觉:打印字符串只要有其首字符的地址即可;

将字符串的地址存放到指针p中,指针p中存放的是整个字符串的地址吗?

  • 我们知道一个字符在内存中占 1 byte 的空间,而一个内存单元有一个地址,如果是想存放整个字符串的地址,就会存多个地址,但是指针变量在x86环境下只向内存申请了4byte 来存放该地址,所以就无法存放整个字符串的地址;printf("指针p中存放的地址=%p\n", p);的结果看以上代码运行结果,可知p中存放的为一个地址,即字符串首元素的地址;

我们将以上的代码又运行了一次:

我们不难发现,字符串的地址未改变,但是数组首元素的地址改变了

  • 这是因为 "abcdefg" 是一个常量字符串,即不能被修改的字符串;字符串是存放在内存中的常量区,在编译期间编译器便指派了地址;

常量区中的值只能读取而不能修改,所以当将此存在常量区的字符串的地址存到指针变量中,为以防对此指针变量解引用而对此字符串进行修改,于是应该优化写作: const char* p = "abcdefg";

注:const 修饰指针变量:(eg. char* p = "abcdefg"; )

1、当 const 放在 * 的左边,有两中写法:

  • 一是 const  char* p = "abcdefg";
  • 二是 char const * p = "abcdefg";

表示 *p 即指针变量p 指向的对象,const 修饰指针p指向的对象也就是说指针p 指向的对象是常变量,即指针p 指向的对象不可被修改;

2、当const 放在* 的右边,写作:

  • char* const p = "abcdefg";

表示指针变量 p是个常变量即存放在指针p中的地址不可修改;

字符指针有两种的表示形式:

1、const 不修饰

  • char* p = "abcdefg";

2、const 修饰

  • const char* p = "abcdefg";   
  •  char const * p = "abcdefg";

三、字符在内存中的存储

我们按照例子来分析:

#include<stdio.h>

int main()
{
	const char* p1 = "abcdef";
	const char* p2 = "abcdef";

	char arr1[] = "abcdef";
	char arr2[] = "abcdef";
	if (p1 == p2)
		printf("p1==p2\n");
	else
		printf("p1!=p2\n");

	if (arr1 == arr2)
		printf("arr1==arr2");
	else
		printf("arr1!=arr2");

	return 0;
}

代码运行结果:

分析:常量字符串 ”abcdef“ 存放在内存中的常量区,常量区只能读取而不能对其进行修改;

为什么常量字符串存在常量区?

  • 因为既然常量字符串不能被修改,那么这样的常量就没必要存多份,只要有一份,并且谁都不能去修改它;即常量字符串在内存中存一份便足以够用了;字符串是存放在内存中的常量区,在编译期间编译器便指派了地址;当是一个指针来存放该常量字符串的地址的时候,指针中存放的便是该字符串首元素在常量区中的地址;故而指针p1 与指针 p2 中存放的地址相同,即p1==p2;
  • 而如若是一个数组想要来存放该字符串,编译器便会先将存放在常量区的字符串拷贝放入到此数组之中,数组(假设该数组为局部变量)的空间是在其所在函数的函数栈帧的开辟的时候便确定了;从另外一个角度来看:数组创建时会向内存申请相对的大小空间来存放数组中的数据;而数组arr1 和 数组arr2 都会向内存申请空间来存放其数据,而每个内存单元的编号都不相同,所以就会导致不同空间的地址也不相同;再者,数组名代表着首元素的地址,所以 arr1 ! = arr2 ;

注:常量字符串不需要创建;代码中出现字符串 eg.const char* p1 = "abcdef"; 程序在编译时期:编译器会在只读数据区中放 abcdef \0 ,将" abcdef "当作常量字符来看待;而此时,只要将常量字符的起始地址交给一个指针去维护即可;同理,因为在编译期间编译器便为常量字符串在常量区指派了地址,故而想要在数组中放入常量字符串会将存放在常量区的字符串拷贝放入数组所占据的空间;


总结

1、字符指针就是指针变量指向的对象是字符类型,即指针变量的类型为 char* ;

2、const char* p = "abcdefg"; \ char const * p = "abcdefg"; 指针p中存放的是常量字符串在常量区存放的首字符 'a' 的地址

3、常量字符串存放在内存中的常量区,常量区中的数据只能读取而不能被修改。

指针进阶之字符指针(超详细) 指针进阶之字符指针,讲到懂 阅读详情

相关推荐

C语言之字符指针

第一种:指向一个字符 第二种:指向一个数组 第三种:指向一个字符常量 对于char*ch="hello,world"这行代码,很多小伙伴会认为是把hello,world放到字符指针ch里面,但其实是把字符串hello,world,首字符的地址放到了ch中. ......

m0_64365419的博客 5756

字符指针

2019-8-9字符指针 ①字符指针:指向字符型数据的指针变量。每个字符串在内存中都占用一段连续的存储空间,并有唯一确定的首地址。即将字符串的首地址赋值给字符指针,可让字符指针指向一个字符串。 ② char *ptr = "Hello";//将保存在常量存储区的"Hello"的首地址赋值给ptr 与 char *ptr; ptr = "Hello";//是等价的,注意不能理解为将字符串赋值给ptr...

小菜鸡 3万+

SAP物料分类账实战:从配置到月结的关键步骤(基于S/4HANA1909)

本文详细解析了在SAP S/4HANA 1909系统中,物料分类账从基础配置到月结操作的全流程。作为成本核算的核心,物料分类账通过多级价格确定实现实际成本核算,关键步骤包括激活物料分类账、配置自动过账科目(OBYC)、运行CKMLCP进行差异分摊与结算。文章旨在帮助财务与成本会计人员确保成本差异的准确归集与分摊,顺利完成月结。

weixin_28314575的博客 195

字符串--字符指针、字符串的访问和输入/输出(字符串空格问题,输入带双引号的字符串)

其实,在前面几个程序都存在一种容易被忽略的问题,即如果用户没有听从括号内的提示信息的指示,键入的字符数超过了数组name的大小12,那么多出来的那些字符就有可能重写内存的其他区域,导致程序出错。例如,在前面定义的字符数组str中,str[0]就表示第1个字符’H’,str[1]表示第2个字符’e’,依此类推,可以通过下标为i的元素str[i]来访问存放于数组中的第i+1个字符。因为数组名是一个地址常量,所以str的值是不可修改的,但ptr的值(即ptr的指向)可以被修改,ptr所指向的字符串也可以被修改。

ww1425408527的博客 7274

别再死记硬背了!用‘瞬时极性法’5分钟搞定三极管反馈电路类型判断

本文介绍了使用‘瞬时极性法’快速判断三极管反馈电路类型的高效技巧。通过假设输入信号变化并追踪传递路径,该方法能准确区分正负反馈、电压电流反馈、串联并联反馈及交直流反馈,避免传统死记硬背的弊端,显著提升电路分析效率。

weixin_42524708的博客 183

深入解析国家企业征信系统瑞数6vmp算法逆向实战

本文深入解析了国家企业征信系统所采用的双层防护机制,重点聚焦于瑞数6代vmp算法的逆向实战。文章详细拆解了从加速乐521状态码的绕过,到瑞数412挑战的核心算法还原过程,提供了通过动态Hook、日志分析进行算法逆向,并用Python实现自动化请求链的完整技术方案,旨在为安全研究与技术学习提供参考。

yellow的专栏 612

C语言 进阶指针笔记博客 #字符指针#数组指针#指针数组对进阶指针的详细解析

通过**“顾名思义”与“内存本质”**双维度,系统梳理从字符指针、数组指针到函数指针数组的逻辑脉络。教你利用优先级与结合性拆解《C陷阱与缺陷》中的复杂声明,看透指针步长与传参本质,助你彻底通关 C 语言底层逻辑,跳出指针陷阱。

2501_91286341的博客 639

Qt6导入QtChart绘制图表(添加组件)

我在按照大佬们的说明添加QtChart时发现,我的电脑上并不能正确的添加QtChart并编译通过,所以在综合了两位大佬的说明又自己探索了一番后,正确编译通过,分享出来,以便同志们节省一些时间。

Nuaa0719301zyx的博客 2951

椋鸟C语言笔记#20:指针数组、指针模拟二维数组、数组指针、二维数组传参的本质、字符指针与字符串

萌新的学习笔记,写错了恳请斧正。

椋鸟的博客小窝 210

创意视觉项目实战:用ESP32-S3+OV5640构建智能图像流系统

本文详细介绍了如何使用ESP32-S3开发板与OV5640摄像头模块构建智能图像流系统。从硬件连接到软件配置,涵盖Arduino环境搭建、图像采集优化、WiFi视频流传输及外设集成,为开发者提供完整的实战指南,助力智能家居和机器人视觉项目开发。

tech5的博客 1292

渗透测试面试题

渗透测试面试题 一.思路流程 1.信息收集 服务器的相关信息(真实ip,系统类型,版本,开放端口,WAF等) 网站指纹识别(包括,cms,cdn,证书等),dns记录 whois信息,姓名,备案,邮箱,电话反查(邮箱丢社工库,社工准备等) 子域名收集,旁站,C段等 google hacking针对化搜索,pdf文件,中间件版本,弱口令扫描等 扫描网站目录结构,爆后台,网站banner,测试文件,...

千寻的博客 12万+

C语言 带参数宏定义中 # 和 ## 知识点总结、代码分析

目录 一、宏定义中 “#”知识点 1、直接转换字符串,不展开。 2、转换出的结果一定是“字符串”。 二、宏定义中 ## 知识点 1、应用场景 2、“##”的作用是将 左右两边的参数做整体字符串拼接替换 3、经过 ## 替换后的内容必须 有一个 同名的 变量与之对应。 4、 只拼接,不展开。 5、## 操作 动态函数指针 案例代码 一、宏定义中 “#”知识点 #的...

猪哥的专栏 4269

Codex 桌面端自定义模型列表为空 — 修复记录

摘要:Codex桌面端首次启动正常,但二次启动后出现中文界面且模型列表丢失。问题根源在于Electron的Chromium持久化状态损坏,导致UI渲染异常。解决方案包括:1) 将config.toml中的模型路径改为绝对路径;2) 删除损坏的状态文件.codex-global-state.json;3) 清除Chromium用户数据目录AppData\Roaming\Codex\web。验证显示修改后模型功能恢复正常,重装无效的原因是卸载未清除用户数据目录。

猿小白的博客 1154

24.大厂总监带你吃透 C语言字符串存储之谜:指针 vs 数组的内存真相 #总结版 #a1

c语言中,c1指向代码段中的常量,只读不写,且常量相同,指向的地址也相同。中,c2指向堆栈段中的数据,可读可写,相当于把代码端的数据拷贝了出来。

c5dnuser的博客 289

基于机器学习的网络安全态势感知模型研究与实现

基于机器学习的网络安全态势感知模型研究与实现 1. 背景介绍 1.1 网络安全态势感知的重要性 在当今互联网时代,网络安全已经成为一个至关重要的话题。随着网络攻击手段的不断升级和演变,传统的被动防御方式已经难以满足日益

AI大模型应用之禅 1582

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

嵌入式C语言高级知识点总结

预处理, 展开头文件/宏替换/去掉注释/条件编译 (test.i main .i)编译(gcc -s), 检查语法,生成汇编 ( test.s main .s)汇编(gcc -c), 汇编代码转换机器码 (test.o main.o)

weixin_56940389的博客 3840

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

C语言的细节(不定期更新)

1.字符串换行c语言中,所有的空格、注释、TAB、换行都可以任意写,但是字符串中不可以。如果要换行,就像这样printf("a very very very very""long string");这个是C89以后才支持,老的风格是printf( "a very very / very long line");2 pow函数pow()的返回类型为double,在对整

暗恋的滋味 1403

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

嵌入式高级c语言学习

传递一个值的地址 int a = 10,void fun(int *p)传递一个空间的地址int buf[10];函数返回值静态区:关键字static 会把buf里的局部变量由局部变量存的栈中放在数据(Data)段在静态区,生存周期是整个程序结束2.只读区: char buf[] = "hello world";3.堆区:malloc() ,free() char *s = (char *)malloc(2048);堆区return s;

m0_62995834的博客 901

C语言经典代码——part 28

题目:从键盘输入一些字符,逐个把它们送到磁盘上去,直到输入一个#为止。题目:计算字符串中子串出现的次数。

2303_81838541的博客 486

C语言和gcc笔记

1.C语言中的register关键字只是类型修饰符和const和volatile的地位一样。在C语言中定义register a;用gcc -Wall 编译后会警告type defaults to `int' in declaration of `a',可见register 只是修饰符。 2.在C语言中数组会退化为指针。如下代码 #include #include int main(){

Notebook 1372

C语言中定义变量“#X”,”X##”, “##X” 和“##X##”解析

关于#和## 在C语言的宏中,#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。比如下面代码中的宏: 1. #使用 #define WARN_IF(EXP) / do{ if (EXP) / fprintf(stderr, "Warning: " #EXP "/n"); } / wh...

weixin_30417487的博客 760

【C语言练习】编写代码,演示多个字符从两端移动,向中间汇聚

利用C语言编写一个程序,实现多个字符从两端从中间汇聚。

embrace99的博客 192

c语言switch函数映射技巧,C语言宏定义##连接符和#符的使用(MFC就是靠##自动把消息和消息函数对应起来了,借助宏来减少switch case代码的编写量)...

C语言中如何使用宏C(和C++)中的宏(Macro)属于编译器预处理的范畴,属于编译期概念(而非运行期概念)。下面对常遇到的宏的使用问题做了简单总结。关于#和##在C语言的宏中,#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。比如下面代码中的宏:#define WARN_IF(EXP) do{ i...

weixin_39616477的博客 502

C //例10.1 从键盘输入一些字符,逐个把它们送到磁盘上去,直到用户输入一个“#”为止。

【代码】C //例10.1 从键盘输入一些字符,逐个把它们送到磁盘上去,直到用户输入一个“#”为止。

Made in Code 760

c语言里的%p的作用,C语言中geiwei=m%10什么意思,求解!

C语言中geiwei=m%10什么意思,求解!以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!C语言中geiwei=m%10什么意思,求解!%运算表示取余数,m%10就把m除以10的余数,实际上就是m的个位数。例如m的值为13,那么m%10的结果就是3。c语言中if(m)什么意思if(m) 如果m是逻辑真if(m=+)是...

weixin_35531779的博客 1785

c语言控制与实现 pdf,c语言的控制输出格式.pdf

1 .转换说明符%a(%A) 浮点数、十六进制数字和 p-(P-) 记数法 (C99)%c 字符%d 有符号十进制整数%f 浮点数 ( 包括 float 和 doulbe)%e(%E) 浮点数指数输出 [e-(E-) 记数法 ]%g(%G) 浮点数不显无意义的零 ...

weixin_42313675的博客 139
上一篇: C语言 数据存储之结构类型 万字讲解#数据类型详细介绍 #整型在内存中的存储 #大小端字节序 #浮点型在内存的存储解析
下一篇: C语言 #指针数组 #数组指针 #数组参数、指针参数
秦少游在淮海
博客等级 码龄3年 1071粉丝 88原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值