Pointers ignoring unsigned trait in C? - c

I'm curious why the following code returns negative numbers even though every single variable and fuction initialization in this program is an unsigned int. I thought they always were supposed to be positive?.
#include <stdio.h>
unsigned int main ()
{
unsigned int ch = 0;
unsigned int asteriskValue = 0;
while(ch!=27)
{
ch = getch();
if (ch == 224)
{
increaseDecrease(&asteriskValue);
}
printf("%d",asteriskValue);
}
}
void increaseDecrease(unsigned int *value)
{
unsigned int upOrDown;
upOrDown = getch ();
if(upOrDown == 72)
(*value)--;
else if(upOrDown == 80)
(*value)++;
}

I think your results is really a positive value, but printing it with a "%d" flag, tells printf to interpret it as a signed value, as if you were casting on the fly the type: (signed)asteriskValue
Try printing it with a "%u" formatting instead of "%d".

Related

Why is my program counting letters and digits not running?

I wrote this function tat checks for upper, lower and digits in a string but when I'm trying to run the code this pops up and cant seem to understand the problem.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define size 50
void statistics(char str[], int *lower, int *upper, int *digits) {
for (int i = 0; i < size; i++) {
if (islower(str[i]) != 0) {
*lower = *lower + 1;
} else
if (isupper(str[i]) != 0) {
*upper = *upper + 1;
} else
if (isalpha(str[i])) {
*digits = *digits + 1;
}
}
}
int main() {
char str[size] = { " " };
int upper = 0, lower = 0, digits = 0;
printf("Enter a string:\n");
gets_s(str);
statistics(&str[size], &lower, &upper, &digits);
printf("Lower: %d\nUpper: %d\nDigits %d", lower, upper, digits);
return 0;
}
There are multiple problems in your code:
the gets_s() function is not portable: it is optional and not supported on many systems. You forget to pass the array size, hence causing undefined behavior. The compiler should output a diagnostic that you should not ignore. You should use fgets() instead.
you should not pass char values to the isupper() and similar functions because they are only defined for values of the type unsigned char and the special negative value EOF. Use an unsigned char variable or cast the str[i] argument as (unsigned char)str[i].
you pass the address of the end of the char array instead of the beginning. Just pass str as the argument to statistics. The statistics function reads characters beyond the end of the array, invoking undefined behavior, and one of these bytes happens to be a negative char value less than -1 triggering the diagnostic in your Visual C++ compiler runtime. The error message is difficult to interpret, the IDE should point you to the calling code.
you iterate on the whole array, beyond the null terminator. The contents of the array is undefined beyond the null terminator set by gets_s() or fgets(). Just stop at the null terminator.
you test if (isalpha(ch)) where you probably mean to use if (isdigit(ch))
the isxxx functions return a non zero value for true and zero for false. It is idiomatic in C to just write if (isdigit(c)) instead of if (isdigit(c) != 0) which seems redundant.
defining size as a macro is error prone. Use upper case and a more explicit name.
Here is a modified version:
#include <ctype.h>
#include <stdio.h>
#define LINE_SIZE 50
void statistics(const char *str, int *lower, int *upper, int *digits) {
while (*str != '\0') {
unsigned char ch = *str++;
if (islower(ch)) {
*lower += 1;
} else
if (isupper(ch)) {
*upper += 1;
} else
if (isdigit(ch) {
*digits += 1;
}
}
}
int main() {
char str[LINE_SIZE];
int upper = 0, lower = 0, digits = 0;
printf("Enter a string:\n");
if (fgets(str, sizeof str, stdin)) {
statistics(str, &lower, &upper, &digits);
printf("Lower: %d\nUpper: %d\nDigits %d", lower, upper, digits);
}
return 0;
}

Decimal to binary using string in c....stuck with some conceptual error

#include<stdio.h>
char bin(int);
int main()
{
setbuf(stdout,NULL);
int num;
char res[50];
printf("Enter the number: ");
scanf ("%d",&num);
res=bin(num);
printf("%s",res);
return 0;
}
char bin(int num)
{
char str[50];
int i,val;
for(i=0;num>=0;i++)
{
val=num%2;
str[i]=val;
num=num/2;
}
return str;
}
I really cant understand the error in the usage of strings... to convert the decimal to binary. Whats the conceptual error Im not following?
char is a single character, so char bin(int) will not be able to return a string (i.e. a null-terminated array of characters). And you cannot "return" an an array of characters, because C does not allow to return any array as function result. You can just pass/return pointers to the begin of such arrays.
So I'd suggest to change the interface of bin to reicieve the result buffer as parameter. Don't forget to "close" the string, i.e. to write the string termination character after the last "actual" character:
void bin(int num, char* resultBuffer) {
...
resultBuffer[i] = '\0';
}
In main, you call it then like
bin(num, res);
Returning str amounts to returning a local variable, you can't do it, what you can do is to return a pointer to a previously allocated memory block that works as an array (as an alternative to the oher answer, which is a good solution).
To do this you can declare str as a pointer, allocate memory for it and return it, making sure the variable to which the value is assigned is also a pointer, all the rest can remain the same.
There are, however, problems with the bin function.
Consider the statement:
str[i] = val;
This will not work as expected you are assigning the int result of the operation, which will be 1 or 0, you need to convert this value to the respective character.
The loop for (i = 0; num >= 0; i++) is an infinite loop because num will never be negative, unless you provide it a negative number in which case it will break in the first iteration, that is to say this code only works with positive integers. You need > instead of >=.
Finally you need to null terminate the string when the conversion is complete.
Corrected code (Online):
#include <stdio.h>
#include <stdlib.h>
char *bin(int); //return pointer
int main() {
setbuf(stdout, NULL);
int num;
char *res; //use pointer to receive string assignment
printf("Enter the number: ");
scanf("%d", &num);
res = bin(num);
printf("%s", res);
return 0;
}
char *bin(int num) {
char *str = malloc(50); // allocate memory
int i, val;
for (i = 0; num > 0; i++) { // replacing >= with >
val = num % 2;
str[i] = val + '0'; // convert to character
num = num / 2;
}
str[i] = '\0'; //null terminate the string
return str;
}
Note that you should also check for the inputed value, if it is larger than what an int variable can hold it will result in undefined behavior.

can't use sscanf() in C for char array

I'm trying to get a very large number (more than unsigned long long int). So I get it as a string and then convert it, digit by digit, into integer and use it.
#include <stdio.h>
#include <string.h>
int main()
{
char m[100];
int x;
scanf("%s",m);
for(int i=0; i<strlen(m); i++){
sscanf(m[i],"%d",&x);
printf("%d",x);
}
return 0;
}
However, during compiling it shows:
warning: passing argument 1 of ‘sscanf’ makes pointer from integer
without a cast
and
note: expected ‘const char * restrict’ but argument is of type ‘char’
And, when I run program it will give me the Segmentation fault (core dumped) error.
I also tried more simple code to find the problem:
#include <stdio.h>
#include <string.h>
int main()
{
char m[5];
char n;
int x;
scanf("%s",m);
n = m[1];
sscanf(n,"%d",&x);
return 0;
}
but nothing changed.
scanf doesn't apply to characters. Once you have the characters just convert digits to integers by subtracting '0' as a char:
for(int i=0; i<strlen(m); i++){
x = m[i] - '0'; // line to change
printf("%d",x);
}
Besides, just to make sure that the buffer won't overflow, 100 bytes is good, but you may want to use an according limit in scanf and check the return code:
if (scanf("%99s",m) == 1) {
Using sscanf to convert a single digit of a character string to an integer is the wrong approach. To do this, you only need to subtract the (integer) value of the representation of the character '0' from that digit. Like this:
#include <stdio.h>
#include <string.h>
int main()
{
char m[50]; // As pointed out, a 4-digit number isn't really very long, so let's make it bigger
int x;
scanf("%49s", m); // Limit the input to the length of the buffer!
for (size_t i = 0; i < strlen(m); i++) { // The "strlen" function return "size_t"
x = m[i] - '0'; // The characters `0` thru `9` are GUARANTEED to be sequential!
printf("%d", x);
}
return 0;
}

fetching string and converting to double

I'm trying to create a function which can determine if an input can be converted perfectly into a double and then be able to store it into an array of doubles. For example, an input of "12.3a" is invalid. From what I know, strtod can still convert it to 12.3 and the remaining will be stored to the pointer. In my function doubleable, it filters whether the input string consists only of digits. However, I'm aware that double has "." like in "12.3", and my function will return 'X' (invalid).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char doubleable (char unsure[], int length){
int i;
int flag;
for (i=0; i<length; i++ ){
if(isdigit(unsure[i]==0)){
printf("You have invalid input.\n");
flag=1;
break;
}
}
//check for '.' (?)
if(flag==1)
return 'X';
else
return 'A';
}
int main(){
char input [10];
double converted[5];
char *ptr;
int i;
for(i=0; i<5; i++){
fgets(input, 10, stdin);
//some code here to replace '\n' to '\0' in input
if(doubleable(input, strlen(input))=='X'){
break;
}
converted[i]=strtod(input, &ptr);
//printf("%lf", converted[i]);
}
return 0;
}
I'm thinking of something like checking for the occurrence of "." in input, and by how much (for inputs like 12.3.12, which can be considered invalid). Am I on the right track? or are there easier ways to get through this? I've also read about the strtok function, will it be helpful here? That function is still quite vague to me, though.
EDIT:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
double HUGE_VAL= 1000000;
void string_cleaner (char *dirty){
int i=0;
while(1){
if (dirty[i]=='\n'){
dirty[i]='\0';
break;
}
i++;
}
}
int doubleable2(const char *str)
{
char *end_ptr;
double result;
result = strtod(str, &end_ptr);
if (result == HUGE_VAL || result == 0 && end_ptr == str)
return 0; // Could not be converted
if (end_ptr < str + strlen(str))
return 0; // Other input in the string after the number
return 1; // All of the string is part of the number
}
int main(){
char input [10];
double converted[10];
char *ptr;
int i;
for(i=0; i<5; i++){
while (1){
printf("Please enter:");
fgets(input, 10, stdin);
string_cleaner(input);
if (doubleable2(input)==0)
continue;
else if (doubleable2(input)==1)
break;
}
converted[i]=strtod(input, &ptr);
printf("%lf\n", converted[i]);
}
return 0;
}
thank you! It works just fine! I have a follow up question. If I enter a string that is too long, the program breaks. If I am to limit the input to, let's say, a maximum of 9 characters in input[], how am I to do that?
from what I understand about fgets(xx, size, stdin), it only gets up to size characters (including \n, \0), and then stores it to xx. In my program, I thought if I set it to 10, anything beyond 10 will not be considered. However, if I input a string that is too long, my program breaks.
You can indeed use strtod and check the returned value and the pointer given as the second argument:
int doubleable(const char *str)
{
const char *end_ptr;
double result;
result = strtod(str, &end_ptr);
if (result == HUGE_VAL || result == 0 && end_ptr == str)
return 0; // Could not be converted
if (end_ptr < str + strlen(str))
return 0; // Other input in the string after the number
return 1; // All of the string is part of the number
}
Note that you need to remove the newline that fgets most of the time adds to the string before calling this function.
From what I know, strtod can still convert it to 12.3 and the remaining will be stored to the pointer.
That is correct – see its man page:
If endptr is not NULL, a pointer to the character after the last character used in the conversion is stored in the location referenced by endptr.
So use that information!
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
bool doable (const char *buf)
{
char *endptr;
errno = 0;
if (!buf || (strtod (buf, &endptr) == 0 && errno))
return 0;
if (*endptr)
return 0;
return 1;
}
int main (void)
{
printf ("doable: %d\n", doable ("12.3"));
printf ("doable: %d\n", doable ("12.3a"));
printf ("doable: %d\n", doable ("abc"));
printf ("doable: %d\n", doable (NULL));
return 0;
}
results in
doable: 1
doable: 0
doable: 0
doable: 0
After accept answer
Using strtod() is the right approach, but it has some challenges
#include <ctype.h>
#include <stdlib.h>
int doubleable3(const char *str) {
if (str == NULL) {
return 0; // Test against NULL if desired.
}
char *end_ptr; // const char *end_ptr here is a problem in C for strtod()
double result = strtod(str, &end_ptr);
if (str == end_ptr) {
return 0; // No conversion
}
// Add this if code should accept trailing white-space like a \n
while (isspace((unsigned char) *endptr)) {
endptr++;
}
if (*end_ptr) {
return 0; // Text after the last converted character
}
// Result overflowed or maybe underflowed
// The underflow case is not defined to set errno - implementation defined.
// So let code accept all underflow cases
if (errno) {
if (fabs(result) == HUGE_VAL) {
return 0; // value too large
}
}
return 1; // Success
}
OP's code
No value with result == 0 in result == 0 && end_ptr == str. Simplify to end_ptr == str.
Instead of if (end_ptr < str + strlen(str)), a simple if (*end_ptr) is sufficient.
if (result == HUGE_VAL ... is a problem for 2 reasons. 1) When result == HUGE_VAL happens in 2 situations: A legitimate conversion and an overflow conversion. Need to test errno to tell the difference. 2) the test should be if (fabs(result) == HUGE_VAL ... to handle negative numbers.

Arithmetic overflow when using atoi() on argv[1] in C

I am using atoi(argv[1]) Here is a snippet of my code
void main(int argc, char* argv[])
{
int evenOrOdd = 0;
int inputtedNum = 0;
pid_t pid;
int i;
char buf[BUF_SIZE];
if (argc != 2)//make sure user has input correctly entered
{
printf("Please use input: ./a.out #\n");
}
else
{
inputtedNum = atoi(argv[1]);
if(inputtedNum < 0) //make sure number is positive
{
printf("Please use a positive number.\n");
return;
}
}
// ...
}
The code goes on and whatnot, but when entering some numbers, it will sometimes believe they're negative. If I enter 123456789 it will have no problem and run the program correctly. If I enter 12345678910 It will think the number is a negative number. Am I running into some buffer error here to where the negative flag flops? I'm not quite certain how to fix it.
Any and all help is appreciated!
int is a relatively narrow integer type, and also it is a signed type. Use unsigned long for inputtedNum and strtoul(argv[1], 0, 0) to parse the number.
On most compilers, the largest non-negative integer type supported in C is a 64-bit unsigned integer.
12345678910 cannot be stored in the int type that you are using, even if it was unsigned long.
You can solve this issue by "dividing" the input string in argv[1] into two strings, when it is longer than 8 digits, use atoi on each string, and combine the results into an unsigned long long variable.
This will extend your positive range from approximately 4 billion, to the largest 16-decimal-digit number.
From main, call readNum(argv[1]):
unsigned long long readNum(const char* input)
{
int length = strlen(input);
if (length <= 8)
{
return atoi(input);
}
else if (length <= 16)
{
char str1[8+1] = {0};
char str2[8+1] = {0};
memcpy(str1,input,8);
memcpy(str2,input+8,length-8);
return 100000000ULL*atoi(str1)+atoi(str2);
}
return 10000000000000000ULL; // Invalid input
}
Keep in mind that in order to print an unsigned long long, you need to use printf with %llu.

Resources