C语言 int类型可以存储字符吗?

wnavrhmk  于 2023-03-29  发布在  其他
关注(0)|答案(4)|浏览(402)

在C语言中,任何整型变量都可以用来表示字符。这意味着int可以存储字符。但是,当我在程序中声明一些条件语句时,程序没有显示预期的结果。下面是我的程序:

#include <stdio.h>
#include <conio.h>

int main(void)

{
    auto signed int a;
    clrscr();

    printf("enter character");
    scanf("%c", &a);

    switch(a)
    {
        case 'K':
        printf("upper case");
        break;

        case 'k':
        printf("lower case ");
        break;

        default:
        printf("no match found");
        break;
    }

    return 0;
}

这是我的程序。当我输入大写字母“K”时。但是输出是“没有找到匹配”,这是什么问题呢?

azpvetkf

azpvetkf1#

简短的回答是“是”。int可以存储char的值。例如:

signed int i;
   signed char c = 'k';
   i = c;
   if(i == 'k') printf("Yes\n");

将打印“是”。
您的代码由于其他原因无法工作。在这一行中:

scanf("%c", &a);

您提供了一个指向未初始化的signed int的指针,该指针至少有2个字节宽,可能有4个字节宽,但%c化选项需要一个指向char的指针,该指针为1个字节宽。现代编译器应该警告过这种情况。
因此,只有a的第一个字节被输入的字符填充,所以当你输入'K'时,a的第一个字节将等于'K',但其余的字节将有一些未定义的值。因此,a可能不等于'K'(如观察到的)。
解决方法是将a初始化为0:

auto signed int a = 0;

但是,为scanf()中的字符格式说明符提供signed int指针是未定义的行为,因此不建议这样做。

1l5u6lss

1l5u6lss2#

是的,任何C的整数类型都可以存储字符值。但是你不能使用scanf("%c")将字符值读入任何整数类型,因为scanf("%c")只能将字符值 * 读入char类型的变量。
但你可以用

scanf("%c", &a);

a = getchar();

(其中ashortintlong或其他),它会工作得很好。
(Here,不过,使用char实际上会更差,因为虽然它看起来很有效,但它会搞砸getchar向您发出EOF信号的能力。)

li9yvcax

li9yvcax3#

  • 我在一台64位x86-64计算机上使用Linux Ubuntu 20.04和gcc --version 9.4.0来编写和测试这个答案。*

如何摆脱过时的Turbo C,安装免费的现代操作系统和编译器

我建议你去找一台旧的二手电脑,免费安装一个现代的操作系统,比如Ubuntu 22.04 LTS(转到https://ubuntu.com/,点击顶部的“下载”;完整的安装说明在这里:https://ubuntu.com/tutorials/install-ubuntu-desktop#1-overview)。这个操作系统被世界上许多现代科技公司使用,并且可以给予你轻松访问gcc,一个现代编译器。
Ubuntu是一个功能强大的平台,现在用户非常友好,有很好的GUI界面,就像其他现代操作系统一样。随着你的成长,我鼓励你鼓励你的学校和老师完全放弃Turbo C,把所有的教学迁移到Ubuntu上,这也是一个 * 免费 * 和开源的现代操作系统,能够在现实生活中的专业环境中使用。现代代码。例如,在我过去工作过的两家科技公司,它一直是我工作电脑上的主要操作系统。我在家里的所有电脑上也使用它,包括小孩的电脑。它运行Chrome浏览器很好,配有LibreOffice文本编辑器、Shotcut视频编辑软件,内置编译器和开发工具,如gcc(用于C)和g++(用于C++),Python,Bash,Java等。

构建错误,修复scanf()中未定义的行为

我尝试使用以下命令在gcc中编译你的程序:

mkdir -p bin
gcc -Wall -Wextra -Werror -O3 -std=gnu17 test.c -o bin/a && bin/a

我得到了这个错误:

test.c:53:10: fatal error: conio.h: No such file or directory
   53 | #include <conio.h>
      |          ^~~~~~~~~
compilation terminated.

我以前从来没有听说过conio.h,所以我在谷歌上搜索了一下,找到了this answer
conio.h标头是Turbo C特有的,它比最早的C标准早了几年。它包含DOS命令行特有的例程。这里经常使用的一个函数是getch,它允许一次阅读一个字符,而不必按Enter键。它还包含gotoxy,它允许将光标放置在终端的特定位置
一般来说,像这样与终端通信的方法是非常特定于操作系统的,因此每种方法都有自己的(通常是不可移植的)方式。
这与stdio. h中的函数形成对比,stdio. h中包含printfscanfgetchar等函数,这些函数无论使用什么类型的控制台都可以工作。
所以,听起来问题下面的评论说你正在使用过时的工具是正确的。你的学校没有使用好的、现代的工具,需要改变。几乎没有人再使用DOS了。很多人和高科技公司使用Linux。Windows和Mac更受欢迎。我建议你改用Linux。只在学校使用Turbo C,但是要意识到你可能被教导了各种过时和不好的做法,所以也要在网上研究一些东西,并在你去的时候试着学习正确的方法。
您可以在https://www.onlinegdb.com/上免费在线在64位Linux机器上编程,但这与自己安装Ubuntu不同。
好了,我清理了一下你的代码,去掉了不寻常的auto signed用法,用一个在Linux上工作的系统调用替换了你的clear screen命令,现在有了这个:

test.c:

#include <stdio.h>
#include <stdlib.h>  // for `system()` call

int main(void)
{
    int a;
    // call the command-line command `clear` to clear the screen
    int return_code = system("clear");
    if (return_code != 0)
    {
        printf("Failed to clear screen.\n");
    }

    printf("enter character");
    scanf("%c", &a);

    switch(a)
    {
        case 'K':
        printf("upper case");
        break;

        case 'k':
        printf("lower case ");
        break;

        default:
        printf("no match found");
        break;
    }

    return 0;
}

使用我的build命令可以产生更好更安全的输出,因为我使用的是-Wall -Wextra -Werror,它不会生成。我得到以下错误。下面是build命令和错误:

$ gcc -Wall -Wextra -Werror -O3 -std=gnu17 test.c -o bin/a && bin/a
test.c: In function ‘main’:
test.c:67:13: error: format ‘%c’ expects argument of type ‘char *’, but argument 2 has type ‘int *’ [-Werror=format=]
   67 |     scanf("%c", &a);
      |            ~^   ~~
      |             |   |
      |             |   int *
      |             char *
      |            %lc
test.c:67:5: error: ignoring return value of ‘scanf’, declared with attribute warn_unused_result [-Werror=unused-result]
   67 |     scanf("%c", &a);
      |     ^~~~~~~~~~~~~~~
cc1: all warnings being treated as errors

因此,要修复它,请将int a;替换为char a;,并使用scanf()的返回值来检查错误。

#include <stdio.h>
#include <stdlib.h>  // for `system()` call

int main(void)
{
    char a;
    // call the command-line command `clear` to clear the screen
    int return_code = system("clear");
    if (return_code != 0)
    {
        printf("Failed to clear screen.\n");
    }

    printf("Enter character: ");
    int num_items_filled = scanf("%c", &a);
    const int NUM_ITEMS_FILLED_EXPECTED = 1;
    if (num_items_filled != NUM_ITEMS_FILLED_EXPECTED)
    {
        printf("Error: we expected %i item(s) to be filled, but got %i.\n",
            NUM_ITEMS_FILLED_EXPECTED,
            num_items_filled);
    }

    switch(a)
    {
        case 'K':
        printf("Upper case.\n");
        break;

        case 'k':
        printf("Lower case.\n");
        break;

        default:
        printf("No match found.\n");
        break;
    }

    return 0;
}

输出示例:

Enter character: k
Lower case.

根据C标准,使用int a;而不是char a;undefined behavior,这意味着它是一个bug。

更进一步:endianness检测,打印int中的所有字节并找到k

我对@neilsen的答案投了赞成票。它是正确的。让我们更深入地了解一下,当你使用int a;而不是char a;时,会发生什么。注意:这是 undefined behavior,意思是C标准没有定义它,所以这是一个bug,我将要做的是编译器和计算机体系结构特定的。为了强制它编译,我将从构建命令中删除-Werror
以下是我的新build命令:

gcc -Wall -Wextra -O3 -std=gnu17 test.c -o bin/a -lm && bin/a

这是我的新程序。我删除了clear调用,所以我们仍然可以看到编译器输出警告我们错误使用scanf()

#include <stdio.h>
#include <stdlib.h>  // for `system()` call

int main(void)
{
    int a;

    printf("Enter character: ");
    int num_items_filled = scanf("%c", &a);
    const int NUM_ITEMS_FILLED_EXPECTED = 1;
    if (num_items_filled != NUM_ITEMS_FILLED_EXPECTED)
    {
        printf("Error: we expected %i item(s) to be filled, but got %i.\n",
            NUM_ITEMS_FILLED_EXPECTED,
            num_items_filled);
    }

    switch(a)
    {
        case 'K':
        printf("Upper case.\n");
        break;

        case 'k':
        printf("Lower case.\n");
        break;

        default:
        printf("No match found.\n");
        break;
    }

    return 0;
}

现在,这里是一个示例运行输出。注意我现在得到“No match found”:

$ gcc -Wall -Wextra -O3 -std=gnu17 test.c -o bin/a -lm && bin/a
test.c: In function ‘main’:
test.c:60:36: warning: format ‘%c’ expects argument of type ‘char *’, but argument 2 has type ‘int *’ [-Wformat=]
   60 |     int num_items_filled = scanf("%c", &a);
      |                                   ~^   ~~
      |                                    |   |
      |                                    |   int *
      |                                    char *
      |                                   %lc
Enter character: k
No match found.

让我们看看int a里面有多少字节。新程序:我在switch语句后添加了print_bytes_in_variable()函数和一些东西,包括endianness检测:

#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h>
#include <stdlib.h>  // for `system()` call

void print_bytes_in_variable(uint8_t* byte_array, size_t num_bytes)
{
    printf("Bytes are: ");
    for (size_t i = 0; i < num_bytes; i++)
    {
        printf("0x%02x ", byte_array[i]);
    }
    printf("\n");
}

int main(void)
{
    int a;

    printf("Enter character: ");
    int num_items_filled = scanf("%c", &a);
    const int NUM_ITEMS_FILLED_EXPECTED = 1;
    if (num_items_filled != NUM_ITEMS_FILLED_EXPECTED)
    {
        printf("Error: we expected %i item(s) to be filled, but got %i.\n",
            NUM_ITEMS_FILLED_EXPECTED,
            num_items_filled);
    }

    switch(a)
    {
        case 'K':
        printf("Upper case.\n");
        break;

        case 'k':
        printf("Lower case.\n");
        break;

        default:
        printf("No match found.\n");
        break;
    }

    // prove we know how to read bytes by reading them one-at-a-time from an int
    int b = 0x04030201;
    uint8_t* byte_array = (uint8_t*)&b;
    printf("\n");
    print_bytes_in_variable(byte_array, sizeof(b));
    // indicate endianness;
    // read about it here: https://en.wikipedia.org/wiki/Endianness
    if (byte_array[0] == 0x01) // this check works on any architecture
    {
        printf("My system is **little-endian**, since the least-significant\n"
               "(smallest-value) byte is first.\n");
    }
    // this check works only on architectures with 4-byte or larger
    // `int`s since I wrote the `0x04` into the 4th byte in `int b`.
    else if (byte_array[0] == 0x04)
    {
        printf("My system is **big-endian**, since the most-significant\n"
               "(biggest-value) byte is first.\n");
    }

    // Now check the values of each byte in the `int a`
    printf("\nInside `a`:\n");
    print_bytes_in_variable((uint8_t*)&a, sizeof(a));

    return 0;
}

样品运行和输出:

$ gcc -Wall -Wextra -O3 -std=gnu17 test.c -o bin/a -lm && bin/a
test.c: In function ‘main’:
test.c:71:36: warning: format ‘%c’ expects argument of type ‘char *’, but argument 2 has type ‘int *’ [-Wformat=]
   71 |     int num_items_filled = scanf("%c", &a);
      |                                   ~^   ~~
      |                                    |   |
      |                                    |   int *
      |                                    char *
      |                                   %lc
Enter character: k
No match found.

Bytes are: 0x01 0x02 0x03 0x04 
My system is **little-endian**, since the least-significant
(smallest-value) byte is first.

Inside `a`:
Bytes are: 0x6b 0x43 0xc8 0x61

还有k!ASCII表显示k是十六进制6B,您可以在上面打印的第一个(在我的情况下是最低有效位)字节中看到这一点!然而,在此之后的其他值是垃圾(未定义)值,它们在运行此程序之前就位于我的内存中。
要简单地将它们全部写入0来解决这个问题,请将int a;更改为int a = 0;,现在您的程序可以工作了!再次运行它,您将看到:

Enter character: k
Lower case.

所以,* 它工作 *,是的,但是编译器警告仍然存在,并且程序仍然依赖于scanf()中的 * 未定义行为 *,所以即使 * 它按预期工作,它仍然有bug和 * 损坏 奇怪,是吧?嗯,就是这样。我们依赖于 * 可预测 *(对于我的编译器和计算机硬件)但是 undefined 行为(根据C标准),让我的程序“工作”。虽然它被破坏了。修复编译器警告。添加-Werror回来把警告变成错误,并使用char a;而不是int a;来修复scanf()中的未定义行为。
结束。

参考文献

  1. https://en.wikipedia.org/wiki/ASCII#Printable_characters
  2. https://en.wikipedia.org/wiki/Endianness
  3. https://cplusplus.com/reference/cstdio/scanf/
  4. https://cplusplus.com/reference/cstdlib/system/
  5. https://en.cppreference.com/w/c/program/system
  6. Why use conio.h?
  7. https://ubuntu.com/tutorials/install-ubuntu-desktop#1-overview
  8. https://en.wikipedia.org/wiki/Borland_Turbo_C
kpbwa7wx

kpbwa7wx4#

根据你的代码,你已经将变量a声明为int,所以当你在输入时插入任何字符时,它将分配该字符的ASCII值,所以你需要将变量的数据类型从int更改为char。

相关问题