Почему использование crypt в glibc вызывает предупреждение компилятора? - PullRequest
0 голосов
/ 06 января 2019

Я попытался скомпилировать следующий код (минимальный пример, см. Редактирование всего кода):

// a.c
#include <stdio.h>

#define _XOPEN_SOURCE
#include <unistd.h>

int main(int argc, char* argv[])
{
    puts((const char*) crypt("AAAA", "$6$2222"));
    return 0;
}

Используя clang-7 -lcrypt a.c и он выдал следующее предупреждение:

minimum.c:8:24: warning: implicit declaration of function 'crypt' is invalid in C99 [-Wimplicit-function-declaration]
    puts((const char*) crypt("AAAA", "$6$2222"));
                       ^
minimum.c:8:10: warning: cast to 'const char *' from smaller integer type 'int' [-Wint-to-pointer-cast]
    puts((const char*) crypt("AAAA", "$6$2222"));
         ^
2 warnings generated.

Но ./a.out, похоже, работает:

$6$2222$6GKY4KPtBqD9jAhwxIZGDqEShaBaw.pkyJxjvSlKmtygDXKQ2Q62CPY98MPIZbz2h6iMCgLTVEYplzp.naYLz1

Я обнаружил, что если я удаляю #include <stdio.h> и puts вот так:

// new_a.c
#define _XOPEN_SOURCE
#include <unistd.h>

int main(int argc, char* argv[])
{
    crypt("AAAA", "$6$2222");
    return 0;
}

Тогда предупреждений нет.

Как исправить эти предупреждения, не удаляя #include <stdio.h>?

Edit:

Вся программа:

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

#define _X_OPEN_SOURCE
#include <unistd.h>

#include <assert.h>

void* Calloc(size_t cnt, size_t size)
{
    void *ret = calloc(cnt, size);
    assert(ret);
    return ret;
}

size_t GetSaltLen(const char *salt)
{
    size_t salt_len = strlen(salt);
    assert(salt_len >  0);
    assert(salt_len <= 16);
    return salt_len;
}

char* GetSaltAndVersion(const char version, const char *salt)
{
    size_t saltlen = GetSaltLen(salt);
    /*
     * The format of salt:
     *     $one_digit_number$up_to_16_character\0
     * For more info, check man crypt.
     */
    char *ret = (char*) Calloc(1 + 1 + 1 + saltlen + 1, sizeof(char));

    char *beg = ret;

    *beg++ = '$';
    *beg++ = version;
    *beg++ = '$';
    memcpy((void*) beg, (const void*) salt, saltlen + 1);

    return ret;
}

void crypt_and_print(const char *passwd, const char *salt_and_version)
{
    char *result = crypt(passwd, salt_and_version);
    assert(puts(result) != EOF);
}

int main(int argc, char* argv[])
{
    if (argc != 4) {
        fprintf(stderr, "argc = %d\n", argc);
        return 1;
    }

    char *salt_and_version = GetSaltAndVersion(argv[2][0], argv[3]);
    crypt_and_print(argv[1], salt_and_version);
    free(salt_and_version);

    return 0;
}

Я попытался, как предложил @Андрей Ахметов, и поставил #define на первую строку, но предупреждения не исчезли.

1 Ответ

0 голосов
/ 06 января 2019

Макрос _XOPEN_SOURCE задокументирован в feature_test_macros(7). В частности, на странице руководства написано:

ПРИМЕЧАНИЕ. Чтобы быть эффективным, перед включением любых заголовочных файлов должен быть определен макрос тестирования функций . Это может быть сделано либо в команде компиляции (cc -DMACRO=value), либо путем определения макроса в исходном коде перед включением любых заголовков.

Когда вы включаете stdio.h, вы косвенно включаете features.h, который использует макросы функциональных тестов, определенные в этой точке . В частности, поскольку _XOPEN_SOURCE и друзья не определены в этот момент, crypt.h не объявляет crypt.

К тому времени, как вы определите _XOPEN_SOURCE, уже слишком поздно, поскольку features.h имеет охрану включения, предотвращающую его включение дважды.

Если поменять местами порядок первых двух строк, код работает без предупреждения в моей системе:

#define _XOPEN_SOURCE
#include <stdio.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
    puts((const char*) crypt("AAAA", "$6$2222"));
    return 0;
}

Ваш более крупный пример не работает по второй причине: вы написали _X_OPEN_SOURCE в качестве имени макроса, а правильное имя - _XOPEN_SOURCE.

...