Скопируйте "указатель на const" в "указатель на не const" через memcpy - PullRequest
0 голосов
/ 07 октября 2019
const void *a = something;
void *b = a;

возвращает предупреждение:

предупреждение: инициализация отбрасывает квалификатор 'const' из целевого типа указателя [-Wdiscarded-qualifiers]

Безопасно ли (четко определенное поведение) скопировать указатель на const на указатель на не const через memcpy, чтобы избежать предупреждений?

/* Linear search */
void *vector_lsearch(const void *key, const void *base, int (*comp)(const void *, const void *))
{
    const struct vector *vector = CONST_VECTOR(base);
    void *cast[1];
    void *data;

    /* Skip const to non const warning */
    data = *(void **)memcpy(cast, &base, sizeof base);

    for (size_t item = 0; item < vector->size; item++)
    {
        if (comp(data, key) == 0)
        {
            return data;
        }
        data = (unsigned char *)data + vector->szof;
    }
    return NULL;
}

Ответы [ 3 ]

2 голосов
/ 07 октября 2019

Копировать указатель безопасно. Потенциальная проблема безопасности - при использовании b. Так как он объявлен как указатель на непостоянные данные, вы можете назначить его через указатель, например, *(int *b) = 1; Если something - это постоянные данные, это приведет к неопределенному поведению.

Если вы используете *Указатель 1006 * в качестве канала, который в конечном итоге передаст указатель в функцию, которая преобразует указатель обратно в исходный тип (подобно тому, как qsort() использует аргумент указателя), вы должны иметь возможность игнорировать это предупреждение. Можно ожидать, что эта функция приведёт его к указателю на const и не будет пытаться назначить его через него.

Я не думаю, что есть способ объявить универсальный указатель, который можно использовать в качестве каналадля постоянных или неконстантных данных. Если вы объявите его неконстантным, вы получите предупреждение при назначении ему константного указателя;если вы объявите его const, вы не сможете использовать его для функций, которым нужен неконстантный указатель.

2 голосов
/ 07 октября 2019

Это предупреждение приходит от удаления спецификатора const как части инициализации;простое добавление явного приведения также позволит избежать предупреждения.

const void *a = something;
void *b = (void *)a;

В разделе 6.5.4 стандарта описаны ограничения на неявное приведение указателей:

Преобразования, в которых используются указатели,кроме случаев, разрешенных ограничениями из 6.5.16.1, должны быть определены посредством явного приведения.

И единственным ограничением на указатели явного приведения является:

Тип указателя не должен быть преобразован в любой плавающий тип. Плавающий тип не должен быть преобразован в любой тип указателя.

В соответствующем разделе первого правила, 6.5.16.1, есть следующее правило для простого присваивания:

левый операнд имеет атомарный, квалифицированный или неквалифицированный тип указателя, и (учитывая тип, который левый операнд будет иметь после преобразования в lvalue) оба операнда являются указателями на квалифицированные или неквалифицированные версии совместимых типов, а тип, на который указывает левый, имеет всеквалификаторы типа, на который указывает право;

Наконец, раздел 6.7.3 о квалификаторах имеет:

Если предпринята попытка изменить объект, определенный с помощьютип с константным квалификацией посредством использования lvalue с неконстантным типом, поведение не определено.

Это предложение не дает большого значения, если lvalue с неконстантными типами сдоступ к объектам, определенным с помощью константных типов, сам по себе не определен. Это указывает на то, что вы можете явно привести const void * к void * и избежать предупреждения, не вводя неопределенное поведение, так как предупреждение относится конкретно к недопустимому использованию неявного приведения посредством простого присваивания, а не к общему возражению против отбрасывания квалификатора const.

1 голос
/ 07 октября 2019

Инициализация void *b = a; недопустима C, она нарушает правило простого присваивания C17 6.5.16.1 (инициализация следует правилам присваивания), в котором говорится, что для того, чтобы выражение было действительным:

... тип, на который указывает левый, содержит все квалификаторы типа, на который указывает правый.

Возможно, вы захотите скомпилировать с -pedantic-errors, чтобы получить ошибки вместопредупреждений о нарушениях языка C.


Что касается четко определенного поведения, то есть до тех пор, пока вы отменяете указатель, используя правильный тип фактических данных, это четко определенное поведение, итип самого указателя не имеет большого значения.

Я даже не понимаю, почему вам нужно преобразовать в void*, поскольку формат вашего обратного вызова таков:

int (*comp)(const void *, const void *)

Так что единственная проблема - это тип возвращаемого значениявнешняя функция, которую можно упростить до чего-то вроде этого:

void* vector_lsearch (const void* key, const void* base, int (*comp)(const void*, const void*))
{
    const struct vector* vector = CONST_VECTOR(base);
    void* result = NULL;
    unsigned char* data = (unsigned char*)base;

    for (size_t i=0; i < vector->size; i++)
    {
        if (comp(&data[i*vector->szof], key) == 0)
        {
            result = data;
            break;
        }
    }
    return result;
}

CONST_VECTOR хотя и подозрительно, пахнет, будто вы прячете гипс за макросом или чем-то еще?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...