我作为自学者对c语言字符串用指针定义和用数组定义的思考

引言

亲爱的读者朋友,你好!

我正在自学C语言,学习字符串时,发现字符串可以用星号或数组形式来定义,然而这不同的定义方式及其带来的效果给了我很大的困惑——当我想修改字符串时,我的定义方式却不允许我这么做。

我查阅了一些图书和视频,自己也反复思考,终于总结了一份自己还比较满意的字符串定义方式及其效果的比较文章,也就是本文。希望本文可以给有同样困惑的朋友以启发。这篇文章的观点不一定完全正确,希望大家批判性地阅读,谢谢!

字符串的两种定义方式

首先,请浏览以下代码。

#include <stdio.h>
int main(void)
{
	char *ch1 = “helloworld”;
	char ch2[20] = “helloworld”;
	printf("%x", "helloworld");//假设输出结果为404000
	return 0;
}

上述代码中,两个字符串分别用两种方式定义。

  1. 字符串ch1是用指针形式定义的;

  2. 字符串ch2是用数组形式定义的。

C语言中这两种字符串定义方式,在效果上有着较大的差异,以下就以我们刚刚初始化的ch1和ch2为例,一起来探讨一下我对这两种定义方式差异的认识。

顺便说一句,如代码片中注释所示,这里,我们假设helloworld字面量存储在常量区里地址为404000的位置上。

两种定义方式的六个差异

差异1:构造字符串的方式不同

ch1构造过程中,”helloworld”被作为常量字符串储存在内存中的常量区中,例如404000这个地址中。ch1作为指针变量,很多情况下,直接指向了这个地址,并没有重新构造一个字符串。

相比之下,ch2则是在内存中又建立了一个新的字符串,这个字符串是根据位于404000上的常量字符串建造的,所以字面量也是helloworld,它的地址可能是62fe10,这与位于404000这个地址上的字符串是两个不同的字符串,只不过其字面量都是helloworld。

差异2:单个字符元素修改情况不同

假设我们在函数体中新增两行语句,你觉得它们起到的效果相同吗?

#include <stdio.h>
int main(void)
{
	char *ch1 = “helloworld”;
	char ch2[20] = “helloworld”;
	printf("%x", "helloworld");//假设输出结果为404000
	ch1[0] = 'a';//新增的语句,希望把ch1中第一个字符从h改为a
	ch2[0] = 'a';//新增的语句,希望把ch2中第一个字符从h改为a
	return 0;
}

其实,对于用指针定义的ch1来说,ch1[0] = ‘a’这种形式在c语言中属于未定义的行为,这种语句往往会得到并不想要的结果,原因在《C Primer Plus》中有介绍,我印象里比较复杂,这里不再赘述。

而ch2[0] = ‘a’这种表述是允许的,因为ch2并不像ch1一样指向常量区,它的每个字符元素都是可被修改的变量,因此执行这步语句后,ch2字面量将变成“aelloworld”,而在常量区404000上的字符串还是helloworld。

所以,如果需要对字符串的某个或某些进行局部修改,一般要用数组定义。

差异3:字符串整体修改情况不同

试分析以下代码块,思考新增的三行语句的运行效果。

#include <stdio.h>
int main(void)
{
	char *ch1 = “helloworld”;
	char ch2[20] = “helloworld”;
	printf("%x", "helloworld");//假设输出结果为404000
	ch1 = “nihaoshijie”;//新增的语句,希望让字符串ch1拥有新的字面量
	ch2 = “nihaoshijie”;//新增的语句,希望把字符串ch2拥有新的字面量
	printf("%x", "nihaoshijie");//新增的语句,假设输出结果为404109
	return 0;
}

以上操作对于ch1来说,相当于让它不再指向常量区404000上的字符串,而是重新指向了404109上的字符串。所以,ch1的字面量也可以直接更改成nihaoshijie,而不再是helloworld。同理,如果我事先定义了ch3这个字符串并赋值,我接下来让ch1 = ch3,ch1则会重新指向ch3这个字符串,ch1的字面量也会跟ch3的相同。

然而这种操作对于ch2来说就行不通了。大家可能还记得学数组时,定义一个int i[3]={1,2,3},事后是不可以再重新整体赋值的,如i[3] = {4,5,6},这会报错,而且也不可以让它重新等于另一个字符串,即int j[3] = {1,2,3}; int i[3]; i = j也是不被允许的。究其原因,则是因为char ch2[]实际上定义出的ch2是一个字符串常量,就如同int i[4]定义出的实际上是数组常量一样,其地址不能更改,也就是不能让它指向别的字符串或数组。这一点一定要理解好。但是,还是有一种曲线救国可以整体修改的方式,也就是下面的差异4中介绍的这种。

差异4:能否用I/O函数从键盘输入值

用指针来定义的ch1是不可以从用gets之类的函数从键盘输入值的,它只能事先指向一个已经定义好的常量区字符串或字符串变量。这一点跟数组类似。

对于ch2来说,不管初始化的时候有没有为其赋值,都可以事后通过gets/fgets等函数进行重新赋值。

所以,凡是涉及到键盘读入时,一定要用ch2来定义,即便必须要用到用指针定义的字符串,也要先通过数组来定义,然后把该字符串赋值给用指针定义的字符串。

差异5:处理效率不同

我隐隐记得《C Primer Plus》一书中曾提到,用星号定义的字符串处理速度较快,用数组定义的字符处理速度较慢,但是这一点我不确定。

差异6:作为函数形参的书写形式不同

进行函数传参时,不管字符串是通过两种定义方式中的哪一种来定义的,当形参写成以下两种形式时,传递的内容是相同的。

void f(char *fch);
void f(fchar fch[]);

上面两种情况都是传递了字符串的地址,即首字符的地址。

结语

以上就是我总结的两种定义方式的对比。个人水平非常有限,希望能给大家带来帮助和思考!如有疏漏之处烦请在评论区指出,以免更多人受到误导,感谢。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值