Comparison of two variables - c

I've just a great programming puzzle. Why is to same?
#include <stdio.h>
#include <limits.h>
int main(int argc, char *argv[])
{
unsigned int x = ULONG_MAX;
char y = -1;
if (x == y) printf("That is same.");
return 0;
}
I think that unsigned int is converted to signed char, and thus it will be -1. It may be a standard for comparison of signed and unsigned type. I don't know...

In a tiff between signed char and unsigned int, unsigned int wins!
Its like this
Here -1 will be converted to unsigned int which is ULONG_MAX and hence if() condition is true.
In C, size does matter. Variables are always converted to the highest size among them.

Many years ago, I learned a couple of things. One of them was compare like types.
I would either cast the char to an unsigned int if the unsigned int's value is greater than sizeof char. Or cast the other way if the unsigned int's values are to be restricted to a sizeof char. In that way, you are telling the compiler how you are comparing the values, and it will help maintainers as well.

Related

how to avoid unsigned integer compare with signed number?

void main() {
unsigned int a = 0;
if (a - 10 < 0) {
printf("error!\n");
}
}
we know this comparison won't work, because a-10 will be a big unsigned integer, it can't be smaller than 0.
To avoid this situation, I try this:
void main() {
unsigned int a = 0;
int b = 0;// or const int b = 0;
if (a - 10 < b) {
printf("error!\n");
}
}
this will get warning C4018 using Visual Studio 2022 17.2.4.
However, when I use gcc 4.8.5, there is no warning at all.
Is there a way to avoid coder compare signed number with unsigned variable?
Update:
a more complex situation could be this:
struct s{
unsigned int len;
char *buffer;
} *a;
int not_safe(struct s *ptr){
if(ptr->len - sizeof(struct s) < 0){
return 0;
}
return 1;
}
Programmers may not be aware of such comparisons are wrong. I
hope we can have a safe way to let programmer avoid this.
If you cast the unsigned int operand to int:
if ((int)a - 10 < 0)
Then all of the math with be done using type int.
Or, you can do a little bit of algebra:
if (a < 10)
To avoid the problem completely.
Please study what C formally calls the usual arithmetic conversions Implicit type promotion rules. The TL;DR is that in case you have two integers of the same size but different signedness, the signed one will get converted to unsigned.
You could explicitly cast the unsigned operand to signed. Or you could let the signed operand be a larger type than the unsigned, such as int64_t, in which case the unsigned int (likely 16 or 32 bits) will get converted to int64_t.
But that won't solve your root problem which is this line:
if(ptr->len - sizeof(struct s) < 0)
This is doesn't make any sense to begin with. And casting ptr->len to int64_t might not help either size sizeof returns a size_t, which is guaranteed to be a large unsigned integer type. Simply replace this with:
if(sizeof(struct s) > ptr->len)
Or if you will, change the whole function to one following common best practices:
bool not_safe (const struct s* ptr) {
return sizeof(struct s) > ptr->len;
}

Is it unsafe to pass an unsigned long to a function which uses a long?

Foreword: I am not allowed to use any functions from the C library
I have this function:
char *to_base(long nbr, char *base)
{
static char buffer[50];
char *ptr;
int base_len;
ptr = &buffer[49];
*ptr = 0;
base_len = ft_strlen(base);
while (nbr != 0)
{
*--ptr = base[nbr % base_len];
nbr /= base_len;
}
return (ptr);
}
As we can see it takes a long. In my program I have an unsigned long which I have to translate into its hexadecimal value. Is it unsafe to pass the unsigned long at this function ? If yes how can I make it work ?
EDIT :
Here is how I am currently using it:
unsigned long val;
char *hexa;
val = (unsigned long)va_arg(*list, void *);
hexa = to_base(val, "0123456789abcdef");
Is it unsafe to pass the unsigned long at this function ?
It's "safe", as in the program will not format your hard drive or start another world war.
Passed function arguments are converted to the argument type. The unsigned value will be converted to a signed one. All architectures today use twos-complement, it's easy to predict the result. Your compiler should document the behavior, ex. in gcc implementation defined beahvior:
The result of, or the signal raised by, converting an integer to a signed integer type when the value cannot be represented in an object of that type (C90 6.2.1.2, C99 and C11 6.3.1.3).
For conversion to a type of width N, the value is reduced modulo 2^N to be within range of the type; no signal is raised.
"reduced modulo 2^N" - basically 2^N is subtracted until the value is within range. So if you have 32-bit longs and have (long)(unsigned long)4294967196ul so you subtract 2^32 from the value and it's equal to (long)-100.
If yes how can I make it work ?
Properly handle negative numbers.
And write a separate function for unsigned and signed numbers.
Also (unsigned long)va_arg(*list, void *); is casting a void* pointer value to an unsigned long (?). Most probably you want va_arg(list, unsigned long) - to take unsigned long from arguments.

Casting a float for int and int to float

Having a little difficulty with pointers. I have to store a float in an array of unsigned ints and be able to pull it out.
I know there is a special way to cast this so I don't reorder the bits, I think this is the correct way to store it when I want to put it into the array:
float f = 5.0;
int newF = (int *) f;
arrayOfInts[0] = *newF
Which seems to successfully store the value in the array.
However, at some point I have to pull the value back out of the array of ints, this is where my confusion comes in (assuming I inputed into the array correctly)
float * f = (float *) arrayOfInts[0]
int result = *f;
however, that gives me the warning: 'cast to pointer from integer of different size'
I can't really think of how to solve that without some sort of long cast.. which doesn't seem right..
I don't want to lose the value or damage the bits.. obviously It will lose decimal point precision.. but I know theirs some way to safety convert back and forth
I have to store a float in an array of unsigned ints and be able to pull it out.
Use a union and unsigned char[]. unsigned char is specified to not have any padding and all bit combinations are valid. This is not always true of many other number types. By overlaying the float with unsigned char[], code can examine each "byte" of the float, one at a time.
union {
float f;
unsigned char uc[sizeof (float)];
} x;
// example usage
x.f = 1.234f;
for (unsigned i = 0; i<sizeof x.uc; i++) {
printf("%u:%u\n", i, 1u*x.uc[i]);
}
Sample output: Yours may vary
0:182
1:243
2:157
3:63
float --> unsigned char[] --> float is always safe.
unsigned char[] --> float --> unsigned char[] is not always safe as a combination of unsigned char[] may not have a valid float value.
Avoid pointer tricks and casting. There are alignment and size issues.
// Poor code
float f = 5.0f;
int newF = *((int *) &f); // Conversion of `float*` to `int*` is not well specified.
Code can also overlay with fixed-width no-padding types like (u)int32_t if they exist (they usually do) and match in size.
#include <stdint.h>
union {
float f;
uint32_t u32;
} x32;
#include <assert.h>
#include <inttypes.h>
// example usage
assert(sizeof x32.f == sizeof x32.u32);
x32.f = 1.234f;
printf("%" PRNu32 "\n", x32.u32);
}
Example output: yours may vary
1067316150
To convert a float to an int
float fval = 123.4f;
int ival = *(int*)&fval;
To convert back
int ival = /* from float */
float fval = *(float*) &ival;
it won't work if float and int are different sizes, but presumably you know that already. The unsigned char union method outlined in other answer for chux is more robust, but over-complicated for what you probably want to do.

warning unsigned long to int in C language

I'd have a quick question over my codes:
int main(void)
{
int i,j,key[5][5],ikey[5][5],row,col,plen,suc;
int devide,count,h,k,no,p1[100],e1[100],d1[100];
char p[100],e[100],d[100],clen;
printf("Enter your plaintext::::::::");
gets(p);
plen = strlen(p); // this line gets error
warning line is:
plen = strlen(p);
implicit conversion loses integer precision unsigned long to int.
As already mentioned the function strlen(p) returns a value of type size_t. Here is an abstract from the C99 standard:
size_t
which is the unsigned integer type
Now the point here is that what exact type it is(unsigned, unsigned long etc.) is actually implementation-defined, which means it varies depending on the platform. Obviously you have it defined as unsigned long which means that by
plen = strlen(p);
you convert unsigned long to int which the compiler can't ignore because in some cases this conversion may lose the value's precision.
The correct declaration:
#include <stddef.h>
size_t plen;
// ...
plen = strlen(p);
As others have already pointed out, strlen() returns a size_t.

C Character from literal Splint warns of incompatible types

I have a program that uses unsigned chars to represent integers with a small range. I find myself needing to clear them to 0 in several different parts of the program, I have also recently started using splint and apparently:
unsigned char c = 0;
gives the warning:
Variable c initialized to type int, expects unsigned char: 0
Types are incompatible.
As there is no suffix for a literal char, How is it best to resolve this? I think I have a few options:
1: Ignore the warning.
2: Cast every time:
unsigned char c = (unsigned char)0;
3: Make a var to cut down the length of the code:
unsigned char uc_0 = (unsigned char)0;
unsigned char c = uc_0;
4: A function:
static inline unsigned char uchar(int in)
{
return (unsigned char)in;
}
unsigned char c = uchar(0);
splint has an option +charint that will treat char int as interchangeable.
You can ignore the warnings and use
unsigned char = 0;
In many cases when there is integer operation in order to save memory instead of using int which obviously consumes extra memory than char people do make use of unsigned char.
unsigned char i = 10;
unsigned char j = 1;
unsigned char k = i +j;
printf("%d",k);

Resources