通过memcpy复制“指向const的指针”到“指向非const的指针”

问题描述 投票:0回答:3
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;
}
c const
3个回答
1
投票

初始化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虽然是可疑的,闻起来像是您将演员表隐藏在宏或其他东西后面?


2
投票

该警告来自在初始化过程中删除const限定符;仅添加显式强制转换也可以避免警告。

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

标准的6.5.4节描述了对指针隐式转换的约束:

除6.5.16.1的约束所允许的情况外,涉及指针的转换应为通过显式强制转换指定。

而且显式强制转换指针的唯一限制是:

指针类型不得转换为任何浮点类型。浮点型不得转换为任何指针类型。

第一个规则的相关部分,即6.5.16.1,具有以下用于简单分配的规则:

左操作数具有原子,限定或不限定的指针类型,并且(考虑该类型左值转换后,左操作数将具有)两个操作数都是限定的指针或兼容类型的非限定版本,并且左侧指向的类型具有所有右边指出的类型的限定词;

最后,关于限定符的第6.7.3节有:

如果试图通过使用一个修饰符来修改以const限定类型定义的对象,具有非const限定类型的左值,行为未定义。

如果具有非const限定类型的,可以访问由const限定类型定义的对象的左值本身是未定义的,则该句子没有太大价值。这表明您可以将const void *显式地转换为void *并避免警告而不会引入未定义的行为,因为警告专门涉及通过简单赋值而不是对const限定符的普遍反对而对隐式强制转换的无效使用。


2
投票

复制指针是安全的。使用b时可能会导致安全问题。由于已将其声明为指向非恒定数据的指针,因此您可以通过该指针进行赋值,例如*(int *b) = 1;如果something是常量数据,则将导致未定义的行为。

[如果您使用void *指针作为管道,并且最终会将指针传递给将指针转换回其原始类型的函数(例如qsort()使用其指针参数的方式),则应能够忽略此警告。您希望该函数将其强制转换回指向const的指针,而不尝试通过它进行分配。

我不认为有一种方法可以声明可用于const或非const数据的通用指针的通用指针。如果将其声明为非const,则在为其指定const指针时会收到警告。如果将其声明为const,则无法将其用于需要非const指针的函数。

© www.soinside.com 2019 - 2024. All rights reserved.