【问题标题】:strlen in the C preprocessor?C预处理器中的strlen?
【发布时间】:2011-06-28 16:02:30
【问题描述】:

是否可以在C预处理器中实现strlen()

给定:

#define MYSTRING "bob"

是否有一些预处理宏,X,让我说:

#define MYSTRING_LEN X(MYSTRING)

【问题讨论】:

  • 为什么需要这样做?宏展开为 strlen("bob") 不行吗?
  • #define MYSTRING_LEN 3 有什么问题?我可以看到它会很有用,因为您只需更改 MYSTRING 而不必担心 MYSTRING_LEN...
  • 我正在开发一个微型嵌入式系统,并且代码空间严重不足。否则不需要 strlen() 函数。我试图避免硬编码所有字符串的长度常量。
  • 是否还有另一个问题是关于如何为指向常量字符串的指针常量数组(如static const char* strings[] = { "foo", "hello" })执行此操作?在这种情况下,下面方便的宏似乎崩溃了,例如enum string_names { S_FOO = 0, S_HELLO }; static const size_t const string_lengths[] = { STRLEN(strings[S_FOO]), STRLEN(strings[S_HELLO]) } 这里所有的string_lengths 成员都是8(指针的大小)而不是字符串的长度。也许我需要放弃并在运行时使用strlen:/

标签: c c-preprocessor strlen


【解决方案1】:

它不使用预处理器,但 sizeof 在编译时被解析。如果你的字符串在一个数组中,你可以在编译时使用它来确定它的长度:

static const char string[] = "bob";
#define STRLEN(s) (sizeof(s)/sizeof(s[0]))

请记住,上面的STRLEN 将包含空终止符,这与strlen() 不同。

【讨论】:

  • 对,所以要让它表现得像strlen,你能不能不做#define STRLEN(s) ( (sizeof(s)/sizeof(s[0])) - sizeof(s[0]) )
  • @nmichaels:是的,是的,我做到了。我实际上输入了-1,然后返回并(在)更正了它。
  • 一个问题是您实际上是在询问数组的大小,而不是 C 风格的字符串。例如static const char string[] = "bob\0and\0mary" 会报告大于"bob" 的长度。
  • @Edwin:我认为#define BOB_LEN 3 比我所描述的更有可能以眼泪收场,但你的观点是正确的。经常用 C 编写代码意味着要尝试错误的命运。决定实际上取决于您愿意做出哪些权衡。
  • 实用程序头文件中的常见定义是#define NELS(array) (sizeof(array)/sizeof(array[0])),表示“元素数量”。这适用于任何数组,并且比 STRLEN 误导性要小得多。
【解决方案2】:

你可以这样做:

#define MYSTRING sizeof("bob")

在我的机器上是 4,因为末尾添加了 null。

当然,这只适用于字符串常量。


使用 MSVC 16 (cl.exe -Wall /TC file.c):

#include "stdio.h"
#define LEN_CONST(x) sizeof(x)

int main(void)
{
    printf("Size: %d\n", LEN_CONST("Hej mannen"));

    return 0;
}

输出:

Size: 11

字符串的大小加上 NUL 字符。

【讨论】:

  • 这不是 MS 扩展,它是标准 C。字符串文字(当不用作数组的初始化器时)是 char 的数组,sizeof 在数组上工作为与任何其他类型。
  • @caf:很高兴知道,我想,但不确定。
【解决方案3】:

通常,C 预处理器实际上并不转换任何数据,它只是替换它。这意味着您可能能够执行这样的操作,前提是您使用数据实现(功能性)持久数据结构污染您的 C 预处理器命名空间。

也就是说,你真的不想这样做,因为一旦你传入一个字符串以外的东西,整个“添加”的功能就会失败。 C 预处理器没有数据类型的概念,也没有内存取消引用的概念(如果您想要存储在变量中的字符串的长度,这很有用)。基本上,这将是一个有趣的“看看你能走多远”的练习,但最终,你会得到一个 MYSTRING_LEN,它只会带你到目标的一小段距离。

此外,C 预处理器缺少名称空间意味着这样的宏扩展系统是不可包含的。必须注意防止生成的名称干扰其他有用的宏。最后,您可能会因为任何重要用途而耗尽预处理器中的内存,因为预处理器并不是真正构建为保存每个被转换为“单位”标记的字符的名称,以及一个名称将每个“单位”标记压缩为其最终的十进制表示法。

【讨论】:

    【解决方案4】:

    是的: #define MYSTRING_LEN(s) strlen(s)

    在大多数编译器中,这将为常量参数生成一个编译时常量......你不能做得比这更好。

    换句话说:您不需要宏,只需使用 strlen;编译器足够聪明,可以为您完成工作。

    【讨论】:

    • “大多数编译器”? Gcc,是的,MSVC 10,不是。
    • 另外,int f() {char XYZ[strlen(s)]; ...} 不是有效的 C89 程序,而 int f() {char XYZ[sizeof(s)]; ...} 是。
    • @MaciejPiechotka 问题是关于 strlen,而不是 sizeof(具有不同的值)。
    • @JimBalter:“计算机科学中有两个难题:缓存验证、变量替换和非一错误。” - 是的,我应该将 -1+1 添加到其中之一,但我的观点仍然存在 - 即使编译器可能会对其进行优化,但这并不意味着它适用于所有情况。
    • sizeof 运算符可用于常量表达式(可变长度数组除外)。 strlen 不可能。可以假设使用预处理器意味着需要一个常量表达式,否则不会使用预处理器来完成。所以是的,你可以比strlen 做得更好,并使用sizeof("foo") 产生一个常量表达式。与编译器优化为编译时常量的表达式相比,它可以在更多的上下文中使用。这样的优化不会改变程序的意义,也不会把表达式变成常量表达式。
    猜你喜欢
    • 2012-05-18
    • 1970-01-01
    • 2010-09-07
    • 1970-01-01
    • 2011-07-02
    • 2014-02-26
    • 2020-02-09
    • 2019-07-08
    相关资源
    最近更新 更多