1

In C, a variable of any integral type can be used to represent characters. This means an int can store a character. But, when I'm declaring some conditional statements in my program, the program is not showing expected results. Here is my program:

#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;
}

This is my program. as I entered character 'K' which is a upper case. But the output is "no match found" what is the problem.

Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
  • 1
    What compiler are you using? A decent compiler should be yelling at you that the second argument to `scanf` needs to be `char *` not `int *`.If you don't have all warnings turned on then do that first. – Damien_The_Unbeliever Mar 22 '23 at 07:07
  • [How to debug small programs](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) – Damien_The_Unbeliever Mar 22 '23 at 07:10
  • Build with gcc using `-Wall -Wextra -Werror` and see what errors you get. – Gabriel Staples Mar 22 '23 at 07:10
  • I'm using turbo c. But in c any integral types can be used to store character. You can try the above code by not using any conditional statements. Try to enter a character and store that character in int type variable. And print that inputed charcter on console. This time your compiler will not complain. Why? – Koustubh Sharma Mar 22 '23 at 07:16
  • 3
    @KoustubhSharma hint: using tools like Turbo C that are older than you is a bad idea. But I suppose your school mandates the usage of Turbo C and changing school is probably not an option.... – Jabberwocky Mar 22 '23 at 07:20
  • 4
    @Jabberwocky More like changing country, I suspect. The school system in one particular large country in central Asia is such a disgrace. They seem to have decided on a national level that MS DOS is the future of programming and therefore use crap like Turbo C, Dev C++ and the book _Let Us C - Yashavant Kanetkar_. That book is likely by far the worst C book ever written... the author wouldn't be able to pass a beginner C class. – Lundin Mar 22 '23 at 07:37
  • Kroustubh, I [added an answer](https://stackoverflow.com/a/75814709/4561887) which digs really deep and hopefully gives you a lot more insight. I also try to help you use modern and better tools and compilers. – Gabriel Staples Mar 22 '23 at 16:42
  • Thanks for your valuable answers. I will keep trying to learn c programming and one day I will become a pro like you. – Koustubh Sharma Mar 23 '23 at 09:26

4 Answers4

3

The short answer is "yes". An int can store the value of a char. E.g.:

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

will print "Yes".

Your code is not working for another reason. In this line:

    scanf("%c", &a);

You are providing a pointer to an uninitialized signed int which is at least 2 and probably 4 bytes wide, but the %c formatting option expects an pointer to a char which is 1 byte wide. A modern compiler should have warned against this.

Hence, only the first byte of a is filled with the typed character, so when you type 'K', the first byte of a will equal 'K', but the remaining bytes will have some undefined value. Thus, a will probably not compare equal to 'K' (as observed).

A workaround could be to initialize a to 0:

auto signed int a = 0;

However, providing a signed int pointer for a character format specifier in scanf() is undefined behaviour, so this cannot be recommended.

nielsen
  • 5,641
  • 10
  • 27
  • Upvoted. I [added an answer](https://stackoverflow.com/a/75814709/4561887) which takes it one step deeper and actually finds out which byte in the integer contains the `k` read in by `scanf()`. – Gabriel Staples Mar 22 '23 at 16:43
2

I used Linux Ubuntu 20.04 and gcc --version 9.4.0 on a 64-bit x86-64 computer, to write and test this answer.

How to get away from outdated Turbo C and install a free, modern OS and compiler

I recommend you go get any old used computer you can find, and install a modern operating system such as Ubuntu 22.04 LTS for free (go to https://ubuntu.com/ and click "Download" at the top; full installation instructions are here: https://ubuntu.com/tutorials/install-ubuntu-desktop#1-overview). This operating system is used by many modern tech companies world-wide, and will give you easy access to gcc, a modern compiler.

Ubuntu is a powerful platform and is quite user-friendly these days, with nice GUI interfaces, just like other modern OSs. As you grow, I encourage you to encourage your school and teachers to abandon Turbo C completely and migrate all instruction to Ubuntu which, again, is a free and open source and modern operating system capable of being used in a professional setting for real-life, modern code. It has been my primary OS on my work computers at both of my past tech companies I've worked for, for instance. I also use it at home on all of my computers, including for young kids. It runs the Chrome browser just fine, comes with LibreOffice text editors, Shotcut video editor software, built-in compilers and developer tools like gcc (for C) and g++ (for C++), Python, Bash, Java, etc. I program Arduinos on it, design 3D models in https://www.tinkercad.com on it, and print to my 3D printer from it.

Build errors, and fixing the undefined behavior in scanf()

I tried to compile your program in gcc using these commands:

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

...and I got this error:

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

I've never heard of conio.h before, so I googled it and found this answer:

The conio.h header is specific to Turbo C, which predates the earliest C standard by several years. It contains routines that are specific to the DOS command line. One function here that's frequently used is getch, which allows reading one character at a time without having to press the Enter key. It also contains gotoxy which allows placing the cursor at a specific location in the terminal

Generally speaking, methods of communicating with the terminal like this are very OS specific, so each has their own (typically non-portable) way of doing it.

This contrasts with the functions in stdio.h which contain functions like printf, scanf, and getchar which work regardless of what type of console is in use.

So, it does sound like the comment under the question which states you are using archaic and outdated tools is correct. Your school isn't using good, modern tools, and needs to change. Almost nobody uses DOS anymore. Tons of people and high-tech companies use Linux. Windows and Mac are even more popular. I recommend you switch to Linux. Use Turbo C just at school, but realize you are probably being taught a variety of outdated and bad practices, so research things online too and try to learn proper ways as you go, as well.

You can program on a 64-bit Linux machine for free online at https://www.onlinegdb.com/, but it's not the same as installing Ubuntu yourself.

Ok, so I cleaned up your code a bit, got rid of the unusual auto signed usage, replaced your clear screen command with a system call that works on Linux, and now have this:

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;
}

With my build commands which produce better and safer output because I'm using -Wall -Wextra -Werror, it won't build. I get the following errors. Here is the build command and errors:

$ 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

So, to fix it, replace int a; with char a; and use the return value from scanf() to check for errors. I now have this well-written, fully functional program:

#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;
}

Example output:

Enter character: k
Lower case.

To use int a; instead of char a; is undefined behavior according to the C standard, which means it is a bug.

Going further: endianness detection, print all bytes in the int and find the k

I upvoted @neilsen's answer. It is correct. Let's go a touch deeper and see what really happens when you use int a; instead of char a;. Note: this is undefined behavior, meaning the C standard does not define it, so it is a bug, and compiler and computer architecture-specific what I'm about to do. To force it to compile I will remove -Werror from my build commands.

Here is my new build command:

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

Here is my new program. I removed the clear call so we can still see the compiler output warning about our incorrect usage of 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;
}

Now, here is a sample run output. Notice I get "No match found" now:

$ 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.

Let's see what bytes are inside int a. New program: I added the print_bytes_in_variable() function and some stuff after the switch statement, including endianness detection:

#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;
}

Sample run and output:

$ 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 

And, there is your k! The ASCII tables indicate a k is hex 6B, and you can see that in the first (least-significant in my case) byte printed just above! The other values after that, however, are garbage (undefined) values that were sitting in my memory prior to running this program.

To simply write them all to 0s to fix this, change int a; to int a = 0;, and now your program works! Run it again and you'll see:

Enter character: k
Lower case.

So, it works, yes, but the compiler warning is still there, and the program still relies on undefined behavior in scanf(), so it is still buggy and broken even though it works as expected. Weird, huh? Well, that's how it is. We are relying on predictable (for my compiler and computer hardware) but undefined behavior (per the C standard), to get my program to "work". It is broken though. Fix the compiler warning. Add -Werror back to turn warnings into errors, and use char a; instead of int a; to fix the undefined behavior in scanf().

The end.

References

  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
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
  • please let me know that, In c how data is stored in memory. Suppose when I'm initialising 12 to a variable of type int, then it is stored in binary numbers such as (1100). But as you can see that (1100) having 4 bits only, what about remaining 12 bits. – Koustubh Sharma Mar 23 '23 at 07:37
  • @KoustubhSharma, when you do `int a;`, variable `a` gets assigned an address in memory pointing to a chunk of memory large enough to hold an `int`. This is architecture-dependent. On an 8-bit Arduino Uno (ATMega328 microcontroller), that's 16 bits (2 bytes). On a 32-bit STM32 microcontroller, that's 32 bits (4 bytes). On my 64-bit x86-64 Linux machine, that's also 32 bits (4 bytes). When you assign the variable a value, such as `a = 12;`, *all* bits must be set in that variable to ensure it is a 12. Leading zeros are written as necessary. *All* bits in the number must be properly set. – Gabriel Staples Mar 23 '23 at 15:45
  • @KoustubhSharma, so, the Arduino Uno would set 12 bits to 0, followed by binary `1100`, and the machines which use 4-byte ints would set 28 bits to 0, followed by binary `1100`. Otherwise, the value stored would be larger than 12, instead of 12. When you pass `scanf()` an address to an `int`, however, when it is expecting an address to a `char`, it treats it like an address to a `char`, setting only the number of bits a `char` is expected to have, which is 8 bits. So, the remaining 24 bits in the 4-byte int (4 bytes for my architecture) remain unchanged, as my code output shows above. – Gabriel Staples Mar 23 '23 at 15:46
2

Yes, any of C's integer types can store character values. But what you can't do is use scanf("%c") to read a character value into just any integer type, because scanf("%c") can read character values only into variables of type char.

But you could replace

scanf("%c", &a);

with

a = getchar();

(where a is a short or an int or a long or whatever), and it would work just fine.

(Here, though, using char would actually be poorer, because although it would seem to work, it would screw up getchar's ability to signal EOF to you.)

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
-2

As per your code you have declare variable a as int ,so when you insert any character at the time of input it will assign ASCII value of that character so you need to change the data type of variable from int to char.

Tejas Prajapati
  • 115
  • 1
  • 6
  • 1
    If the variable type was `char` it would also store the ASCII value of that character. This doesn't address the real problem. – Blastfurnace Mar 23 '23 at 00:20