Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper type for a dynamic array of const strings

I was always under the impression that const char **x was the correct type to use for a dynamically allocated array of const strings, like so:

#include <stdlib.h>

int main()
{
    const char **arr = malloc(10 * sizeof(const char *));
    const char *str = "Hello!";
    arr[0] = str;
    free(arr);
}

However, when compiling this code with VS2017, I get this warning on the free line:

warning C4090: 'function': different 'const' qualifiers

Is there something wrong with my code? FWIW, when I compile with GCC, I don't get any warnings, even with -Wall -Wextra -pedantic.

like image 840
Andrew Sun Avatar asked Mar 23 '17 06:03

Andrew Sun


People also ask

How do you declare a dynamic array of strings in C++?

It's easy to initialize a dynamic array to 0. Syntax: int *array{ new int[length]{} }; In the above syntax, the length denotes the number of elements to be added to the array.

Is string dynamic array?

Class std::string is already contains a dynamic string object and std::vector contains a dynamic array object.

How do you declare a dynamic string?

Allocating Strings DynamicallyEdit In duplicating a string, s, for example we would need to find the length of that string: int len = strlen(s); And then allocate the same amount of space plus one for the terminator and create a variable that points to that area in memory: char *s2 = malloc((len + 1) * sizeof(char));


2 Answers

There is nothing wrong with your code. The rules for this are found in the C standard here:

6.3.2.3 Pointers
A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type; the values stored in the original and converted pointers shall compare equal.

Meaning that any pointer to object type (to a variable) may be converted to a void pointer, unless the pointer is qualified (const or volatile). So it is fine to do

void* vp; 
char* cp; 
vp = cp;

But it is not ok to do

void* vp; 
const char* cp; 
vp = cp; // not an allowed form of pointer conversion

So far so good. But when we mix in pointer-to-pointers, const-ness is a very confusing subject.

When we have a const char** arr, we have a pointer to pointer to constant char (hint: read the expression from right to left). Or in C standard gibberish: a pointer to a qualified pointer to type. arr itself is not a qualified pointer though! It just points at one.

free() expects a pointer to void. We can pass any kind of pointer to it unless we pass a qualified pointer. const char** is not a qualified pointer, so we can pass it just fine.

A qualified pointer to pointer to type would have been char* const*.

Note how gcc whines when we try this:

char*const* arr = malloc(10 * sizeof(char*const*));
free(arr);

gcc -std=c11 -pedantic-errors -Wall - Wextra:

error: passing argument 1 of 'free' discards 'const' qualifier from pointer target type

Apparently, Visual Studio gives an incorrect diagnostic. Or alternatively you compiled the code as C++, which doesn't allow implicit conversions to/from void*.

like image 154
Lundin Avatar answered Sep 28 '22 06:09

Lundin


The assignment is valid for the same reason the following assignment is valid.

const char** arr = malloc(10 * sizeof(const char *));
void* p = arr;

This rule1 explains that the, if both operands are pointer types, the type left pointer is pointing to, must have the same qualifiers, as the type the right pointer is pointing to.

The right operand is a pointer that is pointing to a type that doesn't have any qualifiers. This type being type a type pointer to a const char (const char*). Don't let that const qualifier confuse you, that qualifier dont't belong to the pointer type.

The left operand is a pointer that is pointing to a type that also doesn't have any qualifiers. The type being void. So the assignment is valid.

If the pointer is pointing to a type that has qualifiers, then the assignment would not be valid:

const char* const* arr = malloc(10 * sizeof(const char *));
void* p = arr;    //constraint violation

The right operand is a pointer that is pointing to a type with the qualifier const, this type being a type const pointer to a const char (const char* const).

The left operand is a pointer that is pointing to a type without any qualifiers, this type being type void. The assignment violates the constraint1.


1 (Quoted from: ISO/IEC 9899:201x 6.5.16.1 Simple assignment Constraints 1)
the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;

like image 32
2501 Avatar answered Sep 28 '22 07:09

2501