So I'm writing my own printf function so I'm using stdarg.h and the system call write(). But I have no idea on how to incorrect %p into my function. Would %X produce the same result?
I have %x done as so:
'x': x=va_arg(argp,argp, unsigned int);
char *temp = convert(x,16);
write(1, temp, lengthOFtemp);
break;
char *convert(unsigned int, int)
{
static char buf[33];
char *ptr;
ptr=&buf[sizeof(buff)-1];
*ptr='\0';
do
{
*--ptr="0123456789abcdef"[num%base];
num/=base;
}while(num!=0);
return(ptr);
}
The difference between %p and %X is that pointer sizes are platform specific - 8 bytes on 64-bit platforms, 4 bytes on 32-bit platforms. Thus, you must use an 'unsigned long' variable to handle the pointer argument from va_args, not 'unsigned int' which remains 4 bytes on some 64-bit platforms.
Otherwise yes, very much like the %X code you have above.
You might like to use
void * pv = va_arg(va, void*)
to pull the pointer from the variadic argument list.
To safely receive pointers this way the caller of the variadic function, which does the pulling as shown above, needs to pass in pointers as being casted to void *.
Then do the conversion to integer like so:
#include <inttypes.h>
...
uintptr_t uipv = (uintptr_t) pv;
and finally call a conversion function:
char buff[64] = {0};
char * pc = convert_uintptr(uipv, 16);
with:
char * convert_uintptr(uintptr_t uipv, unsigned base);
Related
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);
I am trying to use the stdout stream to print 0x00000000 instead of (nil) and 0x08ffffff instead of 0x8ffffff.
So basically, char *ptr1 = 0x00000000 and char *ptr2 = 0x08ffffff.
When I run
printf("start of address is %p and end of address is %p", ptr1, ptr2);
I get
start of address is (nil) and end of address is 0x8ffffff
I actually found a way around this and I would actually do this:
char *ptr1 = 0x00000000;
char *ptr2 = 0x08ffffff;
printf("start 0x%08x and end 0x%08x\n",ptr1, ptr2);
This generates
start 0x00000000 and end 0x08ffffff
However, the compiler sends the following warning:
warning: format ‘%x’ expects argument of type ‘unsigned int’,
but argument 3 has type ‘void *’ [-Wformat=]
How should I modify printf to adjust the output print?
First, to have any control over how addresses are printed, you must convert them to an integer type. The specifications for printing pointers with %p are inadequate. To do this, you can include <stdint.h> and use the type uintptr_t, which is an unsigned integer type suitable for some work with addresses.
Second, the header <inttypes.h> provides a macro PRIxPTR that provides a printf conversion specifier for printing uintptr_t values in hexadecimal.
Third, you can add a flag 0 and a minimum field width 8 to request the conversion pad with zeros to at least eight digits.
Here is sample code:
#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
static void PrintAddress(const void *p)
{
uintptr_t u = (uintptr_t) p;
printf("0x%08" PRIxPTR, u);
}
int main(void)
{
char *ptr1 = (char *) 0x00000000;
char *ptr2 = (char *) 0x08ffffff;
printf("Start of address is ");
PrintAddress(ptr1);
printf(" and end of address is ");
PrintAddress(ptr2);
printf(".\n");
}
Better yet, you can adapt the field width to the size of uintptr_t in your C implementation by including <limits.h> and calculating the number of digits needed:
printf("0x%0*" PRIxPTR, ((int) sizeof u * CHAR_BIT + 3) / 4, u);
Note that directly setting pointers to hard-coded constant values, as you do with the initializations of ptr1 and ptr2, is rare. It is generally used in doing special things with hardware. When you are deliberately converting an integer to a pointer, you should use a cast to avoid a compiler warning.
#include <stdio.h>
#include <stdlib.h>
struct someStruct {
int ivar;
char cvar;
float fvar;
};
main(argc,argv)
const char** argv;
{
struct someStruct someObj;
someObj.ivar = 1;
someObj.fvar = 2.3;
someObj.cvar = 'r';
printf("%u\n",&someObj.fvar);
printf("%u\n",(&someObj.cvar + 4));
printf("%f\n",*((&someObj.cvar) + 4));
printf("%f\n",someObj.fvar);
}
This is a program I wrote which tries to access the address of fvar by adding 4 to the address of char.
I know the concept of slack-byte. I tried to access that memory, and lo! it printed the address correctly.
printf("%u\n",&someObj.fvar);
printf("%u\n",(&someObj.cvar + 4));
Both print the same address.
Then I tried to access fvar in the following way:
printf("%f\n",*((&someObj.cvar) + 4));
printf("%f\n",someObj.fvar);
and
0.000000
2.300000
were my results.
Then I realised that char* and float* are interpreted differently.
So, I tried all kinds of typecasting from char* to float* to float and also from char to float* to float and that too at all possible points like for e.g., typecasting the returned address to float,using 4.0 instead of 4 (which I realised wasn't supposed to work... and it didn't),etc.
It somehow just doesn't print 2.300000 and keeps printing 0.000000
Where am I missing the concept?
Note that I have a 64-bit MinGW and adds slack-byte (I know some don't) which I've already verified by:
printf("%u",sizeof(someObj.ivar));
printf("%u",sizeof(someObj.fvar));
printf("%u",sizeof(someObj.cvar));
printf("%u",sizeof(someObj));
which yields 4, 4, 1 and 12 ....(sizeof(char) + 3) respectively.
P.S. I know this is a horrible idea, but this is how I usually learn concepts XD
Assuming that the following is true:
&someObj.cvar + 4 == &someObj.fvar
you can cast the pointer value to a proper type:
printf("%f\n", *(float*)((&someObj.cvar) + 4));
(&someObj.cvar) is a pointer to char, so *(&someObj.cvar) is a char. The %f printf specifier expects a double or a float, passing a char is invalid. Note: float when passed in variadic functions as one of the arguments in ellipsis parameter is implicitly converted to double, see ex. cppreference implicit conversions. You have to pass a double or a float to %f, not a char.
Notes:
main(argc,argv)
const char** argv;
{
Don't use implicit int declaration and old, deprecated, obsolete style of function declaration. Use the normal style:
int main(int argc, char **argv) {
printf("%u\n",&someObj.fvar); is undefined behavior. The %u expects unsigned char, not float *. You can print a void* pointer using %p specifier: printf("%p\n", (void*)&someObj.fvar); or cast it to unsinged int: printf("%u\n", (unsigned int)(uintptr_t)(void*)&someObj.fvar);
C has an macro offsetof declared in stddef.h to access the number of "slack-bytes" (I like the name "padding bytes" better). You can: printf("%f", *(float*)((char*)&someObj + offsetof(struct someStruct, fvar)));
Can you help me to understand why the value of my dataStruct structure isn't the value of one of its members? (As for the simpleDataStruct strucure)
I print the value with this line:
printf("dataStruct:..............0x%X\r\n", dataStruct);
And the result is:
dataStruct:..............0x22FE20
I use GCC.
My code is:
int main(void)
{
typedef struct Main_SimpleStructData_s
{
unsigned char a;
unsigned char b;
}
Main_SimpleStructData_t;
typedef struct Main_StructuredData_s
{
unsigned char a;
unsigned char* b;
}
Main_StructuredData_t;
unsigned char localDataA = 0xBE;
unsigned char localDataB = 0xEF;
unsigned char localDataC = 0xCA;
unsigned char localDataD = 0xFE;
Main_SimpleStructData_t simpleDataStruct;
Main_StructuredData_t dataStruct;
simpleDataStruct.a = localDataA;
simpleDataStruct.b = localDataB;
dataStruct.a = localDataC;
dataStruct.b = &localDataD;
printf("\r\n");
printf("simpleDataStruct:........0x%X\r\n", simpleDataStruct);
printf("Addr simpleDataStruct: 0x%X\r\n", &simpleDataStruct);
printf("Size simpleDataStruct: %u\r\n", (unsigned)sizeof(simpleDataStruct));
printf("\r\n");
printf("Addr localDataC: 0x%X\r\n", &localDataC);
printf("Size localDataC: %u\r\n", (unsigned)sizeof(localDataC));
printf("Addr localDataD: 0x%X\r\n", &localDataD);
printf("Size localDataD: %u\r\n", (unsigned)sizeof(localDataD));
printf("dataStruct:..............0x%X\r\n", dataStruct);
printf("dataStruct.a: 0x%X\r\n", dataStruct.a);
printf("dataStruct.b: 0x%X\r\n", dataStruct.b);
printf("Addr dataStruct: 0x%X\r\n", &dataStruct);
printf("Addr dataStruct.a: 0x%X\r\n", &(dataStruct.a));
printf("Addr dataStruct.b: 0x%X\r\n", &(dataStruct.b));
printf("Size dataStruct: %u\r\n", (unsigned)sizeof(dataStruct));
return (0);
}
And the result is:
simpleDataStruct:........0xEFBE
Addr simpleDataStruct: 0x22FE4A
Size simpleDataStruct: 2
Addr localDataC: 0x22FE4D
Size localDataC: 1
Addr localDataD: 0x22FE4C
Size localDataD: 1
dataStruct:..............0x22FE20
dataStruct.a: 0xCA
dataStruct.b: 0x22FE4C
Addr dataStruct: 0x22FE30
Addr dataStruct.a: 0x22FE30
Addr dataStruct.b: 0x22FE38
Size dataStruct: 16
In advance, thank you.
The %X conversion takes an unsigned int argument. You incorrectly pass a struct Main_StructuredData_s, which is not an unsigned int, which is undefined behaviour, so I don't know why you would expect to see something reasonable as the result.
edit: As for why the Main_SimpleStructData_t appears to "work" by showing its members, the answer is still that it's undefined behaviour and it may do whatever, including the "correct" thing. The underlying reason in this particular case is almost certainly:
printf tries to read an unsigned int argument (because it doesn't know what you actually passed, when you say that you passed an unsigned int)
The small Main_SimpleStructData_t happens to be passed as an argument in the same way as an unsigned int would be (on your platform), and the printf ends up reading in its members' values.
The larger Main_StructuredData_t happens to be passed as an argument in a different way (e.g., on the stack instead of in a register) and the printf reads some random value instead because the struct isn't in the place where the unsigned int argument would have been.
When you call the function printf, the arguments are pushed on the stack.
printf pops the stack when printing out the values from the stack. the stack contains no information about the data type, that is the job for the format specifier.
The format specifier tells printf about the datatypes passed on the stack and then knows the sizes of those arguments, it has otherwise no way of knowing.
printf cannot handle a user-defined struct like that if you give the format specifier %x will just try something but it is undefined behavior. You can write out the address of the struct prefix it with & and or the members of the struct, but not the struct itself.
You can write your own printf function with a custom format specifier that internally prints out the members after passing the struct by value, but as it is now you have not. search for stdarg.h for more info
#EOF, #Ctx, #Anders and #Arkku: Thank you very much for your help. I understand that the problem is between the laptop and my chair ;) I was stupid but now I'm a man :)
To summary, I don't know use correctly the printf function. GCC had been advertised me with the warnings but I didn't read those...
If my simple Main_SimpleStructData_t structure becomes more complicated, the behaviour is the same: Undefined !
typedef struct Main_SimpleStructData_s
{
unsigned char a;
unsigned int c; // Add a little bit complication
unsigned char b;
}
Main_SimpleStructData_t;
The result becomes:
simpleDataStruct:......0x22FE20 // Undefined behaviour also !
Addr simpleDataStruct: 0x22FE40
Size simpleDataStruct: 12
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>.