As double and unsigned long are the same size, va_arg should pop the equal number of bytes from the stack and the value displayed should be the same every time the program is run since the double value doesn't change. But this is somehow not the case, and the value displayed is nowhere to be found in the neighboring memory cells.
Tested on gcc version 9.4.0.
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
void test(int num, ...)
{
va_list arguments;
va_start(arguments, num);
unsigned long x = va_arg(arguments, unsigned long);
printf("%lu\n", x);
va_end(arguments);
}
int main(void)
{
double x = 1.234;
printf("sizeof(double) = %zu\n", sizeof(x));
printf("sizeof(unsigned long) = %zu\n", sizeof(unsigned long));
test(1, x);
}
As double and unsigned long are the same size, va_arg should pop the equal number of bytes from the stack ...
No.
Argument passing mechanisms for different types are not specified by C to be the same nor from the stack. *1
OP's code has undefined behavior (UB).
Use va_arg(arguments, unsigned long); to get an unsigned long.
Use va_arg(arguments, double); to get a double.
*1 Advanced: Some common passing mechanisms may exist for types pairs like int/unsigned, but that does not apply here.
Related
why value a and x is different? I think it should be the same
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int a =5;
int *x=&a;
printf("%ld\n", sizeof(a)); // print 4
printf("%ld\n", sizeof(x)); // print 8
return(0);
}
I am not sure whether you are aware of what you are actually measuring with the sizeof operator.
First you measure integer a, which is the size of an int (4 bytes).
And second you measure not an integer but a pointer which stores the address of integer a, so the value is different and I think the value of x is 8 bytes.
int a (4 bytes INTEGER)
int *x = &a (is address of a)
But I can be wrong.
why value a and x is different? I think it should be the same
a is an int and x is a pointer.
int a = 5;
int *x = &a;
C does not specifier an int and a pointer need to be the same size. They may have the same size. Commonly a pointer is as wide as an int or wider, yet either one may be wider than the other.
It is implementation defined.
Since C99, use "%zu" to print a size_t, which is the resultant type from sizeof.
// printf("%ld\n", sizeof(a));
printf("%zu\n", sizeof(a));
// or
printf("%zu\n", sizeof a); // () not needed about objects.
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.
I'm reproducing printf from scrap and I need to store pointers address into a string then print it, so first I cast void* into an unsigned int then itoa it to hexadecimal but the last three char are wrong.
int main(void)
{
char str[] = "printf from scrap!";
my_printf("MY_PRINTF:'%p'", (void*)str);
printf("\n PRINTF:'%p'\n\n", (void*)str);
return (0);
}
int conv_p(va_list args)
{
void *ptr;
unsigned int ptrint;
ptr = va_arg(args, void*);
ptrint = (unsigned int)&ptr;
my_putstr("0x7fff");
my_putstr(my_itoa_base_uint(ptrint, 16));
return (1);
}
Output:
MY_PRINTF:'0x7fff505247b0'
PRINTF:'0x7fff50524a20'
As you can see the last three char are wrong, is there any documentation about that?
In the second case, you're converting the address of the variable ptr to an int, rather than its value (the pointer you're interested in).
Replacing (unsigned int)&ptr; with (unsigned int)ptr; will give you consistent values.
And an additional aside: there's no guarantee unsigned int is large enough to represent the pointer value: you should use intptr_t or uintptr_t from <stdint.h>.
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.
I'm an IT student and today I had a C programming exam.
One of questions is a bit confusing: Is it possible to cast float* to int* ?
I said no whats your opinion?
You can cast, but you won't magically get an integer version of the float.
What happens is that you have a float somewhere in memory and then have an integer point to the same location. But the two are represented differently (e.g., two's complement vs IEEE 754), and you will get a vastly different integer as a result.
For example:
#include <stdio.h>
int main() {
printf("sizeof(int) = %zu\n", sizeof(int));
printf("sizeof(float) = %zu\n", sizeof(float));
float m = 456.78f;
int *p = (int*)&m;
printf("Float: %f\n", m);
printf("Integer: %d\n", *p);
return 0;
}
On my system, this outputs:
sizeof(int) = 4
sizeof(float) = 4
Float: 456.779999
Integer: 1139041239
I print their sizes as well, to emphasize that they could be different as well.
E.g., if the integer happened to be larger, you would then touch memory outside of the float.
While this is probably not what you want, there are uses for it. For example, if you want to see the bit-representation for a specific float, you could cast it to something like uint8_t and inspect it up to sizeof(float).
Why not just try it out?
$ cat foo.c
#include <stdio.h>
int main(){
float f = 0.15625;
float *p = &f;
printf("%f\n", *p);
printf("%i\n", *((int *)p));
}
$ gcc foo.c
$ ./a.out
0.156250
1042284544
The float is 32-bits and you can read about how its represented: http://en.wikipedia.org/wiki/Single-precision_floating-point_format
Casting the float pointer to an int pointer is just telling C that "the data I'm pointing to is an int".
The float 0.156250 is represented by the binary 0111110001000000000000000000000, which represents the integer 1042284544.
EDIT: #csl beat me to the answer :)