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.

12
  • 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? Commented Oct 19, 2017 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. Commented Oct 19, 2017 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 **’ Commented Oct 19, 2017 at 16:06
  • @EliKorvigo but there is the warning expected ‘const char * const*’ but argument is of type ‘char **’ from gcc :/ Commented Oct 19, 2017 at 16:08
  • 1
    const char *names3[len]; ... sprintf(names3[j],"%s%d","test",j); is bad code. Commented Oct 19, 2017 at 17:26

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

7 Comments

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 Doesn't work for me: "test.c:7:19: error: initializer element is not constant"
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.
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 Thanks! I've changed my answer accordingly.
|
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;
}

3 Comments

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).
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 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.
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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.