About conversion specifier of scanf in C - c

I have a question about conversion specifier of scanf in C.
#include <stdio.h>
int main(void)
{
int num1;
scanf("%hhd",&num1);
printf("%d \n",num1);
}
Since I have typed "%hhd" as the conversion specifier in scanf function, when my input is "128", I expect -128 to be stored in num1. But the result shows that 128 is stored in num1.
Why does this happen even though I have used "%hhd" to specify input as char int?

Step 1 With scanf() issues: check results. Unless the return value indicates something was written into num1, printing it does not reflect user input.
int num1;
if (scanf("%hhd",&num1) == 1) {
printf("%d \n",num1);
}
Step 2: Enable compiler warnings. Saves time.
if (scanf("%hhd",&num1) == 1) {
// warning: format '%hhd' expects argument of type 'signed char *',
// but argument 2 has type 'int *' [-Wformat=]
Why does this happen even though I have used "%hhd" to specify input as char int?
This in not about integer types: int versus char. The is about pointers: int * versus signed char *. "%hhd" in scanf() matches a pointer: signed char * (or unsigned char * or char *), not an int nor char.
Code passed &num1, a int *. Compliant code would pass a signed char *.
If a conversion specification is invalid, the behavior is undefined. C11 §7.21.6.2 13
That is it. Code broke the rules, expected behavior does not occur. Use the correct type.
signed char num1;
if (scanf("%hhd",&num1) == 1) {
printf("%d \n",num1);
}
If code must save the result in an int:
int num1;
signed char ch1;
if (scanf("%hhd",&ch1) == 1) {
num1 = ch1;
printf("%d \n",num1);
}

Related

warning: format ‘%d’ expects argument of type ‘int *’, but argument 3 has type ‘uint8_t * {aka unsigned char *}

I've been getting an issue that says:
warning: format ‘%d’ expects argument of type ‘int *’, but argument 3 has type ‘uint8_t * {aka unsigned char *}
and I am not sure why this is happening because I thought int and uint8_t were interchangeable.
Here is my code:
uint8_t** fileRead(char* file, int* pointer) {
FILE* file = fopen(file, "r");
int count = 0;
pointer = &count;
fscanf(file, "%d", &count); //this retrieves a single integer
uint8_t** result = (uint8_t**) malloc(*pointer * sizeof(uint8_t*));
while (file != NULL) {
for (uint8_t i = 0; i < *pointer; i++) {
uint8_t* a = (uint8_t*) malloc(sizeof(uint8_t));
uint8_t* b = (uint8_t*) malloc(sizeof(uint8_t));
uint8_t* c = (uint8_t*) malloc(sizeof(uint8_t));
fscanf(file, "%d", a);
fscanf(file, "%d", b);
fscanf(file, "%d", c);
if (a == NULL || b == NULL || c == NULL) {
uint8_t* npointer = NULL;
fclose(file);
free(result);
return NULL;
} else {
result[i][0] = *a;
result[i][1] = *b;
result[i][2] = *c;
free(a);
free(b);
free(c);
}
}
}
return result;
}
and the errors are occuring at the following lines:
fscanf(file, "%d", a);
fscanf(file, "%d", b);
fscanf(file, "%d", c);
Thank you so much in advance
I thought int and uint8_t were interchangeable.
They are not - int (signed or unsigned) must be at least 16 bits wide (it may be wider, but not narrower)1. uint8_t, as the name suggests, is 8 bits wide.
You need to use the format specifier %hhu to read a numeric value into a uint8_t or unsigned char object.
Strictly speaking, an int must be able to represent all values in at least the range [-32767..32767]. That requires at least 16 bits. Some architectures (which I've never used) have "padding bits" that contribute to the word size, but aren't used to store a value. So, for example, you could have a 9-bit machine with 18-bit words, but an implementation on that system could still choose to just use 16 bits to represent an `int` value - two of the bits simply aren't used.
Use "%hhu" as the format specifier instead of "%d".
To read an integer into an unsigned char with (f)scanf, you need "%hhu".
When printing, you can get away with using %d as the format specifier because a uint8_t is promted to an int when passed to a variadic function like printf. This promotion does not however occur for pointers, which is what scanf expects for most format specifiers.
The %d format specifier for scanf as you've seen expects an int *. On most systems you're likely to come across, an int is 4 bytes. So when scanf dereferences the pointer you give it for %d to write the value it will write 4 bytes. This breaks if you pass in a uint8_t * since this type only occupies 1 byte. By using %d, scanf will think you're passing the address of an int and write 4 bytes, which is 3 bytes past the end of the given variable. Doing this invokes undefined behavior.
To fix this, change the format specifier to %hhu, which expects a unsigned char *.

Convert a char with va_arg

I need to get from va_arg a char.
i use an integer, but my problem it's does not work !
char c = (char)va_arg(ap, int);
write(1, &c, 1);
it's gave the ascii code of something else.
Arguments to variadic functions (the ones corresponding to the ... in the declaration) undergo the default argument promotions.
Integer arguments narrower than int are promoted to int (or to unsigned int if the type is unsigned and its maximum value exceeds INT_MAX), and arguments of type float are promoted to double.
So you can't get a char from va_arg(). The obvious
char c = va_arg(ap, char); // DON'T DO THIS
has undefined behavior.
What you're doing:
char c = (char)va_arg(ap, int);
looks correct, though the (char) cast is unnecessary; the int result will be implicitly converted to char anyway.
For example,this program's output is c = 'x':
#include <stdio.h>
#include <stdarg.h>
void func(int first, ...) {
va_list ap;
va_start(ap, first);
char c = va_arg(ap, int);
va_end(ap);
printf("c = '%c'\n", c);
}
int main(void) {
func(42, 'x');
}
You need to update your question to describe what the actual problem is. You say "it's gave the ascii code of something else"; I have no idea what that means.

passing argument makes pointer from integer without a cast

I've read through several similar questions on Stack Overflow, but I've not been able to find one that helps me understand this warning in this case. I'm in my first week of trying to learn C though, so apologies if I've missed an obvious answer elsewhere on Stack Overflow through lack of understanding.
I get the following warning and note:
warning: passing argument 2 of ‘CheckIfIn’ makes pointer from integer without a cast [enabled by default]
if(CheckIfIn(letter, *Vowels) ){
^
note: expected ‘char *’ but argument is of type ‘char’
int CheckIfIn(char ch, char *checkstring) {
When trying to compile this code:
#include <stdio.h>
#include <string.h>
#define CharSize 1 // in case running on other systems
int CheckIfIn(char ch, char *checkstring) {
int string_len = sizeof(*checkstring) / CharSize;
int a = 0;
for(a = 0; a < string_len && checkstring[a] != '\0'; a++ ){
if (ch == checkstring[a]) {
return 1;
}
}
return 0;
}
// test function
int main(int argc, char *argv[]){
char letter = 'a';
char *Vowels = "aeiou";
if(CheckIfIn(letter, *Vowels) ){
printf("this is a vowel.\n");
}
return 0;
}
Vowels is a char*, *Vowels is just a char, 'a'. chars get automatically promoted to integers, which your compiler is allowing to be implicitly converted to a pointer. However the pointer value will not be Vowels, it will be the address equal to the integer encoding of the character 'a', 0x61 almost universally.
Just pass Vowels to your function.
In your case, the type conversion is from char to integer pointer. In some cases, the function takes void pointer as the second argument to accommodate for all the data-types.
In such cases, you would need to typecast the second argument as (void *)
This would be the function declaration in most well written modular functions:
int CheckIfIn(char ch, void *checkstring);
You would need to pass the argument as a void pointer, provided the Vowels is not a char pointer
if(CheckIfIn(letter, (void *)Vowels) ){
printf("this is a vowel.\n");
}

C programming warning: array subscript has type 'char' [-Wchar-subscripts]

I can't seem to fix this problem. Below is my code:
#include<stdio.h>
#include<ctype.h>
#include<string.h>
_Bool are_anagrams (const char *word1, const char *word2);
int main (void)
{
char an1[30], an2[30];
int j;
printf("Enter first word: ");
scanf("%s", an1);
printf("Enter second word: ");
scanf("%s", an2);
printf("The words are");
j = are_anagrams (an1, an2);
if (j == 0)
{
printf(" not anagrams. \n");
}else
printf(" anagrams. \n");
return 0;
}
_Bool are_anagrams (const char *word1, const char *word2)
{
int i;
int check[26] = {0};
for(i=0; i<30; i++)
if(word1[i] == '\0')
i=40;
else
{
word1[i] = toupper(word1[i]);
check[word1[i]-65]++;
}
for(i=0; i<30; i++)
if(word2[i] == '\0')
i=40;
else
{
word2[i] = toupper(word2[i]);
check[word2[i]-65]--;
}
for(i=0; i<26; i++)
if(check[i] != 0)
{
return 0;
}
return 1;
}
these are the error messages:
anagram1.c:38:3: warning: array subscript has type ‘char’ [-Wchar-subscripts]
word1[i] = toupper(word1[i]);
^
anagram1.c:38:3: error: assignment of read-only location ‘*(word1 + (sizetype)((long unsigned int)i * 1ul))’
anagram1.c:46:4: warning: array subscript has type ‘char’ [-Wchar-subscripts]
word2[i] = toupper(word2[i]);
^
anagram1.c:46:4: error: assignment of read-only location ‘*(word2 + (sizetype)((long unsigned int)i * 1ul))’
The warnings:
warning: array subscript has type ‘char’
are a result of 'toupper()' requiring an 'int' type as a parameter, while the question code is providing a 'char' type.
word1[i] = toupper(word1[i]);
...
word2[i] = toupper(word2[i]);
To eliminate the warning, give toupper() 'int' values:
word1[i] = toupper((unsigned char)word1[i]);
...
word2[i] = toupper((unsigned char)word2[i]);
To be thorough, you can cast the values returned by 'toupper()' from 'int' back to 'char':
word1[i] = (char)toupper((unsigned char)word1[i]);
...
word2[i] = (char)toupper((unsigned char)word2[i]);
The errors:
error: assignment of read-only location
are a result of trying to modify a value with a 'const' flag:
_Bool are_anagrams (const char *word1, const char *word2)
If appropriate, you can eliminate the errors by eliminating the 'const' flags:
_Bool are_anagrams (char *word1, char *word2)
Or, you can make local-working copies of the 'const' strings:
_Bool are_anagrams (const char *I__word1, const char *I__word2)
{
int rCode = 0;
int i;
int check[26] = {0};
char *word1 = strdup(I__word1);
char *word2 = strdup(I__word2);
for(i=0; i<30; i++)
if(word1[i] == '\0')
i=40;
else
{
word1[i] = toupper(word1[i]);
check[word1[i]-65]++;
}
for(i=0; i<30; i++)
if(word2[i] == '\0')
i=40;
else
{
word2[i] = toupper(word2[i]);
check[word2[i]-65]--;
}
for(i=0; i<26; i++)
if(check[i] != 0)
goto CLEANUP;
rCode=1;
CLEANUP:
free(word2);
free(word1);
return(rCode);
}
NOTE: The above code uses the the question code body, which may or may not be accurate. This answer has no intention to fix other issues in the question code; only to demonstrate a proper method to work around the 'const' flags on the parameters by creating non-'const' copies of the parameters
The toupper and tolower functions declared in <ctype.h> (along with the is*() functions) expect an argument of type int.
The type isn't the problem, since char will be implicitly converted to int. The problem is that the value they expect must be either within the range of unsigned char or the value EOF (typically -1). We can ignore the EOF case.
Plain char is either signed or unsigned, at the whim of your compiler developer (guided by your system's ABI). If plain char is signed, and the value you pass to toupper happens to have a negative value (that doesn't happen to equal EOF), then you have undefined behavior.
The solution is to explicitly convert (cast) the argument to unsigned char.
Rather than:
word1[i] = toupper(word1[i]);
you need to write:
word1[i] = toupper((unsigned char)word1[i]);
Yes, it's unfortunate that you have to do this. It would be better if toupper() simply took an argument of type char and returned a char result. But this is the way it is, and we're stuck with it.
So why did you get a warning about an array subscript of type char? The toupper function is commonly implemented as a macro that expands to an array indexing operation. After the preprocessor expands the macro invocation, it no longer looks (to the rest of the compiler) like a function call. (Any standard library function can be implemented as a macro, as long as the macro has the same behavior that an actual function call would have.)
As Mahonri Moriancumer correctly identifies in his answer (and, indeed, as Keith Thompson correctly identified and explained in his answer), the problem with the calls to toupper() is that the type you're passing is char yet the functions expect an int. But since an int can contain any value that a char can contain, why is it complaining?
The answer is that the range of valid int values is restricted by the standard (ISO/IEC 9888:2011):
7.4 Character handling <ctype.h>
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.
What the compiler is warning you is that if you pass a char to this function, and if the char type is a signed type, then you may be passing an index that is negative. The result of getchar(), getc() or fgetc() matches the specification of the argument type for the functions (and this is not an accident). The compiler assumes you won't pass out of bounds values if you use an int, but if you use plain char to hold characters such as 'Å' (U+00C5, LATIN CAPITAL LETTER A WITH RING ABOVE) and plain char is signed, then you will be passing a negative value well outside the range of valid values.
If it were my code, I would cast to unsigned char:
word1[i] = toupper((unsigned char)word1[i]);
Simply casting a signed plain char to int doesn't deal with the sign properly.
You can review C isupper() function to understand more about why the ranges are set as they are.
The other pair of errors comes because you modify a constant string. You can avoid that error (for unaccented characters) with:
unsigned char uc = word1[i];
if (isalpha(uc))
check1[toupper(uc) - 'A']++;
This avoids problems with spaces, digits and punctuation in the input. However, if you need to deal with accented characters in the input, then your best bet is to make check into an array of size 256, and then check over the whole range 0..255 that the counts are the same.

2-D character array

#include<stdio.h>
int main()
{
char str[3][10]={
"vipul",
"ss",
"shreya"
};
Why this won't work:
printf("%s",str[1][0]);
If i want to access str whereas
printf("%s",&str[1][0]);
or this would do it perfectly
printf("%s",str[1]);
Can anyone explain ?
Why is the first code giving an error
prog.c: In function ‘main’:
prog.c:9:5: error: format ‘%s’ expects argument of type ‘char *’, but
argument 2 has type ‘int’ [- Werror=format]
cc1: all warnings being treated as errors
Why does the argument has type int?
printf("%s",str[1][0]);
The problem is in this line. When For %s format specifier, printf() expects a pointer to a null terminated string. Whereas str[1][0] is simply a char (specifically the first s in "ss"), which is promoted to int (default argument promotions). That's exactly what the error message says.
Well
str[1] is a char* and str[1][0] is a char.
But when you use %s, printf() expect a pointer so you try to cast the char into a pointer.
So your char is promoted to an int.
It is said in the error:
format ‘%s’ expects argument of type ‘char *’
and your argument str[1][0] is a char, not the expected char *. In C, a char is treated as an int.
In first case printf("%s",str1[1][0]); you are passing single character to printf function and format specifier that you use it %s. For %s printf function expects string of character and not character.So it gives error.
As in 1st printf function you are specifying %s and you are passing character, argument promotion will takes place and char will promoted to int.
•The default argument promotions are char and short to int/unsigned int and float to double
•The optional arguments to variadic functions (like printf) are subject to the default argument promotions
More on Default argument promotion and here.
on your line error:
printf("%s",str[1][0]);
you try to print a string where you have a character ("%c" in printf)
so to only print one of ur 2D array you would have to do something like that:
int main()
{
int i;
char str[3][10]=
{
"vipul",
"ss",
"shreya"
};
i = 0;
while(str[0][i] != '\0')
{
printf("%c",str[0][i]);
i++;
}
}
which is pretty ugly ^^
instead you could print all ur 2D array with 3 single iteration with that:
int main()
{
int i;
char str[3][10]=
{
"vipul",
"ss",
"shreya"
};
i = 0;
while(i < 3)
{
printf("%s\n",str[i]);
i++;
}
}

Resources