Избегайте предупреждения о несовместимости указателя при работе с двойной косвенной адресацией

Предполагая, что эта программа:

#include <stdio.h>
#include <string.h>

static void ring_pool_alloc(void **p, size_t n) {
    static unsigned char pool[256], i = 0;
    *p = &pool[i];
    i += n;
}

int main(void) {
    char *str;
    ring_pool_alloc(&str, 7);
    strcpy(str, "foobar");
    printf("%s\n", str);
    return 0;   
}

... можно ли как-то избежать предупреждения GCC

test.c:12: warning: passing argument 1 of ‘ring_pool_alloc’ from incompatible pointer type
test.c:4: note: expected ‘void **’ but argument is of type ‘char **’

... без преобразования в (void **) (или просто отключив проверки совместимости)? Потому что я бы очень хотел сохранить предупреждения о совместимости на уровне косвенного обращения ...


person fnawothnig    schedule 31.05.2010    source источник


Ответы (3)


Почему бы вам не изменить сигнатуру метода так, чтобы он возвращал новый указатель, а не передавал его по указателю? Фактически, как и обычный malloc:

static void * ring_pool_alloc(size_t n) {
    static unsigned char pool[256], i = 0;
    void *p = &pool[i];
    i += n;
    return p;
}

int main(void) {
    char *str = ring_pool_alloc(7);
    strcpy(str, "foobar");
    printf("%s\n", str);
    return 0;   
}
person Konrad Rudolph    schedule 31.05.2010
comment
Потому что мне нужно это возвращаемое значение для чего-то еще. Блин. - person fnawothnig; 31.05.2010
comment
@fnawothnig: это красивое void возвращаемое значение? Блин. Если хотите правильных ответов, задавайте точные вопросы. И не раздражайтесь, когда отсутствие информации приводит к неприменимым ответам. - person Konrad Rudolph; 31.05.2010

Измените ring_pool_alloc, чтобы получить void *. Затем вы можете преобразовать его в void ** в функции, если хотите.

Или в вашем конкретном случае:

/* You can pass any pointer as a first parameter */
static void ring_pool_alloc(void *r, size_t n) {
    unsigned char **p = r; /* no cast necessary */
    static unsigned char pool[256], i = 0;
    *p = &pool[i];
    i += n;
}

Обратите внимание, что void ** не может действовать как универсальный тип указателя на указатель. С другой стороны, преобразования из и в void * с другими типами указателей применяются автоматически.

person Artefacto    schedule 31.05.2010
comment
Итак, преобразование (char **) в (void **) как-то нежелательно, но преобразование (char *) в (char **) нормально? Все для того, чтобы обойти вполне приемлемое предупреждение компилятора? - person sizzzzlerz; 31.05.2010
comment
@sizzzzlerz: Разница в том, что приведение выполняется внутри метода, и да, это вполне приемлемо. Но, честно говоря, я бы опасался принимать одиночный указатель, если на самом деле передается двойной указатель. - person Konrad Rudolph; 31.05.2010
comment
@sizzzzlerz Сразу отвечу на вопрос: p А где же преобразование из (char *) в (char **)? В любом случае, дело в том, что компилятор не генерирует предупреждения, если вы передаете какой-либо указатель на функцию, которая принимает void *. - person Artefacto; 31.05.2010
comment
@sizzzzlerz И, как сказал Конрад Рудольф, у него есть то преимущество, что вам не нужно приводить аргумент каждый раз, когда вы вызываете функцию., - person Artefacto; 31.05.2010
comment
Обратите внимание, что void ** не может действовать как универсальный тип указателя на указатель. С другой стороны, преобразования из и в void * с другими типами указателей применяются автоматически. - Я так понимаю, это просто невозможно? Полагаю, меня бы устроила справка из C ++ ... но там у меня тоже были бы шаблоны ... вздох. - person fnawothnig; 31.05.2010
comment
Да и ... Я лучше буду использовать везде, где я вызываю, чем лгать в прототипе функции ... - person fnawothnig; 31.05.2010
comment
@fnawothnig Вы не врете в прототипе функции. char ** - это указатель, и объявление функции для получения void * означает, что вы принимаете указатель - любой указатель, независимо от уровня косвенности. Просто невозможно выразить, что вам нужен указатель на указатель любого типа, не являющегося указателем. - person Artefacto; 31.05.2010

Просто измените:

static void ring_pool_alloc(void **p, size_t n) {

to:

static void ring_pool_alloc(char **p, size_t n) {
person Community    schedule 31.05.2010
comment
Это было бы лучше, но я думаю, что он хочет передать что-то другое, кроме char **. - person Artefacto; 31.05.2010
comment
@Artefacto: Это невозможно, потому что невозможно изменить переменную, не зная ее фактического типа. Вот почему возврат void * в порядке - потому что исходная переменная изменяется в месте вызова функции, где известен ее тип. - person caf; 01.06.2010