缩小从 'long' 到有符号类型 'char' 的转换是实现定义的(C 中的 strtol 函数)

Narrowing conversion from 'long' to signed type 'char' is implementation-defined (strtol function in C)

我正在尝试使用 strtol 将输入的字符转换为整数。这是源代码的一部分:

char option;
char *endptr;

printf("=========================================Login or Create Account=========================================\n\n");
while(1) {
    printf("Welcome to the Bank management program! Would you like to 1. Create Account or 2. Login?\n>>> ");
    fgets(&option, 1, stdin);
    cleanStdinBuffer();
    option = strtol(&option, &endptr, 10);

在 strtol 函数中,我收到一条警告:

Clang-Tidy: Narrowing conversion from 'long' to signed type 'char' is implementation-defined

谁能告诉我哪里做错了?

char 只能包含 long 值的很小一部分。 strtol returns long 并将其分配给 char.

这次调用fgets

fgets(&option, 1, stdin);

总是将字符 option 设置为终止零字符 '[=16=]',前提是用户没有中断输入。

这是一个演示程序。

#include <stdio.h>

int main(void) 
{
    char c = 'A';
    
    printf( "Before calling fgets c = %d\n", c );
    
    fgets( &c, 1, stdin );
    
    printf( "After  calling fgets c = %d\n", c );

    return 0;
}

程序输出为

Before calling fgets c = 65
After  calling fgets c = 0

与用户输入的内容无关。这里的值65是调用fgets.

之前保存在变量c中的字符'A'的ASCII码

如果你想输入一个字符,那么你应该使用

scanf( " %c", &input );

注意转换说明符前的空格。

在这个调用之后你可以检查用户是否输入了像

这样的数字
#include <ctypes.h>

//...

if ( isdigit( ( unsigned char )input ) ) input = input - '0';

并将变量输入设为[0, 9].

范围内对应的整数值

Clang-Tidy 警告您在此处进行的隐式转换,您将 strtollong return 值分配给 char

option = strtol(&option, &endptr, 10);

如果这是故意的,并且您确定该值将在 [-128,127] 范围内,这不一定是一个问题(这只是一个警告),但即便如此我还是建议显式地转换 return 类型的 strtol,使用 int8_t 而不是 char 并且不重用 option 变量作为 return 值。换句话说:

int8_t value = (int8_t)strtol(&option, &endptr, 10);

如果不是故意的,我建议您简单地使用 long 作为您分配给 strtol 的 return 值的变量的类型,因此:

long value = strtol(&option, &endptr, 10);

Clang-tidy 没有警告您的是 strtol 的第一个参数应该是指向包含以 0 结尾的字符串的字符缓冲区的指针,而不是指向单个字符的指针。这也是 fgets 的问题。有两种方法可以解决这个问题:

  1. 使选项成为至少包含两个字符的 char 数组,

  2. 改用 fgetc 并将您的代码修改为如下内容:

    int option = fgetc(stdin);
    if (option == '1') {
        /*Create Account */
    } else if (option == '2') {
        /* Login */
    }
    else {
        /* Error */
    }
    

我觉得后者看起来干净多了