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>.
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 would like to copy 4 bytes from unsigned int to unsigned char array. Once executed the following function get_result goes to segmentation fault :
int exec_cmd(unsigned int * apu32Var)
{
int ret = -1;
char cmd[100] = { 0 };
char resp[100] = { 0 };
sprintf(cmd, "%s %s", "/home/send_frames.sh", "read");
ret = exec_cmd_ret_result(cmd, resp);
if( apu32Var != NULL )
{
*apu32Var = (((unsigned int)resp[0]) <<24)+(((unsigned int)resp[1]) <<16)+(((unsigned int)resp[2]) <<8)+(unsigned int)resp[3];
}
return ret;
}
int get_result(unsigned char * buffer, unsigned short * size)
{
unsigned int u32Var = 0;
exec_cmd(&u32Var);
memcpy(buffer, &u32Var, sizeof(unsigned int));
*size += sizeof(unsigned int);
return 0;
}
int main(int argc, char **argv)
{
unsigned char *buf;
unsigned short *size;
get_result(buf+4, size);
return 0;
}
However, regarding to memcpy() man page it seems memcpy() are well managed. What is going wrong ?
Assuming your call to test_result actually should be calling get_result, then you have two big problems.
The first and most serious is that you pass in uninitialized local variables as arguments to the function. Uninitialized local variables have indeterminate values. For a pointer, it means it can point just about anywhere, and trying to dereference it will lead to undefined behavior. You need to actually make these pointers point somewhere valid for it to work. This goes for both variables.
The second problem is that you misunderstand how emulating pass by reference works in C. Yes the function should take a pointer, but you should not actually create a pointer variable and pass to the function. Instead you should use the address-of operator & on a non-pointer variable.
To solve both problems, your code should look something like
unsigned char buf[256] = { 0 }; // Arbitrary size, all initialized to zero
unsigned short size = 0; // To make sure it's properly initialized
get_result(buf + 4, &size); // Note use of & to pass a pointer to the variable size
Note that it works using an array, since arrays naturally decays to pointers to its first element.
buf in main is never initialized, so it points to some random location in memory. This is undefined behavior and a perfect recipe for a segfault.
Similarly, *size is read from when you use +=, but the value was never initialized in main, so your dereference an undefined value.
You should declare buf as an array of sufficient size and pass that in. Also, declare size as an int, initialize it to 0, and pass its address:
int main(int argc, char **argv)
{
unsigned char buf[100];
unsigned short size = 0;
// I'm assuming this was a typo and you ment to call get_result instead of test_result
get_result(buf, &size);
return 0;
}
learning C and now i study simple code snipper that show byte representation of primitive values:
typedef unsigned char *byte_pointer;
void show_bytes(byte_pointer start, int len) {
int i;
for (i = 0; i < len; i++)
printf(" %.2x", start[i]);
printf("\n");
}
void show_float(float x) {
show_bytes((byte_pointer) &x, sizeof(float));
}
void show_int(int x) {
show_bytes((byte_pointer) &x, sizeof(int));
}
void show_pointer(void *x) {
show_bytes((byte_pointer) &x, sizeof(void *));
}
If i understand correct, &x (an ampersand character) showing address of memory (equal to *x).
So. program routine is showing hexadecimal values of each data type, with int value of bytes like (sizeof(int)).
Im not really understand how its work. First, we typedef pointer of unsigned char, and then use it with other types. What is the meaning of (byte_pointer) &x and why does it work, when we define byte_pointer as value of type unsigned char? I understand that we get address of memory that contain value, but i don't know how exactly it work and WHY it work with char pointer. Could you explain that part?
Thanks.
The code simply takes the address of a random chunk of data and prints the contents byte by byte. The code takes the address of whatever you pass to it, then converts it to a pointer-to-byte (unsigned char). Any pointer type in C can be converted to another pointer type, although in some cases doing so is dangerous practice. In the case of char, it is safe though, you are guaranteed to get a pointer to the lowest addressed byte of the object.
Note that hiding a pointer behind a typedef is bad and dangerous practice. Just forget about that typedef, it adds nothing of value. A better way to write the same code would be:
void show_bytes (const uint8_t* start, int len)
or alternatively
void show_bytes (const void* s, int len)
{
const uint8_t* start = s;
...
byte_pointer is defined to be a pointer to an unsigned char; this is so show_bytes can print out each individual byte (in hexadecimal) of what the address passed to show_bytes points to.
I would have declared start to be a void*, and then cast it inside of show_bytes, making it a) clearer that show_bytes doesn't care what type of thing start points to, and b) avoids the cast in every call.
I have a "generic" linked link in C that takes void * data to store the data in a Node.
insertNode(linkedList * list, void *data);
//Storing/retrieving a string works fine;
char *str="test";
insertNode(list, str);
char *getback=(char *)node->data;
//Storing/retrieving an Int results a cast warning
int num=1;
insertNode(list,(void *)num);
int getback=(int)node->data;
This is because int is 32 bit, but void * is 64 bit on x64 machine. What is the best practice to get rid of this error?
Use intptr_t or uintptr_t. They are integers of the same size as a pointer:
#include <stdint.h>
...
intptr_t num = 1;
insertNode(list, (void *) num);
intptr_t getback = (intptr_t) node->data;
Of course, the maximum value that you can store depends on the system, but you can examine it at compile time via INTPTR_MIN and INTPTR_MAX.
Using a void * member to store an integer value isn't very good practice - apart from anything else, there's no guarantee what range of integers are storable in this way. If you want to store either a pointer or an integer in your structure, then what you should be using is a union:
union data {
void *p;
int i;
};
insertNode(linkedList * list, union data data);
// Storing/retrieving a string:
char *str="test";
union data d;
d.p = str;
insertNode(list, d);
char *getback = node->data.p;
// Storing/retrieving an int:
int num = 1;
union data d;
d.i = num;
insertNode(list, d);
int getback = node->data.i;
If you have a compiler that supports C99 constructs, you can make it a little neater by avoiding the local union variable:
// Insert a string
insertNode(list, (union data){ .p = str});
// Insert an int
insertNode(list, (union data){ .i = num});
You can cast it twice: insertNode( list, (void *)(uint64_t) num);
Or you can use "unsigned long" for num instead of int. At least in my experience, sizeof(unsigned long) == sizeof(void *) on both 32 and 64-bit systems. Using uintptr_t may be more correct; it's been a while since I've read the standards.
Are you sure you're doing what you actually want to be doing? In the first example with the string you are passing the pointer to the string literal to insertNode, while in the second example ((void *)num) you are casting the value of the int to a pointer to void. Did you actually want to do this: insertNode(list,(void *)&num);, i.e., passing the reference to the data like you were doing in the first example?
Also as an aside, char *str="test"; makes str point to a string literal, which is read-only. So you really should be doing const char *str="test";. If you wanted a normal (writable) string then char str[] = "test";.
Apologies if you already knew these things and actually wanted to store an int in a void *, I just can't imagine why you would.
Edit: If you actually want generic storage space then a union would be a safe approach. There is absolutely no guarantee that an int will fit in a void * in standard C, even though in most implementations it probably does.
Here is the full code of it
#include <stdio.h>
#include <string.h>
void reverse_string(unsigned short *buf, int length)
{
int i;
unsigned short temp;
for (i = 0; i < length / 2; i++)
{
temp = buf[i];
buf[i] = buf[length - i - 1];
buf[length - i - 1] = temp;
}
}
int main(int argc, char **argv)
{
unsigned short* tmp = (unsigned short*)argv[1];
reverse_string(tmp,strlen(argv[1]) / 2);
printf("%s",argv[1]);
return 0;
}
As you can see, in main, we have
unsigned short* tmp = (unsigned short*)argv[1];
Arent pointers supposed to point "to the address of" of a variable? The one above isn't(using the ampersand). Yet the program works as intended.
Why is it like that?
And what does this part mean?
(unsigned short*)argv[1]
argv is a pointer-to-an-array-of-pointers:
argv[0][0] (a char)
argv[0] (a char*)
argv (a char**)
unsigned char* tmp = (unsigned char*)argv[1];
...works, because you're referencing the the second "string" in that set.
Note that in this case, "char" and "unsigned short" might be roughly equivolent depending on the compiler and platform, but it is probably not a good idea to assume that. For example, if you compiled to enable a "unicode" command line, then you might get "short" instead of "char" forwarded to you from the command line. But, that may be a dangerous assumption, as "these days" a "short" is usually 16-bits and a "char" is usually 8-bits.
Addressing the original questions:
argv is an array of pointers, each of which point to a character array. argv[1] is a pointer to the character array with the first argument (i.e. if you run ./program arg1 arg2, the pointer argv[1] points to the string arg1).
The ampersand is used to denote a reference, which is for most purposes the same as a pointer. It is syntactic sugar to make it easy to pass a reference to a variable that you have already declared. The common example is using scanf.
int x = 1;
scanf(..., &x, ...)
is equivalent to
int x = 1;
int *p = &x;
scanf(..., p, ...)
The program itself is designed to flip endianness. It's not sufficient to go character-by-character because you have to flip two bytes at a time (ie short-by-short), which is why it works using shorts.
(unsigned short*)argv[1] instructs the compiler to treat the address as if it were an array of shorts. To give an example:
unsigned char *c = (unsigned char *)argv[1];
c[1]; /*this points to the address one byte after argv*/
unsigned short *s = (unsigned short *)argv[1];
s[1]; /*this points to the address two bytes after argv */
Take a look at a primer on type casting.