How to get string length in the array at compile time?

Viewed 427

My question is related to this but from slightly different site.

Say we have the same array

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

Okay, compiler knows the length of array and we can get it in compile time: sizeof(data)/sizeof(data[0]). At the same time compiler knows the size of each string, say the result of strlen(data[1]) is actually known at compile time. But strlen is runtime call, and sizeof[i] will always return the sizeof(char *) value. Is there any way to get the length of each string of array without separate definition of each string as variable or macro?

2 Answers

UPDATE 2

Per your request, I will unequivocally state there is no part of the standard that allows you to obtain the length of a constant string except through the use of functions like strlen. You won't be able to create a macro that can do what you want.

You wrote:

strlen is runtime call

It can be a run-time call or a compile-time call depending upon the compiler's optimization level, which, so far, no other person has mentioned. I'm a huge fan of letting the compiler do the work for me.

$ 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

UPDATE 1, prompted by chqrlie. See the instruction at 100003f7b. Altering the number of characters in that string will produce a different constant being loaded into the esi register.

$ 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

But even if it is a run-time call, there are two things to remember:

  1. The cost of an optimized strlen call is quite small compared to many other operations that you would probably perform on the string.
  2. You can minimize the frequency of your calls to strlen with responsible factoring.

You can if you don't mind changing the definition of data a bit.

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")
};
Related