Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't GLib use 'const' in these functions?

Tags:

c

constants

glib

I have a simple question about GLib.

I have the following code:

static const char *words[] = { "one", "two", "three", NULL };

void main() {
  puts(g_strjoinv("+", words));
}

This code prints one+two+three. It uses a GLib function which joins strings.

The signature of that function is:

char *g_strjoinv (const char *separator, char **str_array);

(To be exact, GLib uses gchar, not char, but let's ignore this.)

Now, I wonder why the parameter is char **str_array and not const char **str_array. It forces me to do an explicit cast to get rid of the compiler's warning ("expected 'char **' but argument is of type 'const char **'"):

  puts(g_strjoinv("+", (char **)words));

I look in GLib's reference and I see that all the functions there are defined this way: they accept char **, not const char **.

Why is that? Why doesn't GLib use const char **?

The need to use an explicit cast to get rid of the const makes my code less safe (because the compiler no longer checks the arguments' compatibility). It also makes me nervous because GLib doesn't "sign a contract" that say it won't change my data.

like image 819
Niccolo M. Avatar asked Jun 12 '14 17:06

Niccolo M.


1 Answers

The assumption in your question is that if the second argument of g_strjoinv() was declared as type const char **, then you can just as easily pass either a const char ** or a char ** to it.

Unfortunately, this isn't true. As explained in this question from the comp.lang.c FAQ, using a pointer to T (for any type T) where a pointer to const T is expected is OK, but only where the mismatch is at the top level of indirection. That is to say, you can pass a char * where a const char * is expected, but you cannot (without a cast) pass a char ** where a const char ** is expected, because in this case the mismatch is at the second level of indirection. The linked question explains in some detail why it works this way, which is "somewhat obscure".

So the upshot is there there is no type (in C) which will accept both a char ** and a const char ** without at least one of them requiring a cast. This being the case, there's probably not much reason to prefer either other than convenience, and the library authors apparently decided to go with the former. As a matter of pure speculation, I'd guess that wanting to pass a char ** is slightly more common than wanting to pass a const char **, and so the option they picked is probably more convenient.

In your case, you can just drop the const qualifier from the definition of your array. It might seem like bad form, if you like const, but since you have an array of pointers which point to string literals, your program is overwhelmingly likely to loudly complain and fail if anything attempts to write to them anyway, so you're not really losing safety in any meaningful way in this particular case. Even though it's undefined behavior to attempt to modify them, string literals have type array of char in C, not array of const char (unlike C++).

Slightly more obscure, but even if you could pass it as a const char **, the function here could still modify the pointers even if it couldn't modify what they point to, and just cause mayhem in a different way, so the promise the function made still wouldn't be guaranteeing that what you pass to the function wouldn't be changed in some way.

like image 132
Crowman Avatar answered Sep 20 '22 06:09

Crowman