how to avoid unsigned integer compare with signed number? - c

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

Related

How does really c gives memory for int and long long?

I know that in C type int is 4 bytes and long long is 8 bytes. However, as I checked, if you declare two int varuables consequently, (for example int a; int b;) the difference between their addresses is 8 and if you do the same for long long varuables, the difference will be 4.
There is my code:
void ckeck()
{
int ai = 0;
int bi = 0;
printf("%llu\n",(unsigned long)(&bi - &ai));
unsigned long long a = 3*INT_MAX;
unsigned long long b = 0;
printf("%llu\n", (unsigned long long)(&b-&a));
}
You have two mistakes.
First, the size of int is not standardised. For fixed types you would use int32_t from stdint.h. On some platforms it might be 64 bits and on others 16.
Second you are using pointer arithmetic to derive the size, which doens't work that way. You should use sizeof(int) for that.
consider this code:
int i[10];
int* iter = i;
while(iter != i+10)
{
*iter = 123;
iter+=1;
}
Here iter gets incremented by one every time so that it points to the next int. But since int (usually) isn't one byte big, your compiler will replace this with iter += sizeof(*iter) * 1, so that you make a step of exactly one int. This is because usually you don't want to point to the address halfway an int.

how to convert int to void *?

I'm trying to make an analogue of sscanf with a specifier %p.
I use this:
int res = ahex2num(buf);
*va_arg(ap, void **) = (void *) res;
It works correctly, i actually get the address i pass, like 0x1A but i am facing this error:
warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
In main function:
int main(){
void *a;
readFromStr("0x1A", "%p", &a);
printf("%p", a);
return 0;
}
/*
out: 0x1a
*/
Can I somehow avoid this?
long ahex2num(unsigned char *in){
unsigned char *pin = in;
long out = 0;
while(*pin != 0){
out <<= 4;
out += (*pin < 'A') ? *pin & 0xF : (*pin & 0x7) + 9;
pin++;
}
return out;
}
Apparently pointers, particularly void *, have a different size than int on your system. E.g., pointers may be 64 bits and int may be 32 bits. Implementing %p in a routine like sscanf is a valid reason for converting an integer to void *, but you need to use an integer type that can hold all the bits needed for a pointer. A good type for this may be uintptr_t, declared in <stdint.h>.
You will need to ensure all the code that works with the integers from the scanning, such as ahex2num, can support the necessary width and signedness, including handling potential overflow as desired.
If I had your entire code, I could test it. I assume to remove the warning without using a pragma is as simple as changing your typecast from int to long int.
I solved this problem like this:
long long int res = ahex2num(buf);

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.

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

Comparison of two variables

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.

Resources