【问题标题】:How to get string length in the array at compile time?如何在编译时获取数组中的字符串长度?
【发布时间】:2021-05-17 14:35:41
【问题描述】:

我的问题与this 有关,但来自略有不同的网站。

假设我们有相同的数组

const char *data[] = {
    "Str1",
    "Str Two",
    "Long string three",
};

好的,编译器知道数组的长度,我们可以在编译时得到它:sizeof(data)/sizeof(data[0])。同时编译器知道每个字符串的大小,比如strlen(data[1]) 的结果实际上在编译时是已知的。但strlen 是运行时调用,sizeof[i] 将始终返回sizeof(char *) 值。有什么方法可以获取数组中每个字符串的长度没有将每个字符串单独定义为变量

【问题讨论】:

  • sizeof(data[0])/sizeof(data[0][0]);
  • 编译器也知道sizeof("whatever"),所以让你的字符串宏并使用宏来初始化,并在你需要大小的地方使用sizeof(macro)
  • @hetepeperfan 如果我错了,请纠正我,但这不只是将 char 指针的大小除以 char 的大小吗?
  • strlen 有什么问题?
  • @shved 宏不是变量。它不消耗任何内存。

标签: arrays c string


【解决方案1】:

更新 2

根据您的要求,我将明确指出,除了使用 strlen 之类的函数外,标准中没有任何部分允许您获取常量字符串的长度。你将无法创建一个可以做你想做的事情的宏。

你写道:

strlen 是运行时调用

它可以是运行时调用或编译时调用,具体取决于编译器的优化级别,到目前为止,还没有其他人提到过。我非常喜欢让编译器为我完成这项工作。

$ cat t.c
#include <stdio.h>
#include <string.h>
int main (int argc, char *argv[]) {
    char *data[] = {"abc"};
    printf("sl=%ld\n", strlen(data[0]));
}
$ gcc t.c
$ nm a.out
                 U ___stack_chk_fail
                 U ___stack_chk_guard
0000000100008018 d __dyld_private
0000000100000000 T __mh_execute_header
0000000100003f00 T _main
                 U _printf
                 U _strlen
                 U dyld_stub_binder
$ gcc -O1 t.c
$ nm a.out
0000000100008008 d __dyld_private
0000000100000000 T __mh_execute_header
0000000100003f70 T _main
                 U _printf
                 U dyld_stub_binder

更新 1,由 chqrlie 提示。请参阅 100003f7b 处的说明。更改该字符串中的字符数将产生一个不同的常量被加载到 esi 寄存器中。

$ objdump --disassemble-symbols=_main a.out

a.out:  file format mach-o 64-bit x86-64


Disassembly of section __TEXT,__text:

0000000100003f70 <_main>:
100003f70: 55                           pushq   %rbp
100003f71: 48 89 e5                     movq    %rsp, %rbp
100003f74: 48 8d 3d 33 00 00 00         leaq    51(%rip), %rdi  # 100003fae <dyld_stub_binder+0x100003fae>
100003f7b: be 03 00 00 00               movl    $3, %esi    #### This is the length of the string constant
100003f80: 31 c0                        xorl    %eax, %eax
100003f82: e8 05 00 00 00               callq   0x100003f8c <dyld_stub_binder+0x100003f8c>
100003f87: 31 c0                        xorl    %eax, %eax
100003f89: 5d                           popq    %rbp
100003f8a: c3                           retq

但即使是运行时调用,也有两点要记住:

  1. 与您可能对字符串执行的许多其他操作相比,优化的strlen 调用的成本非常小。
  2. 您可以通过负责任的保理最大限度地减少致电strlen 的频率。

【讨论】:

  • 符号中没有_strlen这一事实并不意味着它是在编译时计算出来的……它可能只是被内联了……
  • 完美点!但这又是编译器的决定,而不是标准。我们无法保证strlen 会得到优化。
  • 我明白了,没有人建议绝对有效的变体,所以 Eugeny 在评论中说最短的答案是 NOT,但你是最接近解决方案的。您能否添加简短的您不能回答?
  • @shved 你的意思是“你能说没有这样的方法可以用宏来实现吗”?
  • @JeffHolt 我的意思是没有像super_sizeof(data[i]) 这样的魔术调用,它在编译时返回数组中字符串的大小,如sizeof(data),而不添加额外的变量或宏。但大多数时候编译器会优化strlen 调用以完全按照我的要求进行。说清楚。
【解决方案2】:

如果您不介意更改data 的定义,可以。

struct Elem
{
  size_t len;
  const char* str;
}

#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)))
#define ELEM(x) ({ ARRAY_SIZE((x)) - 1, ((x)) })

const Elem[] data = {
  ELEM("Str1"),
  ELEM("Str Two"),
  ELEM("Long string three")
};

【讨论】:

  • 不错的尝试,但在像memcpy(dst, data[1], sizeof(data[1]));这样的通用方法中没有用
猜你喜欢
  • 2011-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-29
  • 1970-01-01
  • 2010-09-18
  • 2019-12-08
  • 1970-01-01
相关资源
最近更新 更多