3

I know the problem seems weird but I need to initialize (or convert) a constant string array in C.

The problem is that the string array is initialized dynamically but an API function I'd like to use only accepts constant string arrays.

I know that this works:

const char *const arr[] = { "test" };

But again: Since I don't know how many items the array will have nor I know the content pre runtime, I can't initialize the array that way.

So of course this won't work

const char *const arr[1]; 
arr[1] = "test"; // won't work

My question is: Is it possible to convert somehow the dynamically string array to a read-only one? Or is there a way to initialize the array dynamically once?

EDIT 1: My exact problem

int len = 8;
const char *names1[8] = {"test0","test1","test2","test3","test4","test5","test6","test7" }; // not what I'm looking for
const char *names2[len]; 
const char *names3[len];

// nearly what I'm looking for
for(int j=0; j<len; j++) {
    names2[j] = "test";
}

// exactly what I'm looking for
for(int j=0; j<len; j++) {
    sprintf(names3[j],"%s%d","test",j); // discards 'const' qualifier
}

// ...

Cudd_DumpDot(gbm, 1, ddnodearray, names1, NULL, outfile);
Cudd_DumpDot(gbm, 1, ddnodearray, names2, NULL, outfile);
Cudd_DumpDot(gbm, 1, ddnodearray, names3, NULL, outfile); // won't work

Okay this is my progress so far. The method with names2 is indeed working but I'd like to use sprintf (as shown with names3) since I need to append j in this case. And this would wound the const qualifier.

Sonnywhite
  • 136
  • 11
  • 1
    Can you clarify in what way the API only accepts constant string arrays? Is it just declared `const char`, then you can pass `char` arrays as well. Or does it test the memory region the string is in? – Karsten Koop Oct 19 '17 at 16:01
  • 1
    If a function argument has the `const` modifier, it only means that the function will not mutate the data you pass. I.e. you can easily pass your dynamically allocated array as it is. – Eli Korvigo Oct 19 '17 at 16:03
  • @KarstenKoop the requested argument from the API is `char const *const * inames,` and if I try to pass a string array gcc says: `expected ‘const char * const*’ but argument is of type ‘char **’` – Sonnywhite Oct 19 '17 at 16:06
  • @EliKorvigo but there is the warning `expected ‘const char * const*’ but argument is of type ‘char **’` from gcc :/ – Sonnywhite Oct 19 '17 at 16:08
  • Possible duplicate of [Warning when passing non-const parameter to a function that expects const parameter. Is there a better way?](https://stackoverflow.com/questions/12992407/warning-when-passing-non-const-parameter-to-a-function-that-expects-const-parame) – Karsten Koop Oct 19 '17 at 16:10
  • @Sonnywhite see my answer, you **must** have the first `const` in the type you pass, which refers to the data of the individual strings (the reasons are complicated, there are other questions explaining them), but you don't need the second one (referring to the pointers stored in the array). -- so you CAN put pointers in your array before passing it. –  Oct 19 '17 at 16:10
  • @Sonnywhite and [here's the reason why you need the first `const`](https://stackoverflow.com/questions/45011978/const-causing-incompatible-pointer-type-why-only-for-double-pointers/45013356#45013356) –  Oct 19 '17 at 16:18
  • @KarstenKoop indeed a duplicate, but the answer given there is (IMHO) quite weak ... –  Oct 19 '17 at 16:25
  • @Sonnywhite The [comment](https://stackoverflow.com/questions/46834064/c-initialize-a-constant-string-array-dynamically#comment80616080_46834064) does not match the posted code. The comment argument points to a `char *`. Code's `arr` elements are `const char *`. So the goal in your comment differs from the question's explanation. Best to post your _true_ failing code. – chux - Reinstate Monica Oct 19 '17 at 16:41
  • @chux I added the actual code and the API calls that causing the troubles.. – Sonnywhite Oct 19 '17 at 17:04
  • Solved it with first `sprintf` the content to a non constant string array and then copy the contents to the const array with the method of @FelixPalmen. Thanks to all so far! – Sonnywhite Oct 19 '17 at 17:18
  • 1
    `const char *names3[len]; ... sprintf(names3[j],"%s%d","test",j);` is bad code. – chux - Reinstate Monica Oct 19 '17 at 17:26

3 Answers3

3

Technically there is nothing stopping you from casting the pointer to (char *) and then setting the elements with memset or alike.

However this invokes undefined behaviour since the compiler can put it into read-only marked memory.

Excerpt from an answer on another SO question:

The const qualifier is an instruction to the compiler to reject code that attempts to modify that object directly; attempts to modify the object indirectly (as you do in the second code snippet) results in undefined behavior, meaning any result is possible.

There is no way (without invoking UB) to change a constant after its initialisation - so don't do that.

UPDATE As @chux pointed out in the comments it is indeed possible to dynamically initialize local variables.

Marco
  • 7,007
  • 2
  • 19
  • 49
  • `int const x[2] = { rand(), rand()};` is a counter example to " no way ... to initialize a constant array dynamically. No UB. Your quote is about _assignment_. ("attempts to modify"). _Initialization_ is something else - it is done at definition time and may be dynamic. – chux - Reinstate Monica Oct 19 '17 at 17:39
  • @chux Doesn't work for me: "test.c:7:19: error: initializer element is not constant" – Marco Oct 19 '17 at 17:49
  • @chux "Moreover, in C language the term "constant" refers to literal constants (like 1, 'a', 0xFF and so on) and enum members. Const-qualified objects (of any type) are not constants in C language terminology. They cannot be used in initializers of objects with static storage duration, regardless of their type." https://stackoverflow.com/questions/3025050/error-initializer-element-is-not-constant-when-trying-to-initialize-variable-w – Marco Oct 19 '17 at 17:52
  • 1
    `int const x[2] = {fn(),3};` is your example is in global space. `int const x[2] = {fn(),3};` works fine as a local object. – chux - Reinstate Monica Oct 19 '17 at 17:55
  • OK, so code can locally create a `const` array dynamically. – chux - Reinstate Monica Oct 19 '17 at 17:57
  • 1
    Aside: C does not refer to `1, 'a', 0xFF` as literal constants. The spec calls them _integer-constant_. _Literal_ is used for _string literal_ and _complex literals_. – chux - Reinstate Monica Oct 19 '17 at 18:00
  • 1
    @chux Thanks! I've changed my answer accordingly. – Marco Oct 19 '17 at 18:03
2

an API function I'd like to use only accepts constant string arrays.

That's no reason to pass an array of constant pointers ... the conversion to const (in this case constant array elements) is allowed (and even implicit), so the following (nonsensical) code compiles just fine:

const char *test(const char *const *foo)
{
    return foo[0];
}

int main(void)
{
    const char *arr[10];
    arr[0] = "Foobar";

    const char *x = test(arr);
    return (int) *x;
}
  • That only works flawlessly if `arr` is an array of pointers to *`const`*. If you declare it with `char* arr[10];`, you get a warning from `gcc` (-Wincompatible-pointer-types). – cmaster - reinstate monica Oct 19 '17 at 16:43
  • Okay thanks, it actually works ... but there is one more problem: At the moment I set the content with `sprintf` since the content consist of strings and integers and stuff ... but with doing that gcc has now this problem: `passing argument 1 of ‘sprintf’ discards ‘const’ qualifier from pointer target type` – Sonnywhite Oct 19 '17 at 16:44
  • @Sonnywhite Allocate your array members as `char *` (e.g. with `char *x = malloc(size)`) and sprintf there, then put this pointer in your array like `a[0] = x;`. Again, this conversion is allowed and implicit. –  Oct 19 '17 at 17:25
1

Initialize a constant string array dynamically

Within a function, there are various ways to initialize a const string array at run-time.

// example
const char *s[2] = { (char [3]){ rand(), 0, 0},(char [3]){ rand(), 0, 0} }; 

Yet it appears OP needs only something like that.

Form the various strings, each in valid memory.

// Exmaple
#define SZ (4 + 11 + 1)
char buf[len][SZ];
for(int j=0; j<len; j++) {
  sprintf(buf[j],"%s%d","test",j);
}

Form an array of const char *

  const char *names[len];
  for(int j=0; j<len; j++) {
    names[len] = buf[len];
  }

Call Cudd_DumpBlifBody(). The char const *const * parameters can be called with type char const *const * or char const **

#include <stdio.h>
#include <stdlib.h>

typedef void DdManager;
typedef void DdNode;
int Cudd_DumpBlifBody(DdManager *dd, int n, DdNode **f, 
    char const *const *inames, 
    char const *const *onames, FILE *fp, int mv) {
  return 0;
}
#define SZ (4 + 11 + 1)

int sw(int len) {
  char buf[len][SZ];
  const char *names[len];
  for(int j=0; j<len; j++) {
    sprintf(buf[j],"%s%d","test",j);
    names[len] = buf[len];
  }
  char const *const *inames = names;
  char const *const *onames = names;
  return Cudd_DumpBlifBody (NULL, 0, NULL, inames, onames, NULL, 0);
}

Local objects like char buf[len][SZ]; could easlily get too large for local storage. Consider *alloc() if unsure or if len could be large.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256