C/C++易错知识点(3):字符数组的修改、sscanf、sprintf

字符数组是一个很细节的语法,涉及很多知识点,这篇文章我主要分享一下如何理解字符数组,以及对应的sscanf、sprintf有什么用

1.字符数组的初始化以及内容修改易错点

字符数组的初始化方式有两种,一种是直接用字符串进行初始化,另一种是大括号加上字符或字符串进行初始化

8b82c40a3d2f40aeba75c57c6dfbf247.png

这两种初始化方式均可,但是如果我现在想要更改数组的内容呢,那应该怎样操作?

很多人会第一时间想到直接给arr赋上一个新的字符串,但这显然是不行的。

55eaaeef02944073b5799eb7a3be0d27.png

接下来分析原因:

a.数组名的理解:我们都常说数组名是首元素地址,但这并不准确,因为arr本质上并不能算作一个指针,有一个典型的错误理解是:arr是一个指针,编译器为arr这个指针变量独立开辟一块空间,并赋值为首元素地址。

但事实上,arr并不是我们传统意义理解上的变量,它更像是一种标签,在不同的时候有不同的意义。在大多数情况下,它表现出来的特性都类似于指针,这也是我们为什么常说arr是首元素地址。但其实arr不是指针,编译器没有为arr单独分配空间,这也是它不能完全等于指针的本质原因。

当我们写出char* p = arr时,看似能证明arr是一个指针,但其实这只是arr在这里表现出了指针的特性,拥有指针一样的用法,实际上是将arr数组的首元素的地址直接赋给p,并没有通过arr这个单独的指针来赋值,准确来说arr只是一个标签。

而对应的&arr标志的就是取出整个数组的地址。不是我们传统理解的对arr这个指针变量取地址。

这一点非常细节,需要每个人特别注意!

后面我所说的“arr这个指针”实际上是帮助我们理解语法,因为arr很多时候确实会表现出指针一样的特性,但是要明白它的实质并不是传统的变量,要明白它只是一个标签。

62316d19a20a4dc5be0a82d68e46bdbd.png

arr这个指针还有个常属性,即arr存储的值不能被修改,类似于const int* p,这就导致arr的指向的空间区域在一开始就确定了,不能被修改了。

b.表达式的返回值:我们还要清楚表达式的返回值是存储在一个临时变量里的,这个临时变量同样具有常属性,这个临时变量我们是看不到的,如果有变量接收就把这个临时变量的值赋给这个变量。如果学过C++的引用,那么我们对这个临时变量的印象就会很深刻。

在字符数组的两种初始化表达式语句中,"Hello"返回的值是字符'H'的首元素地址,{'Hell", 'o'}返回的也是括号内第一个字符'H'的地址。更详细一点,就是这些初始化表达式的值(地址)存到一个临时变量里,再把临时变量里的这个地址赋给arr这个指针,完成初始化。

后续对字符串的访问其实就是靠的这个返回的地址的。

下面看一种情况:

8a88f02b8c38425db14edcfef4a9233e.png

这种情况也同理,只有在定义的时候,在arr这个指针还没有明确指向哪一块空间的时候,我们可以对它初始化,但如果不初始化,那么编译器会自动分配一块空间并把这块空间的最低地址赋给指针arr,这个时候arr就再也无法进行修改了。在上面这张图中,我们可以看到如果我们想要将"Hello"存到数组,直接赋值操作是不行的,因为就像上面所说的,arr = "Hello";本质是修改arr这个指针存储的值,这是绝对不允许的。我们明显需要其它方法来实现这种操作,达到在这块指定的空间里修改值的操作。

2.为什么字符数组会遇到这种情况

其实arr具有的常属性适用于所有的数组,一旦数组被定义了,首元素的地址是一定不能变的。

至于在int、float、double等类型的数组我们很少遇到这个情况,是因为几乎没有人会写出下面的代码:

b224a25c1569403eb3fd6de541433058.png

一般而言,我们都会针对arr[0]这种写法来修改数组里面的值,这其实就相当于在指定空间里面修改值的操作,而不是直接去修改arr指向的空间。

但是字符数组里面存储的信息大多是连续的,如一个单词,一个句子,我们需要批量修改字符数组里面的值,单个字符修改太慢,且又不能使用循环来解决,因此我们才会容易犯上面的错误。而这个需求在其它类型的数组里面遇到的情况相对较少,通常都是针对某个数据进行修改,如果是批量修改,写很容易通过循环来解决。

3.字符串或内存函数解决方案

有以下几种函数可以帮助我们批量修改字符串中的值,它们都是直接到对应内存区域里修改值实现的。

(1)strcpy

标准格式:char * strcpy ( char * destination, const char * source )

(2)strcat

标准格式:char * strcat ( char * destination, const char * source );

(3)memcpy

标准格式:void * memcpy
 ( void * destination, const void * source, size_t num );

(4)memmove 

标准格式:void * memmove ( void * destination, const void * source, size_t num );

其中内存函数的第三个参数是拷贝的字节,一般情况推荐用memmove 

4.sscanf和sprintf

上面的字符串函数和内存函数已经可以实现我们全部的需求,但是仍然不方便,这个时候就可以考虑sprintf。

sprintf就是将我们想要输入的任何数据直接存入字符数组中。

ba51f418545e4bf5ae7c6c2c6daebb1a.png

我们把数组的地址传过去,函数就能自动找到对应的区域并将这块区域的数据进行修改。

这非常方便,同时这个函数也可以使用占位符,和printf一致。

57f96d03636e49d4ba260de389ebd413.png

sscanf也是对字符数组内的数据进行操作,和scanf的功能类似。但是scanf是需要我们从屏幕中输入数据,再进行读取。而sscanf是直接在字符串中对数据进行读取。读取的规则和scanf一致。

d15f0aa5d73f4078a5c8f091f506a9b4.png

 

 

 

 

  • 25
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值