在操作内存时,是否需要乘以 sizeof(char)?

17

当使用 malloc 和进行类似的内存操作时,我能否依赖于 sizeof(char) 总是为1?

例如,我需要为 N 个 char 类型的元素分配内存。是否需要乘以 sizeof(char):

char* buffer = malloc( N * sizeof( char ) );

我是否可以相信sizeof(char)始终为1,直接跳过乘法运算呢?

char* buffer = malloc( N );

我完全理解sizeof在编译期间被评估,编译器甚至可能会将乘法优化掉,因此性能惩罚将是最小的,很可能为零。

我主要关心代码的清晰度和可移植性。对于char类型,这个乘法操作是否有必要?

8个回答

29

按照定义,sizeof(char)的大小始终等于1。在C语言中,一个字节是字符的大小,无论字节中包含多少位(通常在桌面CPU上为8)。

一个字节不是8位的典型例子是PDP-10和其他旧的、迷你计算机般的架构,其字节大小为9/36位。但我认为不是2的N次幂的字节正在变得极为罕见。

此外,我认为这种写法更好:

char* buf1;
double* buf2;

buf1 = malloc(sizeof(*buf1) * N);
buf2 = malloc(sizeof(*buf2) * N);

因为它可以针对任何指针类型工作。


我认为1字节= 8位的定义是正确的。你有例外情况吗? - AlexDrenea
6
1个字节的定义是N位,其中N取决于机器。并非所有机器都有8位/字节(虽然现在没有太多这样的机器了)。 - 1800 INFORMATION
11
今天,你通常只能遇到8位字节。但是字节的定义是不同的,并且与今天的体系结构没有关系,因为曾经存在9位字节和甚至36位字节的系统。如果你想确保,使用ISO术语“八位组”而不是“字节”。 - OregonGhost

14

sizeof(char)无论何种类型的内存操作,始终为1。

然而,sizeof(TCHAR)可能会因编译器选项而异。


我不是专家,但字符在Unicode情况下大小会变大,不是吗? - Alex S
5
@Shadow,不是。在这种情况下,通常使用宽字符类型wchar_t代替char。Microsoft特定的TCHAR方法是编写可以编译为宽字符或窄字符的代码的一种方式。目前还不清楚这是否是个好主意。 - RBerteig
@RBerteig:我认为这很明显是个坏主意。在Windows上使用非宽字符的char字符串的唯一原因是为了拥有可移植的代码,以便在其他更符合标准的系统(POSIX)上能够正常工作。一旦你写了TCHAR,你的代码已经被Windows特定的东西污染了,你可能会直接使用他们的宽字符函数和类型。就好像没有人希望他们的程序在用户尝试打开一个包含非代码页字符的文件名时立即崩溃。 - R.. GitHub STOP HELPING ICE
1
@R.,实际上 TCHAR 是一种可移植性的折衷方案,为了让 Windows 3.x 程序员离开 8 位的 char,让他们可以编写精心构造的代码,在 Win16、Win32(ASCII)和 Win32(Unicode)中进行编译而不需要更改。在大量使用 Windows API 调用的情况下,它的使用不会影响你的可移植性。但是,请不要将其用于应用程序处理的核心部分,否则你真的会影响可移植性。如果你确实避免了在 API 中使用 TCHAR,请在编译时断言 UNICODE 已定义,并可能编写 MessageBoxW 而不是 MessageBox 等。 - RBerteig

12
我认为这是一种反模式。它表明程序员并不完全知道自己在做什么,这立即使得代码的其他部分显得可疑。
当然,它并不是(引用维基百科)“无效”的,但我确实发现它“远非最佳”。它在运行时不会花费任何东西,但它会在代码中添加不必要的垃圾,同时表明有人认为它是必要的。
此外,请注意,该表达式不解析为函数调用:sizeof不是函数。你没有调用一个传递了神奇符号char的函数。你正在将内置的一元前缀运算符sizeof应用于一个表达式上,在这种情况下,你的表达式是类型char的转换,C中写作(char)
在可能的情况下,可以完全使用sizeof来处理其他表达式,并且它将产生表达式值的大小。
char a;
printf("A char's size is %u\n", (unsigned int) sizeof a);

这将在所有符合 C 标准的实现中始终打印 1

我也非常赞同 David Cournapeau 的观点,并认为在调用 malloc() 时重复类型 名称 也是一种反模式。

而不是

char *str;

str = malloc(N * sizeof (char));

对于许多人会编写以分配N个字符容量的字符串缓冲区的方式,我会选择

char *str;

str = malloc(N * sizeof *str);

或者(仅适用于字符串)像上面那样省略sizeof,但这当然更通用,对于任何类型的指针都同样有效。


2
我不同意。如果你省略它,你(以及任何阅读你代码的人)必须记住这是一个特殊情况,并将其识别为这样。这增加了认知负担。有时候更多的代码更好。 - Michael Carman
1
是的,sizeof不是一个函数 - 但如果你把它当作一个函数来处理,对我来说读起来更容易。除非你知道有额外的括号会改变输出的情况? - Mark Ransom
2
@Michael Carman - 这通常是一个特殊情况,因为你经常要分配和处理字符串,而如果你创建一个整数数组,它可能是为任何目的。我们需要对字符串与任意类型的数组进行不同的处理,我发现在malloc()中缺少sizeof(type)是一个很好的提醒。 - Chris Lutz

7

虽然它不是必要的,但我认为保留 sizeof( char ) 是一个好的习惯,因为这样可以使代码更易读,并避免使用魔术数字。另外,如果将来需要更改代码,以便将 char 的大小分配到指向该对象的指针中,那么更改代码会更容易,而不是只有一个 "1"。


9
“改变代码容易性”的论点是无意义的。sizeof()有8个字符。因为某人没有写sizeof(char),然后类型更改为wchar_t而不得不添加它,这不会让任何人手腕疼痛。如果你担心这一点,应该使用sizeof *buf,因为它甚至可以省略更少的输入。 - Chris Lutz
@ChrisLutz,我正在查看C11标准,但我没有看到任何条款明确说明sizeof(char)为1。根据wikipedia的说法,它可以是任何值:“例如,所有类型都可以是64位”。请问您能告诉我在标准中哪里有这样的规定吗? - Shahbaz
4
@Shahbaz - 6.5.3.4第4段:“当sizeof应用于具有类型charunsigned charsigned char(或其限定版本)的操作数时,结果为1。” 因此一直如此,将来也会如此。 char是C语言中的“字节”。从语言的角度来看,char是最小的完整单元,不管一个char有多少比特,所有sizeof值都是以char而不是“字节”(在C标准中不存在)为单位给出的。 如果所有类型都是64位,则sizeof(char) == sizeof(short) == sizeof(int) == sizeof(long) == 1,而不是8。 - Chris Lutz
1
@Shahbaz - 如果你需要标准化、可靠的大小,请使用 stdint.h,它是专门为此目的在 C99 中添加的。 - Chris Lutz
1
buffer = malloc(sizeof *buffer * N); 很容易编写,没有魔数,也不需要匹配/编辑 buffer 的指针类型更改。 - chux - Reinstate Monica
显示剩余4条评论

6
不是必须的。例如查看这里。根据C语言标准,sizeof(char)始终定义为1(字节)。请注意,由于sizeof返回的是字节数,每个字节的位数都无关紧要(在实践中通常为8)。

3

来自《新C标准:经济和文化评论》。

  1. 统计数据:2.0%的sizeof取自char,1.5%取自unsigned char。书籍1.2版本第1033页。
  2. 第1037页。

字符类型的表示中位数是无关紧要的。按定义,字符类型的字节数为1个。

编码指南开发人员有时将一个字节始终包含8个位。在字符类型为16位的主机上,这可能会导致错误地认为将sizeof应用于字符类型将返回值2。这些问题在其他地方讨论。


3

还需要记住的是,编译器静态知道 sizeof(char) 的值为1,并且它也知道将数字乘以静态的1意味着不需要进行乘法运算; 编译器会对其进行优化。在这些情况下,性能问题不应成为考虑的因素。


-4

使用sizeof(char)可以使您的代码更易读和可移植。

在x86上,我们都知道一个字符是1个字节。但明确地写下来有助于使您的意图更清晰,这总是一件好事。

此外,如果您的代码被放置在其他平台上,其中一个字符不是1个字节,那该怎么办?如果一个字符只有4位呢?

同意,这并非必要,但它不会减慢您的运行时间,并且在您需要将代码移植到不同架构的情况下,它将得到回报。


这就是我所询问的内容。正式地说,char 是内存中最小的可寻址块,但并不保证为8位。问题在于 malloc 和所有类似的东西是否基于 char 而非 8 位字节工作。 - sharptooth
1
啊好的,那么是的,malloc函数是按字符而非字节工作的。malloc(1)会返回一个大小为1个字符的内存块。 - samoz
2
-1 你的回答事实上是错误的。sizeof(char) 始终 是1。如果一个char只有4位,那么在该平台上4位就是1个字节,但是sizeof(char)被定义为1(字节),无论它有多少位。你讨论的问题是由CHAR_BITS宏解决的。 - Chris Lutz
3
UCHAR_MAX 的最小可能值是 255;因此 char 不能是 4 位。 - R.. GitHub STOP HELPING ICE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接