I'm learning C, and have been trying to make a program that takes user input, and removes any double spaces in it, then prints it out again. We have not done arrays yet so I need to do this char by char. This is my code:
#include <stdio.h>
main()
{
char c;
int count;
count = 0;
while ((c = getchar()) != '\n')
if (c == ' ')
count++;
if (c != ' ')
count = 0;
if (count <= 0)
printf("%s", c);
}
This code does not work, however. The compiler returns the error
:15: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘int’
Any help? I have no clue what I am doing wrong.
Use the %c format specifier to print a single char
printf("%c", c);
The %s format specifier tells printf to expect a null-terminated char array (aka a string).
The error message refers to c having type int due to default promotion of arguments (beyond the format string) passed to printf. This previous answer has a nice description of default promotion; this previous thread explains some of the reasoning for why default promotion is necessary.
You are using %s which is used for a string and which expects a terminating NULL character(\0)..
Using %c will print you char by char..
Your code has so many problems
First, you print a char with %s (which expects a char pointer, i.e. a string)
In C, char literals are of type int, so whether promoted or not, they're always int. In C++, char literals would be of type char, but after promotion like other answers said, again they'll be int. A plain char variable will also be promoted to int in expressions, and passed as int in vararg functions like printf. That's why the compiler warns you that argument 2 has type ‘int’, because it's expecting a char* and you're passing it an int
→ You must use %c to print a char
Your while loop's body is only the first if block, because in C block scope is defined by {}, not by indentation. So the code will run like this, which is not like what you intended
while ((c = getchar()) != '\n')
{
if (c == ' ')
count++;
}
if (c != ' ')
count = 0;
if (count <= 0)
printf("%s", c);
→ You need to put the code block in a pair of brackets. And using else instead of 2 separate ifs to make it more readable and faster (for dumb compilers)
while ((c = getchar()) != '\n')
{
if (c == ' ')
count++;
else
count = 0;
if (count <= 0)
printf("%s", c);
}
main() is wrong. The correct versions in C would be
int main(void)
int main(int argc, char **argv)
See What should main() return in C and C++?
c must be declared as int because getchar returns int. See Why must the variable used to hold getchar's return value be declared as int?
A minor point is that instead of int count; count = 0;, just initialize the variable while declaring int count = 0. Or better yet use an unsigned int, because count can't be negative
Related
int main(void)
{
char s[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int i;
i = 0;
while (s[i] != '\0') {
printf("%c -> %c\n", s[i], lower(s[i]));
i++;
}
return 0;
}
int lower(int c)
{
return (c >= 'A' && c<= 'Z') ? c + 'a' - 'A' : c;
}
This is the program to convert all the alphabets to lower case . So in the solution they used a lower function but I don't know why they used int as return type.
Why is function 'lower' has int as return type and input type?
The code is entirely int math. Even if the signature was char c, the code would have an int result due to the usual integer promotions.
int lower(int c) {
return (c >= 'A' && c<= 'Z') ? c + 'a' - 'A' : c;
}
To map typically 257 different values like the tolower(). With int lower(char c) { and c < 0, this could function differently than tolower().
int is usually near the native processor integer size and code is usually tightest and fastest with int versus char. This is a C historically choice and compromise. Including original C did not prototype the function signature and all int and sub-int arguments were promoted to int.
7.4 Character handling
The header <ctype.h> declares several functions useful for classifying and mapping
characters. In all cases the argument is an int, the value of which shall be
representable as an unsigned char or shall equal the value of the macro EOF. If the
argument has any other value, the behavior is undefined. C11dr §7.4 1
These is...() and to...() functions work in the unsigned char range (and EOF) and not char.
Robust code would avoid negative char when calling tolower() or tolower()-like functions.
// printf("%c -> %c\n", s[i], lower(s[i]));
printf("%c -> %c\n", s[i], lower((unsigned char) s[i]));
Its most likely attempting to mimic the tolower function which uses int.
A similar question on tolower using an int is here.
Why putchar, toupper, tolower, etc. take a int instead of a char?
PVS-Studio gave me a warning about this :
char c;
sscanf(line, "%d", &c);
I changed %d to %c but this created a bug because "c" now contains the ASCII value of the number and not the decimal one, so I went back to "%d".
So what's the correct specifier to ? is there another solution ?
c is a char. You asked to scan an int. PVS-Studio did right in warning you. Change the type of c to int and scan for a %d.
There are multiple solutions for your problem:
you can specify the correct destination type:
char c;
if (sscanf(line, "%hhd", &c) == 1) {
/* successful conversion */
...
}
you can use an intermediary variable:
char c;
int cc;
if (sscanf(line, "%d", &cc) == 1) {
/* successful conversion */
c = cc;
...
}
you can use different conversion function:
#include <stdlib.h>
...
char c;
c = atoi(line); // no error handling, return 0 if not a number
Note however that in all cases, if the numeric value converted by sscanf() or atoi() is outside the range of type char, the behavior is undefined. Most current system will just use the low order byte of the conversion result, but the C Standard does not guarantee it.
#include <stdio.h>
int main (void)
{
int c;
while ( (c = getchar ()) != EOF )
putchar (c);
return 0;
}
So in this program, there is c = getchar (). So i would assume that getchar returns to an integer regardless of what you type?
If that was the case, if I had the code:
#include <stdio.h>
int main (void)
{
char c;
c = getchar();
printf("%c",c);
return 0;
}
It should not work. As if I typed 'y' for example, the getchar function can return to an integer that is 2digits or more and cannot be stored in a char variable. Yet it worked.... In fact, when I type 'f' c becomes f. But why??
Characters are integers, or rather there are contexts where certain integer values are interpreted as characters. The type char is just an integer type in C, so an int can hold all values of a char.
One reason why getchar returns an int is because the special value EOF is not any of the integer values that represent characters (because if it were, you could not distinguish end of file from such a character being input). Once you have checked that the value returned by getchar was not EOF, all other possible return values are characters.
So in this program, there is c = getchar (). So i would assume that getchar returns to an integer regardless of what you type?
True. Detail: int getchar() returns not any integer but an int. The return value is either in the unsigned char range (example 0 ... 255) or EOF. EOF is a negative value.
char c = getchar();
printf("%c",c);
... should not work. As if I typed 'y' for example, the getchar function can return to an integer that is 2 digits or more and cannot be stored in a char variable. Yet it worked.... In fact, when I type 'f' c becomes f. But why??
If the user types y, the typical reuslt is a valueof 121 (ASCII y). The int in not "2 digits or more". The int is a value store in memory, typical 32-bits. It is not store as "digits".
The printf("%c",c); accepts an int as to match the "%c". As a char, c goes though integer promotion (to an int) before being passed to printf(char *format, ...) all it well specified.
The following will typically all print the same. "%c" recieves the int value, converts it to an unsigned char and then print the corresponding character associate with that value. usually ASCCI y.
char c = 'y';
printf("%c", c);
printf("%c", 'y');
printf("%c", 121);
printf("%c", 121 + 256);
the code is like this:
char c;
scanf("%d", &c);
inputting 3...
My guess is that when 3 is inputted, it is as type int;
and then type-demoted to char and assigned to c;
I print the value of c in specifier %d yielding 3, seems to be as expected;
but printing the value of c with specifier %c yields --a blank-- on the terminal;this is one question...(1);
to test more I furthermore declare a variable ch with type char and initialized it like this:
char ch = 1;
and the test is like this:
(i&j)? printf("yes") : printf("no");
and the result is "no"
I print out the value of i&j, and it is 0
but 1&3 should be 1? this is another question....(2);
my question is (1) and (2)
You're actually invoking undefined behavior doing that.
By using the format string %d, you're telling scanf to expect an int* as a parameter and you're passing it a pointer to a single character. Remember that scanf has no further type information on what you're passing it than what you're putting in the format string.
This will result in scanf attempting to write an int sized value to memory at an address that points to a char sized reservation, potentially (and for most architectures) writing out of bounds.
After invoking UB, all bets are off on your further calculations.
Suppose that scanf() were not a varargs-function, but a plain ordinary function taking a pointer-to-int as the 2nd argument:
int noscanf(char *format, int *ptr)
{
*ptr = 42;
return 1;
}
int main(void)
{
char ch;
int rc;
// This should *at least* give a warning ...
rc = noscanf("Haha!" , &ch);
return 0;
}
Now, scanf() is a varargs function. The only way for scanf() to determine the type of the (pointer) arguments is by inspecting the format string. And a %d means : the next argument is supposed to be a pointer to int. So scanf can happily write sizeof(int) bytes to *ptr.
I can't see a variable jthere. So i&j will be 0. And yes, if i == 1 and j == 3 then i & j == 1.
(i&j)? printf("yes") : printf("no");
statement gives the output yes,for i=1 and j=3.
And for (1) question ASCII 3 is for STX char which is not printable.
I'm testing this function that's supposed to read input from the user but it throws me a segmentation fault
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX_STRING_LENGTH 10
int
readinput(char *input)
{
int c;
int i=0;
while((c=getchar()) != EOF && c != '\n') //Here is where the warning occurs.
{
input[i]=c;
i++;
}
input[i]=0;
if(strlen(input)>0)
{
if(isalpha(input[0]) && input[1]=='-' && isalpha(input[2]) && strlen(input)==3)
return 0;
else if(!strcmp(input, "quit"))
return 1;
else if(!strncmp(input, "save ", 5))
return 2;
else if(!strcmp(input, "undo"))
return 3;
}
return -1;
}
int main()
{
char *str;
printf("write a string\n");
int nr=readinput(str);
printf("%d\n", nr);
printf("%s\n", str);
return 0;
}
I did notice the stupid error I made, but still, segmentation fault, why?
This is because EOF is defined (in my compiler) as -1 and char is unsigned byte. so it is always !=
c != '/n' is wrong
change it to
c != '\n'
c != '/n' should be c != '\n'
\ is an escape character which indicates, in the case where it is followed by n, a newline. /n will be treated as two distinct characters, which cannot properly be compared to a single char variable.
As for you segmentation fault, you'll need to allocate some space for str in your main function:
char* str = malloc(sizeof(char)*MAX_STRING_LENGTH);
or
char str[MAX_STRING_LENGTH];
but you'll also have to ensure you don't try to read a string that has more characters than your str array can hold.
It faults because you never allocated space for str and it points to a random location which causes readinput to try to store data in a place that doesn't exist.
The segmentation fault arises because you've passed an uninitialized pointer to the function readinput(). You need to do something like:
char str[4096];
int nr = readinput(str);
You should pass in a length of the array so that the called code can verify that it does not overflow its boundaries. Or you can live dangerously and decide that 4096 is big enough, which it probably will be until someone is trying to break your program deliberately.
The original compiler warning was because the multi-character constant '/n' has a value (of type int) which is outside the range of values that can be stored in a char, so when c is promoted to int, the != comparison with the (implementation-defined) value of '/n' is bound to be true. Hence the warning:
Warning: comparison is always true due to limited range of data type
All multi-character character constants have implementation-defined values. There are no portable multi-character character constants.
ISO/IEC 9899:2011 §6.4.4.4 Character constants
¶10 ... The value of an integer character constant containing more than one character (e.g.,
'ab'), or containing a character or escape sequence that does not map to a single-byte
execution character, is implementation-defined. ...