C预处理器中的strlen是什么?

36

能否在C预处理器中实现strlen()函数?

已知:

#define MYSTRING "bob"

是否有一些预处理宏,X,可以让我这样说:

#define MYSTRING_LEN X(MYSTRING)

为什么需要这样做?宏展开为strlen("bob")不可以吗? - Ken Wayne VanderLinde
#define MYSTRING_LEN 3 有什么问题吗?虽然我能看出它很有用,因为你只需要更改 MYSTRING 而不必担心 MYSTRING_LEN... - James
12
我正在开发一个小型嵌入式系统,但代码空间非常紧缺。虽然不需要使用strlen()函数,但我希望避免为所有字符串硬编码长度常数。 - Joby Taffey
可能是Determine #defined string length at compile time的重复问题。 - jww
是否有其他问题讨论如何处理指向常量字符串的常量数组,例如 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 :/ - jacobq
4个回答

47

它不使用预处理器,但sizeof在编译时解析。如果您的字符串在数组中,则可以使用该数组在编译时确定其长度:

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

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


7
好的,要使它像strlen一样工作,你能不能只是这样做:#define STRLEN(s) ( (sizeof(s)/sizeof(s[0])) - sizeof(s[0]) ) - James
1
@nmichaels:是的,是的。我实际上输入了“-1”,然后回去更正了它。 - James
9
一个问题是你实际上在询问数组的大小,而不是 C 风格字符串的大小。例如,static const char string[] = "bob\0and\0mary" 会报告一个比 "bob" 更长的长度。 - Edwin Buck
2
@Edwin:我认为#define BOB_LEN 3更容易导致问题,但你的观点是正确的。在C中编写代码通常意味着冒险引发错误。决策实际上取决于你愿意做出什么样的权衡。 - nmichaels
2
实用程序头文件中的常见定义是 #define NELS(array) (sizeof(array)/sizeof(array[0])),表示“元素数量”。这适用于任何数组,并且比STRLEN更少误导性。 - Jim Balter
显示剩余7条评论

10

你可以这样做:

#define MYSTRING sizeof("bob")

因为末尾添加了空字符,所以在我的机器上显示为4。

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


在使用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 字符。

3
这不是MS的扩展,而是标准的C语言。一个字符串字面值(当它不被用作数组的初始化程序时)是一个char数组,而sizeof对于数组和其他类型一样有效。 - caf
@caf:好知道,我猜到了但不确定。 - Skurmedel

5

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

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

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


8
“大多数编译器”?是的,GCC支持,但MSVC 10不支持。 - Joseph Quinsey
2
同时,int f() {char XYZ[strlen(s)]; ...} 不是一个有效的 C89 程序,而 int f() {char XYZ[sizeof(s)]; ...} 则是一个有效的程序。 - Maciej Piechotka
1
@MaciejPiechotka 这个问题是关于strlen的,而不是sizeof(它有不同的值)。 - Jim Balter
2
@JimBalter:“计算机科学中有两个难题:缓存验证、变量替换和偏移错误。” - 是的,我应该在其中一个加上-1+1,但我的观点仍然成立 - 即使编译器可能会优化它,这并不意味着它适用于所有情况。 - Maciej Piechotka
3
sizeof 运算符可以用于常量表达式(变长数组除外),而 strlen 则不能。可以假定使用预处理器意味着需要一个常量表达式,否则不会使用预处理器。因此,您可以比使用 strlen 更好地使用 sizeof("foo"),这将产生一个常量表达式。它可以在比编译器优化为编译时常量更多的上下文中使用。这样的优化不会改变程序的含义,并且不会将表达式转换为常量表达式。 - Kuba hasn't forgotten Monica
显示剩余2条评论

1
通常情况下,C预处理器实际上并不会转换任何数据,它只是替换它。这意味着,如果您使用实现(功能)持久数据结构的数据来污染C预处理器命名空间,则可能能够执行此操作。
话虽如此,您真的不想这样做,因为一旦传入除字符串以外的其他内容,整个“添加”的功能将彻底失败。 C预处理器没有数据类型的概念,也没有内存解引用的概念(如果您想要在变量中存储的字符串的长度)。基本上,这将是一个有趣的“看看你能走多远”的练习,但最终,您将拥有一个仅能让您短暂接近目标的MYSTRING_LEN。
此外,C预处理器缺乏名称空间意味着这样的宏扩展系统无法被包含。人们必须注意保持生成的名称不会干扰其他有用的宏。最终,对于任何重要的用途,您可能会在预处理器中耗尽内存,因为预处理器实际上并不是为每个转换为“单位”令牌的字符保存名称,也不是为每个被压缩成其最终十进制表示形式的“单位”令牌保存名称而构建的。

回复:“C预处理器实际上并不转换任何数据,它只是替换它。” C预处理器能够在条件包含中评估常量表达式。例如:在#if 1+2中,1+2根据常量表达式的规则进行评估。 Note: The translation is in Simplified Chinese. - pmor

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