C предупреждение Отсутствует страж в вызове функции

Это мое предупреждение.

Missing sentinel in function call

Как я могу удалить это.

Я использую Linux и компилятор GCC.

8 ответов

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

Я только что столкнулся с той же проблемой. Код, который вызывал это для меня, был...

execl("/bin/bash", "/bin/bash", fname, '\0');

но это должно быть...

execl("/bin/bash", "/bin/bash", fname, (char *)0);

Проблема с первой версией заключается в том, что список параметров должен заканчиваться нулевым указателем. Но '\0' - это не нулевой указатель, это нулевой символ. Таким образом, значение (0) является правильным, это просто неправильный тип.

(Char *)0 также равен нулю, но приводится как указатель на символ, который является нулевым указателем (т. Е. Он указывает на адрес 0). Это необходимо, чтобы система могла определить, где заканчивается список параметров, чтобы она не продолжала сканировать параметры после последнего. Это может привести к недействительным указателям, которые могут указывать на любую память, что, вероятно, приведет к ошибке сегментации.

Это (char *)0 называется часовым, и это то, чего не хватало в первом примере.

Наконец, обратите внимание, что NULL определяется как (void *)0, поэтому

execl("/bin/bash", "/bin/bash", fname, NULL);

Работает так же хорошо, и немного удобнее. (Спасибо @mah за это).

В XCode, если вы кодируете в target-c и используете некоторые методы, которые принимают список переменных параметров, вам нужно добавить nil- объект в конце списка.

Например:

N SArray * names = [NSArray arrayWithObjects: @ "Name1", @ "Name2"]; // приведет к предупреждению, указанному выше

Однако NSArray * names = [NSArray arrayWithObjects: @ "Name1", @ "Name2", nil]; //Правильный

Надеюсь, это поможет!

использование (void*)0 или же (void *)NULL вместо NULL как страж.

Предупреждение генерируется sentinel атрибут функции, как упомянуто Джичао. Документировано по адресу: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Function-Attributes.html котором говорится:

Допустимый NULL в этом контексте определяется как ноль с любым типом указателя. Если ваша система определяет макрос NULL с целочисленным типом, вам нужно добавить явное приведение. GCC заменяет stddef.h на копию, которая переопределяет NULL соответствующим образом.

В ANSI C NULL может быть 0 или же (void *)0, и только указатель удовлетворяет этому атрибуту, см. также: В чем разница между NULL, '\0' и 0

В то время как 0 будет приведен к нулевому указателю с обычным аргументом указателя, невозможно дифференцировать нулевое целое от нулевого указателя с помощью vararg, поэтому различие имеет решающее значение. Смотрите: продвижение аргумента по умолчанию для указателя

Правда, GCC гарантирует, что NULL будет указателем, а поскольку NULL предоставляется GCC в stddef.h (некоторые заголовки ANSI C находятся в GCC, другие в glibc), я не уверен, что это может быть сломано, так как sentinel это расширение GCC для начала.

Но я бы еще написал (void *)NULL или же (void*)0 просто чтобы убедиться.

Кроме того, как POSIX, так и man execl требует "нулевого указателя" для завершения функции execl(), что является прототипом примера использования часового.

NULL там не гарантируется работа, потому что это "константа нулевого указателя", которая не обязательно является "нулевым указателем", поэтому с помощью (void *)0 везде вы более соответствует хорошо известным API. Смотрите также: Является ли ((void*)0) константой нулевого указателя? || Является ли (int *)0 нулевым указателем?

GCC 5.2.0 обеспечивает execl как встроенный и наборы sentinel программно на нем:

DEF_EXT_LIB_BUILTIN (BUILT_IN_EXECL, "execl", BT_FN_INT_CONST_STRING_CONST_STRING_VAR, ATTR_SENTINEL_NOTHROW_LIST)

Существует также версия Glibc 2.21 execl который не имеет атрибута.

Тривиальный пример:

#include <stdio.h>
#include <stdarg.h>

void print_strings(const char* first, ...) __attribute__((sentinel));

void print_strings(const char* first, ...)
{
    va_list ap;
    const char* tmp;
    if (!first)
        return ;
    printf("%s\n", first);
    va_start(ap, first);
    while (1) {
        tmp = va_arg(ap, const char*);
        if (tmp == 0)
            break;
        printf("%s\n", tmp);
    };
    va_end(ap);
}

int main()
{
    print_strings("how are you?", "i'm fine", "and you?", NULL);
    return 0;
}

В основном, если вы вызываете print_strings как print_strings("how are you?", "I'm fine", "and you?") который не имеет конца NULL, GCC будет жаловаться на "пропавшего стража".

Потому что мы добавляем атрибут функции sentinel в функцию print_strings, Это расширение gcc, указывающее, что аргументы переменной должны заканчиваться на NULL. Поэтому, если вы не заканчиваете переменные аргументы значением NULL, компилятор может обнаружить его и показать предупреждение.

Вы можете передать NULL как: execl("/bin/bash", "ls","-l", NULL); Последний параметр всегда должен быть 0. Это терминатор NULL. Поскольку список аргументов является переменным, у нас должен быть способ сообщить C, когда он должен закончиться.

Sentinel означает охранять или защищать. Таким образом, в этом контексте возникает ошибка, потому что вы можете пропустить параметры защиты. Если вы используете массив или словарь, убедитесь, что после именования объектов вы заканчиваете их ключевым словом nil.

Пример:

[NSDictionary dictionaryWithObjectsAndKeys:@"UIAlertView", kSectionTitleKey,
              @"Show Custom", kLabelKey,
              @"AlertsViewController.m - alertOtherAction", kSourceKey];

Приведенный выше оператор выдаст ошибку "Отсутствует страж в вызове функции"

Правильный синтаксис:

[NSDictionary dictionaryWithObjectsAndKeys:@"UIAlertView", kSectionTitleKey,
              @"Show Custom", kLabelKey,
              @"AlertsViewController.m - alertOtherAction",kSourceKey,nil];

Вы должны пройти NULL как последний аргумент функции.

Наконец-то я нашел способ избавиться от этого странного и раздражающего предупреждения.

Все, что вам нужно сделать, это привести нулевой указатель к соответствующему типу указателя.

В случае UIAlertView это выглядит так:

UIAlertView* alert = [ [ UIAlertView alloc ] initWithTitle: @"Title" message: @"Message" delegate: nil cancelButtonTitle: @"Cancel" otherButtonTitles: @"OK", ( NSString* )nil ];

Обратите внимание на ( NSString* )nil бросать.

Попробуйте и дайте мне знать, если это работает для вас.

Другие вопросы по тегам