【问题标题】:Why write `sizeof(char)` if char is 1 by standard?如果 char 按标准为 1,为什么要写 `sizeof(char)`?
【发布时间】:2011-08-30 13:23:54
【问题描述】:

我正在做一些 C 编码,在阅读了一些 C 代码后,我注意到有类似 sn-ps 的代码

char *foo = (char *)malloc(sizeof(char) * someDynamicAmount);

所以我想问一下为 char 数组分配内存的 C-ish 方法是什么?使用sizeof(char) 并假定代码不会受到任何标准更改的影响,或者忽略它并直接使用该数字?

【问题讨论】:

    标签: c coding-style


    【解决方案1】:

    更Cish的方式是

    char* foo = malloc(someDynamicAmount * sizeof *foo);
    

    引用变量而不是类型,因此不需要类型。 并且没有强制转换 malloc 的结果(这是 C++ish)。

    【讨论】:

    • 这样更好,因为如果foo 的类型改变了,它仍然会分配正确的数量。这对于在 memcpy 中执行此操作尤为重要。
    • "which is C++ish" - 好吧,万一您在 C++ 中使用 malloc,您将不得不强制转换返回值。我怀疑这是一个被误导的 C 习语,也许之前是一个 C++ 习语。我觉得有趣的是,有一个针对 C 代码的 CERT 建议说您应该始终转换 malloc 的返回值,而另一个说您永远不应该这样做。
    • @James:在我看来,您可以选择重复类型(可能会变得不同步并引入错误),或者重复变量名称。除非您的变量遮盖了另一个变量,否则更改变量的名称并且未能更新 malloc 参数会导致编译失败(“foo 未定义”),因此它是一种更安全的重复。
    • @Steve,有目的地设计一个库,以便它可以在 C 和 C++ 中编译,有时是正确的做法。认为使用 C++ 编译器编译随机 C 代码无需修改即可工作是一种错觉。但这比在 32 位平台上开发代码而不过多关注 64 位平台上的可移植性要少。
    • @Steve Jessop:在 C89 之前,*alloc 函数返回 char * 而不是 void *,因此演员 必要的。这就是为什么这种做法如此难以根除的部分原因。年长的程序员可能仍然出于习惯这样做,当然,任何看过他们编写的代码或书籍的人都会看到这种做法并复制它。一旦我意识到不再需要这样做,我花了一段时间才停止这样做。
    【解决方案2】:

    sizeof(char) 是为了说明意图。如果有人决定他希望 foo 成为 int,他知道他需要这样做 sizeof(int) 才能继续工作。

    或省略它并使用数字

    另外,使用幻数并不是很好的编码习惯。

    【讨论】:

    • 这不是幻数。我的意思是具有一些 int 值的变量。
    • 即使编译器设置为发出所有可能的警告,int *foo = malloc(sizeof(char) * some_quantity) 也会毫无怨言地编译,默默地创建一个错误。
    • sizeof (*foo) 是 goo 的方式
    • @Blrfl 不一定是错误 - int *foo = malloc(sizeof(char) * 4) 是为 32 位整数分配内存的有效代码 ...
    • @0x69:这在int 不是四字节或char 不是八位的系统上严重失败。该标准允许两者,并且这两种情况都存在于现实世界中。如果您需要一个已知大小的整数,<stdint.h> 会提供类型来处理它。
    【解决方案3】:

    恕我直言,最好的做法是写sizeof(*foo)。那你就是 如果 foo 的类型发生变化并且 sizeof 未更正,也包括在内。

    【讨论】:

      【解决方案4】:

      比较:

      float*baz = malloc(sizeof(float) * someDynamicAmount);
      int  *bar = malloc(sizeof(int)   * someDynamicAmount);
      char *foo = malloc(sizeof(char)  * someDynamicAmount);
      

      对比:

      float*baz = malloc(sizeof(float) * someDynamicAmount);
      int  *bar = malloc(sizeof(int)   * someDynamicAmount);
      char *foo = malloc(someDynamicAmount);
      

      我喜欢第一个版本。你更喜欢第二种吗?

      【讨论】:

      • 因此,如果代码 sn-p 是列表的一部分,这些列表的内在相关性如此之高,以至于您将它们水平对齐 - 如此相关以至于如果您稍后为 long long 添加一个,那么您'很高兴差异显示除了添加的那一行之外还更改了 3 行 -- 然后 可以写 sizeof(char) ;-p
      • @Steve:这与列表无关。这不是因为您碰巧对它的大小有保证,就将 char 的一般模式排除在外。
      • 不过,您可以通过两种方式查看它。如果您认为malloc 采用“以字节为单位的大小”,而sizeof 是获取“以字节为单位”的大小的方式,那么编写sizeof(char) 是有意义的。但是如果你认为malloc 是一个“字符大小”,那么写sizeof(char) 是没有意义的,因为不需要“单位转换”。对于这种心态,这就像写if (something() != false)。如果我为 I/O 或字符串操作分配字节缓冲区,我不会写 sizeof(char)。如果代码几乎是通用的,但目前恰好使用char,我会这样做。
      • ...好吧,那是我会为大小写一些东西。如果由于某种原因不适合以 AProgrammer 的方式编写,则应为 sizeof(char)。在实践中,对于读取缓冲区,我写了char *foo = malloc(BUF_SIZE);,在那个几乎通用的代码中,我写了char *foo = malloc(sizeof(*foo)*ARRAY_SIZE)。我不会在我的代码中添加我认为是噪音的东西,以便将前者视为与后者相同的“一般模式”的一部分。
      • @Steve Jessop:精通某种语言的程序员会训练他们的眼睛来发现该语言的潜在 wtfs。由于几乎所有的 malloc 都必须有 sizeof(),如果在浏览代码时你看到 malloc 没有一个,那么你将不得不暂停并四处寻找它的类型,现在你意识到你已经浪费了你的时间暂停,因为结果是成为一个字符。这种暂停可能会破坏您当前正在思考的内容,并且可能会累积。保持一致是有好处的。
      【解决方案5】:

      你是对的,按照标准,乘法是无关紧要的。也就是说,看起来有人养成了保持一致的习惯。如果您始终使用sizeof(),无论类型如何,您都不会忘记。

      char *foo = (char *)malloc(sizeof(char) * someDynamicAmount);
      int  *bar = (int  *)malloc(sizeof(int)  * someDynamicAmount);
      

      【讨论】:

        【解决方案6】:

        常用的成语是

        T *p = malloc(N * sizeof *p);
        

        T *p;
        ...
        p = malloc(N * sizeof *p);
        

        这样您就不必担心类型。

        【讨论】:

          【解决方案7】:

          引用C99 standard,第 6.5.3.4 节 sizeof 运算符

          当应用于具有 char、unsigned char 或 signed char 类型的操作数时, (或其合格版本)结果为 1。当应用于具有数组的操作数时 类型,结果是数组中的总字节数。应用于操作数时 具有结构或联合类型,结果是此类对象中的总字节数, 包括内部和尾随填充。

          也就是说,sizeof char == 1 按标准,而不是按实现。因此,在为字符和类似情况调用 malloc() 时省略 sizeof(char) 是绝对正确的。恕我直言,未来的 C 标准不太可能允许特定于实现的大小 char,因为太多的代码已经依赖它为 1 并且向后兼容性在 C 中非常重要。

          因此,问题只是关于风格,而不是正确性,在这里我支持AProgrammer's answer

          【讨论】:

            【解决方案8】:

            编写sizeof(char) 并不是针对标准中可能发生的更改“为您的代码“防患于未然”。这通常表明完全误解了sizeof 的含义以及C 中内存对象的整个基本模型——在C 标准语言中称为类型表示sizeof 运算符甚至存在或有意义的唯一原因是因为 C 指定对象在内存中以可能的最小单位 unsigned char 具有“表示”。如果不是这样,存储会更加抽象,并且不会使用sizeof 和相关的指针算法。

            sizeofchar单位定义,即sizeof(T)==N 表示T 类型占用Nchars。鉴于此,sizeof(char) 完全是愚蠢的;它试图测量char 占用了多少chars。

            【讨论】:

            • sizeof 以字节为单位,而不是字符,字符是 sizeof 的特殊情况,但这与说 sizeof 以字符为单位完全不同。
            • @jmoreno:-1'ing 我的回答并不能改变你错的事实。 “字节”在 C 标准中几乎没有使用,在使用它的地方,它与 char 同义。如果不是“多字节字符”这个短语,它可能根本不会被添加。
            • 其实他是对的,sizeof()返回的是chars的个数,而不是字节数。根据定义,sizeof(char)1
            • @R: C99 6.5.3.4 表示The sizeof operator yields the size (in bytes) of its operandWhen applied to an operand that has type char, unsigned char, or signed char, (or a qualified version thereof) the result is 1。我看到它在这里定义为字节。
            • -1,表示“这完全是一种误解……”。您看不到写它的理由(因为它始终为 1)这一事实并不意味着其他写它的人一无所知。与您的分歧不构成“完全误解”。
            【解决方案9】:

            这完全是编码风格的问题。存在多种样式。

            要真正回答这个问题,人们会写

            malloc(n * sizeof(char))
            

            保持所有使用malloc 的代码一致。下次他们可能需要int,然后他们可以用同样的方式编写代码,

            malloc(n * sizeof(int))
            

            所以之所以这样做是为了保持编码风格一致。尽管sizeof(char) 确实保证始终为 1,因此是多余的。这是一种编写自文档化代码的方法。


            然而,在 C 语言中使用 malloc 最常见的方式可能是

            type* t = malloc(n * sizeof(*t));
            

            或 100% 等效:

            type* t = malloc(n * sizeof *t);
            

            由于sizeof 的运算符未评估副作用,因此此代码是安全的,即使变量t 尚未初始化。


            第三种可能的样式是迂腐正确的样式,它会使用数组指针,因为我们分配的实际上是数组:

            type (*t)[n] = malloc( sizeof(type[n]) );
            

            就类型正确性而言,这可能是最正确的方法。分配数组的大小,我们用数组指针指向分配的数组。

            但是,这种风格的问题在于数组指针给语法增加了额外的复杂性:您必须将该数组取消引用为(*t)[i],而不是t[i]。它使代码更难阅读。 (另外,如果 n 不是整数常量表达式,代码将无法在旧的、过时的 C 编译器上编译。)

            【讨论】:

              【解决方案10】:

              编码风格也可以写成:-

              char *foo = (char *)malloc(1 * someDynamicAmount);
              

              但是这样做是为了可以根据需要的基本数据类型的数量来增加动态分配的内存。如果你想增加 100 个字符,它将增加 100 个字符。如果可能不需要,但如果我们写 101 或 102 这样做会浪费内存。根据基本数据类型来做不会浪费任何内存空间

              【讨论】:

                【解决方案11】:

                该标准对常见类型的大小故意含糊其辞。 [Wikipedia]

                虽然char 的大小不太可能不会改变,但short 的大小已经改变了。惯用的方式是:

                type_t *foo = malloc(sizeof(type_t) * someDynamicAmount);
                

                对于任何类型(普通或复杂)type_t 或

                type_t *foo = malloc(sizeof(*foo) * someDynamicAmount);
                

                这样您就可以决定稍后对 foo 的类型进行更改,并且只在一个地方进行更改。

                【讨论】:

                • sizeof(char)为1的标准保证。
                • @AProgrammer 当然可以,但是您遇到了一致性问题。
                • “愚蠢的一致性是小头脑的妖精,受到小政治家、哲学家和神学家的崇拜”。我已经和 Armen 有过这个论点,但在几乎所有我使用 char 缓冲区的情况下,它与其他数组“一致”并没有任何好处。我几乎可以观察到,有时我会写malloc(strlen(x) + strlen(y) + 1),然后开始写malloc(sizeof(int)*(someDynamicAmount + 0 + 0)) 以保持一致性。
                • 然后我没有继续给出和解释与 35 票赞成的答案相同的答案。并且仅仅因为您现在选择char 输入并不意味着您以后不会决定更改为某种 Unicode 类型并且需要除 char 之外的其他类型。然后你需要加回 sizeof(type_t)。是的,我遇到过这样的情况,我从一个 char 数组开始,然后在进一步的工作表明 char 不足以满足我的使用后去别的地方。
                【解决方案12】:

                想想 unicode 和多字节字符串。如果 char 表示字符串中的单个字符,它实际上可以占用多个字节,导致 sizeof() > 1

                【讨论】:

                • -1 sizeof (char) 定义为 1。字符的多字节表示与它无关。这就是说,虽然sizeof (char) 总是1,CHAR_BIT 不一定总是8。基本类型的大小必须符合这个关系:sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)
                猜你喜欢
                • 1970-01-01
                • 2018-03-05
                • 2021-12-17
                • 2012-08-18
                • 2011-01-16
                • 2021-10-31
                • 2014-05-25
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多