C字符串是否始终以null终止,还是取决于平台?


13

现在,我正在使用嵌入式系统,并研究在没有操作系统的微处理器上实现字符串的方法。到目前为止,我正在做的只是使用使NULL终止的字符指针并将它们视为NULL表示结尾的字符串的想法。我知道这很普遍,但是您是否总是可以指望这种情况?

我问的原因是我正在考虑也许在某个时候使用实时操作系统,并且我想尽可能多地重用当前代码。因此,对于存在的各种选择,我是否可以期望字符串能够正常工作?

让我更具体地说明我的情况。我正在实现一个通过串行端口接收和处理命令的系统。我能否保持命令处理代码相同,然后期望在RTOS(包含命令)上创建的字符串对象全部以NULL终止?还是根据操作系统而有所不同?

更新资料

在被建议看一下这个问题之后,我确定它不能完全回答我的要求。问题本身是在询问是否应该始终传递字符串的长度,这与我要问的完全不同,尽管某些答案中包含有用的信息,但它们并不是我要找的。那里的答案似乎提供了为什么或为什么以空字符终止字符串的原因。我要问的是,我是否可以或多或少地期望不同平台的出生字符串以空值终止自己的字符串,而不必走出去尝试在那里的每个平台。


3
我已经很长时间没有使用C了,但是我想不起来遇到不使用以NULL结尾的字符串的实现的时候了。如果我没记错的话,它是标准C的一部分(就像我说的,已经有一段时间了...)
MetalMikester

1
我不是C语言方面的专家,但据我所知,C语言中的所有字符串都是以null终止的char数组。您可以创建自己的字符串类型,但是您必须自己实现所有字符串操作功能。
马查多


1
@MetalMikester您认为可以在标准C规范中找到此信息?
史努比(Snoop)

3
@史努比很可能是。但是实际上,在谈论C语言中的字符串时,它们只是一个以NULL结尾的字符数组,仅此而已,除非您使用某种非标准的字符串库,但这不是我们在这里谈论的内容。我怀疑您会发现一个不尊重这一点的平台,尤其是C的优势之一就是可移植性。
MetalMikester's

Answers:


42

在任何平台上,称为“ C字符串”的事物都将以空值终止。这就是标准C库函数确定字符串结尾的方式。

在C语言中,没有什么可以阻止您拥有不以null结尾的字符数组。但是,您将不得不使用其他方法来避免耗尽字符串的结尾。


4
只是为了补充;通常您在某个地方有一个整数来跟踪字符串的长度,然后您最终得到了一个自定义数据结构来正确处理它,就像Qt中
Rudolf Olah

8
恰当的例子:我使用一个至少使用五种不同字符串格式的C程序:空终止char数组,char长度在第一个字节中编码的数组(通常称为“ Pascal字符串”),wchar_t两种以及char结合了这两种方法的数组:在第一个字节中编码的长度,以及终止字符串的空字符。
标记

4
@Mark与许多第三方组件/应用程序交互或遗留代码混乱?
Dan在Firelight的抚养下

2
@DanNeely,以上全部。用于与经典MacOS进行交互的Pascal字符串,用于内部使用和Windows的C字符串,用于添加Unicode支持的宽字符串以及混混的字符串,因为有人试图变得聪明并制作了可以同时与MacOS和Windows交互的字符串。
Mark

1
@Mark ...当然,没有人愿意花钱还清技术债务,因为经典的MacOS早已死了,而混蛋弦在每次需要触摸时都是双重的。我的同情。
Dan在Firelight的抚养下

22

通常,终止字符的确定取决于文字的编译器以及字符串的标准库的实现。它不是由操作系统决定的。

NUL终止的约定可以追溯到标准C之前,在30多年的时间里,我不能说我遇到了可以做其他事情的环境。此行为已在C89中进行了编码,并且继续是C语言标准的一部分(链接到C99的草案):

  • 第6.4.5节NUL通过要求NUL在字符串文字后附加a 来设置以终止符结尾的字符串的阶段。
  • 7.1.1节通过将字符串定义为“由第一个空字符终止并包括第一个空字符的连续字符序列” ,将其带入标准库中的函数。

没有理由不能有人编写处理以其他字符结尾的字符串的函数,但是在大多数情况下,也没有理由放弃已建立的标准,除非您的目标是让程序员适应。:-)


2
原因之一是避免一遍又一遍地查找同一字符串的结尾。
圣保罗Ebermann

@PaŭloEbermann对。以必须传递两个值而不是一个为代价。如果仅传递字符串文字(如中),这会有点令人讨厌printf("string: \"%s\"\n", "my cool string")。在这种情况下,传递四个参数(除了某种终止字节)之外的唯一方法是将字符串定义为类似于std::stringC ++中的字符串,这有其自身的问题和局限性。
cmaster-恢复莫妮卡

1
6.4.5节并不需要一个字符串文本,以一个NULL字符被终止。它明确指出:“ 字符串文字不必是字符串(请参见7.1.1),因为可以通过\ 0转义序列将空字符嵌入其中。
bzeaman

1
@bzeaman脚注说您可以构造不符合7.1.1字符串定义的字符串文字,但是引用该句子的句子表示兼容的编译器- NUL终止它们,无论如何:“在翻译阶段7,字节或代码值0会附加到由一个或多个字符串文字产生的每个多字节字符序列中。” 使用7.1.1定义的库函数在NUL找到它们时首先停止,并且不知道或不在乎它之外是否还有其他字符。
Blrfl

我站得住了。我搜索了诸如“空”之类的各种术语,但错过了提及“零值”的6.4.5.5。
bzeaman

3

我正在使用没有操作系统的嵌入式系统...我正在...使用使NULL终止的字符指针并将其视为字符串(其中NULL表示结尾)的想法。我知道这很普遍,但是您是否总是可以指望这种情况?

在C语言中没有字符串数据类型,但是有字符串文字

如果在程序中放入字符串文字,通常会以NUL终止(但请参见下面的注释中讨论的特殊情况。)也就是说,如果将文字放在期望值"foobar"的地方const char *,编译器将发出foobar⊘到程序的const /代码段/节,表达式的值将是指向存储f字符的地址的指针。(注意:我用来表示NUL字节。)

C语言使用字符串的唯一其他含义是,它具有一些对NUL终止的字符序列进行操作的标准库例程。这些库例程在裸机环境中将不存在,除非您自己移植它们。

它们只是代码-与您自己编写的代码没有什么不同。如果您在移植它们时不破坏它们,那么它们将做他们一直做的事情(例如,停在NUL上)。


2
回复:“如果在程序中放入字符串文字,它将始终被NUL终止”:您确定吗?我非常确定(例如)char foo[4] = "abcd";是创建四个字符的非空终止数组的有效方法。
ruakh

2
@ruakh,糟糕!我没有考虑过这种情况。我正在考虑出现在期望char const * 表达式的地方的字符串文字。我忘记了C 初始化程序有时可以遵循不同的规则。
所罗门慢传

@ruakh字符串文字是NUL终止的。数组不是。
jamesdlin

2
@ruakh,你有一个char[4]。这不是一个字符串,但它初始化从一个
Caleth

2
@Caleth,“从一个初始化”并不是在运行时必须发生的事情。如果我们在staticRuakh的示例中添加关键字,则编译器可能会向初始化的数据段发出 NUL终止的“ abcd”,以便变量由程序加载器初始化。因此,Ruakh是正确的:在至少一种情况下,程序中字符串文字的出现不需要编译器发出NUL终止的字符串。(ps,我实际上使用gcc 5.4.0编译了该示例,并且编译器未发出NUL。)
所罗门慢

2

正如其他人提到的那样,字符串的空终止是C标准库的约定。如果您不打算使用标准库,则可以按照任何希望的方式处理字符串。

这对于任何带有“ C”编译器的操作系统都是如此,并且,您也可以编写未在真正的操作系统下运行的“ C”程序,就像您在问题中提到的那样。一个例子是我设计过一次的喷墨打印机的控制器。在嵌入式系统中,可能不需要操作系统的内存开销。

例如,在内存紧张的情况下,我将针对处理器的指令集查看编译器的特性。在对字符串进行大量处理的应用程序中,可能需要使用诸如字符串长度之类的描述符。我正在考虑一种情况,其中CPU在处理短偏移和/或地址寄存器的相对偏移方面特别有效。

那么,在您的应用程序中哪个更重要:代码大小和效率,或与OS或库的兼容性?另一个考虑因素可能是可维护性。您偏离惯例的距离越远,他人维护的难度就越大。


1

其他人已经解决了一个问题,在C语言中,字符串主要是由它们构成的。但是,对于终止符本身,您的问题似乎有些困惑,从一个角度来看,这可能是您所处位置的人所担心的。

C字符串以null终止。也就是说,它们以空字符终止NUL。它们不会被null指针终止,null指针NULL是一种完全不同的值,具有完全不同的用途。

NUL保证其整数值为零。在字符串中,它还将具有基础字符类型的大小,通常为1。

NULL完全不能保证具有整数类型。NULL通常用于指针上下文,通常应具有指针类型,如果编译器良好,则不应将其转换为字符或整数。虽然定义NULL涉及字形0,它不能保证真正有值[1],除非你的编译器实现的常数作为一个字符#define(许多不这样做,因为NULL 实在不应该在非有意义指针上下文),因此不能保证扩展代码实际上包含零值(即使它确实包含零字形也令人困惑)。

如果NULL键入,则大小也不太可能为1(或其他字符大小)。可以想象,这可能会导致其他问题,尽管实际字符常量在大多数情况下也没有字符大小。

现在,大多数人会看到这种情况,并认为“空指针不是全零位吗?是什么废话”-但是这样的假设仅在x86这样的通用平台上才是安全的。由于您已经明确提到了针对其他平台的兴趣,因此您需要考虑到此问题,因为您已将代码与关于指针与整数之间关系的性质的假设明确分开。

因此,尽管C字符串以null终止,但它们不是以终止NULL,而是以NUL(通常为'\0')终止。明确NULL用作字符串终止符的代码将在具有简单地址结构的平台上运行,甚至可以与许多编译器一起编译,但这绝对不是正确的C语言。


[1]实际的空指针值是由编译器在上下文中读取0 令牌时插入的,该令牌将被转换为指针类型。这不是从整数值 0 的转换,并且如果0使用了令牌本身以外的其他任何值(例如,来自变量的动态值),则不能保证保持不变。转换也是不可逆的,并且将空指针转换为整数时不必产生值0。


好点。我已经提交了一个编辑内容,以帮助解决此问题。
蒙迪·哈德

NUL保证整数值为零。” -> C没有定义NUL。相反,C定义的字符串有一个最后的空chracter,设置为0的所有位字节
恢复莫妮卡- chux

1

我一直在C中使用字符串,这意味着具有空终止符的字符称为字符串。

当您在裸机或任何操作系统(例如Windows,Linux,RTOS:(FreeRTO,OSE))中使用时,它不会有任何问题。

在嵌入式世界中,空终止实际上更有助于将字符标记为字符串。

我已经在许多安全关键系统中像这样在C中使用字符串。

您可能想知道,C中的字符串实际上是什么?

C样式字符串是数组,也有字符串文字,例如“ this”。实际上,这两种字符串类型仅仅是在内存中彼此相邻的字符集合。

每当您编写用双引号引起来的字符串时,C都会自动为我们创建一个字符数组,其中包含该字符串,并以\ 0字符终止。

例如,您可以声明和定义一个字符数组,并使用字符串常量对其进行初始化:

char string[] = "Hello cruel world!";

简单的答案:您实际上不必担心使用带有空终止符的字符,该工作独立于任何平台。


谢谢,不知道在用双引号声明时NUL会自动附加a。
史努比(Snoop)

1

正如其他人所说,空终止对于标准C几乎是通用的。但是(正如其他人也指出的那样)不是100%。对于另一个示例,VMS操作系统通常使用其所谓的“字符串描述符” http://h41379.www4.hpe.com/commercial/c/docs/5492p012.html在C中通过#include <descrip.h访问>

应用程序级的东西可以使用空终止,也可以不使用空终止,但是开发人员认为合适。但是底层VMS绝对需要描述符,这些描述符根本不使用空终止(有关详细信息,请参见上面的链接)。这在很大程度上是为了使所有直接使用VMS内部语言的语言(C,汇编语言等)都可以具有一个公共接口。

因此,如果您预期会出现任何类似的情况,则可能需要比“通用null终止”更为必要,要谨慎一些。如果我正在做您正在做的事情,我会更加小心,但是对于我的应用程序级的东西,可以假定为空终止。我只是不建议您使用相同级别的安全性。您的代码很可能必须在将来的某个时刻与汇编代码和/或其他语言代码进行交互,而这些代码可能并不总是符合以null终止的字符串的C标准。


如今,0终止实际上是非常不寻常的。C ++的std :: string没有,爪哇字符串不,Objective-C的的NSString没有,夫特字符串不-其结果是,与NUL码每个语言库支持字符串的字符串(这是不可能使用C字符串的原因显而易见)。
gnasher729

@ gnasher729我将“ ...相当通用”更改为“对于标准C相当通用”,我希望它消除任何歧义并在今天保持正确(根据OP的主题和问题,这就是我的意思)。
John Forkosh

0

以我在嵌入式,安全关键和实时系统中的经验,同时使用C和PASCAL字符串约定(即,将字符串长度作为第一个字符(将长度限制为255)并结束字符串)并不少见。至少包含一个0x00的字符串,(NUL),将可用大小减少到254。

这样做的一个原因是要知道在接收到第一个字节后需要多少数据,而另一个原因是,在这种系统中,尽可能避免使用动态缓冲区大小-分配固定的256缓冲区大小更快,更安全,(没有需要检查 malloc失败)。另一个是与您通信的其他系统可能未使用ANSI-C编写。

在任何嵌入式工作中,重要的是建立并维护一个接口控制文档(IDC),该文档应尽快定义您的所有通信结构,包括字符串格式,字节序,整数大小等,(最好在开始之前),它应该是你的,和所有的球队,圣书时写系统-如果有人希望引入一个新的结构或格式化它必须有记录在案第一,每个人都可能受到影响告知,可能有一个选项,以否决的变化。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.