I am trying to understand number representation in C.
I am working on a code segment which looks like the one below.
#include <stdio.h>
#include <string.h>
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_int(int x) {
show_bytes((byte_pointer) &x, sizeof(int));
}
void show_unsigned(short x) {
show_bytes((byte_pointer) &x, sizeof(unsigned));
}
int main(int argc,char*argv[])
{
int length=0;
unsigned g=(unsigned)length;// i aslo tried with unsigned g=0 and the bytes are the same
show_unsigned(g);
show_int(length);
printf("%d",g);//this prints 0
return 0;
}
Here, show_unsigned() and show_int() prints the byte representations of the variables specified as arguments.For int length the byte representation is all zeroes as expected, but for unsigned g, the byte representation is 00 00 04 08.But when I print g with a %d, I get 0(so i suppose the numeric value is interpreted as 0 )
Please could somebody explain how this is happening.
In:
void show_unsigned(short x) {
show_bytes((byte_pointer) &x, sizeof(unsigned));
}
You declared the argument short x which is smaller than int x so you ignored some of the 00 and your print function is displaying adjacent garbage.
You're reading sizeof(unsigned) bytes in a short. short isn't guaranteed to be the same size as unsigned, hence, when reading the bytes next to your short, garbage data is read.
To fix this, either pass your argument as an unsigned, or when using sizeof, use sizeof(short).
what you are doing doesn't make any sense, particularly with the type conversions that you have occurring. Someone else already pointed out my point about the conversion to short
Rather than writing an absurd number of functions try doing this
void show_bytes( void *start, unsigned int len ) {
unsigned char* ptr = (unsigned char *) start;
unsigned int i = 0;
for ( i = 0; i < len; ++i, ++ptr ) {
printf( " %.2x", ptr[0] );
}
}
Instead of calling as you had been just call it like:
show_bytes( (void *)&x, sizeof(x));
And if thats too much typing make a macro out of that. now it works for any type you come up with.
Related
I am currently working on a task where I need to print the address of a variable. It would be easy to use printf %p but I am only allowed to use write from unistd.
I tried casting the pointer in to an unsigned integer and uintptr_t and then converting it into a hexadecimal number. With uintptr_t it works but with an unsigned integer it only prints half of the address. Maybe someone can explain me why this is the case?
I also saw some solutions using ">>" and "<<" but I didn't get why that works. It would be nice if someone can explain a solution using "<<" and ">>" step by step, because I am not sure if I am allowed to use uintptr_t.
this is the code I use to cast it into a unsigned int / unitptr_t / unsigned long long (I know that ft_rec_hex is missing leading 0's):
void ft_rec_hex(unsigned long long nbr)
{
char tmp;
if (nbr != 0)
{
ft_rec_hex(nbr / 16);
if (nbr % 16 < 10)
tmp = nbr % 16 + '0';
else
tmp = (nbr % 16) - 10 + 'a';
write(1, &tmp, 1);
}
}
int main(void)
{
char c = 'd';
unsigned long long ui = (unsigned long long)&c;
ft_rec_hex(ui);
}
It looks like only half of the address is printed because the "unsigned integer" you used has only half size of uintptr_t. (note that uintptr_t is an unsigned integer type)
You can use an array of unsigned char to store data in a pointer variable and print that to print full pointer withput uintptr_t.
Using character types to read objects with other type is allowed according to strict aliasing rule.
#include <stdio.h>
#include <unistd.h>
void printOne(unsigned char v) {
const char* chars = "0123456789ABCDEF";
char data[2];
data[0] = chars[(v >> 4) & 0xf];
data[1] = chars[v & 0xf];
write(1, data, 2);
}
int main(void) {
int a;
int* p = &a;
/* to make sure the value is correct */
printf("p = %p\n", (void*)p);
fflush(stdout);
unsigned char ptrData[sizeof(int*)];
for(size_t i = 0; i < sizeof(int*); i++) {
ptrData[i] = ((unsigned char*)&p)[i];
}
/* print in reversed order, assuming little endian */
for (size_t i = sizeof(int*); i > 0; i--) {
printOne(ptrData[i - 1]);
}
return 0;
}
Or read data in a pointer variable as unsigned char array without copying:
#include <stdio.h>
#include <unistd.h>
void printOne(unsigned char v) {
const char* chars = "0123456789ABCDEF";
char data[2];
data[0] = chars[(v >> 4) & 0xf];
data[1] = chars[v & 0xf];
write(1, data, 2);
}
int main(void) {
int a;
int* p = &a;
/* to make sure the value is correct */
printf("p = %p\n", (void*)p);
fflush(stdout);
/* print in reversed order, assuming little endian */
for (size_t i = sizeof(int*); i > 0; i--) {
printOne(((unsigned char*)&p)[i - 1]);
}
return 0;
}
It would be easy to use printf %p but I am only allowed to use write from unistd.
Then form a string and print that.
int n = snprintf(NULL, 0, "%p", (void *) p);
char buf[n+1];
snprintf(buf, sizeof buf, "%p", (void *) p);
write(1, buf, n);
Using a pointer converted to an integer marginally reduces portability and does not certainly form the best textual representation of the pointer - something implementation dependent.
With uintptr_t it works but with an unsigned integer it only prints half of the address.
unsigned is not specified to be wide enough to contain all the information in a pointer.
uintptr_t, when available (very common), can preserve most of that information for void pointers. Good enough to round-trip to an equivalent pointer, even if in another form.
I was wondering if you could help me overcome a hurdle I've run into with my C syntax. I have written the function:
binary_and_trim(char *password, unsigned *key1, unsigned *key2)
that has achieved the goal of converting a provided string into binary and trimmed off the leading zero. I have assigned my key1 and key2 pointers to the correct indexes. But then, when I return to the main function the values are all lost.
I believe that the problem is that when I pass the *key1/*key2 pointers to the function it only receives a copy of them. But, as I am new to C, I don't know how to fix it?
I created a for loop to help me test/debug.
#include <stdio.h>
#include <string.h>
void binary_and_trim(char *password, unsigned *key1, unsigned *key2);
unsigned int get_n_bits(unsigned *bits, int width, int index);
int main(int argc, const char * argv[]) {
unsigned *key1 = NULL;
unsigned *key2 = NULL;
binary_and_trim("password", key1, key2);
//This test fails with a EXC_BAD_ACCESS error
for(int i = 0 ; i < 28; i++){
printf("key1[%d] %u key2[%d] %d\n", i, key1[i], i, (key2 + i));
}
}
void binary_and_trim(char *password, unsigned *key1, unsigned *key2){
char c;
int count = 0;
unsigned tmp;
unsigned long len = strlen(password);
unsigned trimmedbinary[len * 7];
for(int i = 0; i < len; i++){
c = *(password + i);
for( int j = 6; j >= 0; j--) {
tmp = 0;
if(c >> j & 1){
tmp = 1;
}
*(trimmedbinary + count) = tmp;
count++;
}
}
key1 = trimmedbinary;
key2 = &trimmedbinary[28];
//This test works correctly!!!
for(int i = 0 ; i < 28; i++){
printf("key1[%d] %d key2[%d] %d\n", i, *(key1 + i), i, *(key2 + i));
}
}
I believe that the problem is that when I pass the *key1/*key2 pointers to the function it only receives a copy of them.
Yes, exactly. Pointers are just integers and integers get copied. You solve this with a pointer to a pointer, a "double pointer".
However, there is another problem. trimmedbinary is using stack/automatic memory. "Automatic" meaning it will be freed once the function exits. Once the function returns key1 and key2 will point at freed memory. trimmedbinary must be declared in heap/dynamic memory with malloc.
void binary_and_trim(char *password, unsigned int **key1, unsigned int **key2){
unsigned int *trimmedbinary = malloc(len * 7 * sizeof(unsigned int));
...
*key1 = trimmedbinary;
*key2 = &trimmedbinary[28];
for(int i = 0 ; i < 28; i++) {
printf("key1[%d] %u, key2[%d] %u\n", i, (*key1)[i], i, (*key2)[i]);
}
return;
}
And call it as binary_and_trim("password", &key1, &key2);
Update: I answered the question about how to alter the pointer value, but I have not noticed the memory issue in the code. Please refer to this answer instead.
Pointers are variables themselves. You may already know that with a pointer, you can change the value stored in the variable the pointer points to. Therefore, you need to use a pointer to a pointer to change the value (the memory address) stored in the pointer.
Change your function signature to:
void binary_and_trim(char *password, unsigned **key1, unsigned **key2)
Call with:
binary_and_trim("password", &key1, &key2);
and replace key1 and key2 to *key1 and *key2 in the function definition.
Your problem is that the variable you use to fill with your keys data trimmedbinary is allocated only for the scope of the function binary_and_trim. That said, when you print inside the function
void binary_and_trim(char *password, unsigned **key1, unsigned **key2){
...
unsigned trimmedbinary[len * 7]; // <--
...
*key1 = trimmedbinary; // <--
*key2 = &trimmedbinary[28]; // <--
//This test works correctly!!!
for(int i = 0 ; i < 28; i++){
printf("key1[%d] %d key2[%d] %d\n", i, *(key1 + i), i, *(key2 + i));
}
}
it just works because the data your key1 pointer is trying to access is still there.
However, when you return from your function back to main, key1 and key2 still point back to the buffer you initialized inside binary_and_trim, which is no longer valid because is out of scope.
I suggest you create a buffer in main and pass it as a parameter,
int main(int argc, const char * argv[]) {
const char* password = "password";
unsigned long len = strlen(password);
unsigned buffer[len * 7]; // <-- Add buffer here
unsigned *key1 = NULL;
unsigned *key2 = NULL;
binary_and_trim(password, &key1, &key2, &buffer, len * 7);
//This test succeeds
for(int i = 0 ; i < 28; i++){
printf("key1[%d] %u key2[%d] %d\n", i, key1[i], i, (key2 + i));
}
}
void binary_and_trim(char *password, unsigned **key1, unsigned **key2, unsigned** buffer, size_t buff_size){
char c;
int count = 0;
unsigned tmp;
...
//Use *buffer instead of trimmedbinary
//Check if buff_size matches len(password) * 7
or alternatively, make the buffer heap allocated (dont forget to free() later).
I believe that the problem is that when I pass the *key1/*key2
pointers to the function it only receives a copy of them.
Already altered in code as well.
Wow! Thank you EVERYONE! I finally got it up and running (after 4 hours of beating my head against the wall). I can't begin to say how clutch you all are.
I'm realizing I have tons to learn about the granular memory access of C (I'm used to Java). I can't wait to be an actual WIZARD like you all!
I'm studying section 2.1 from Computer Systems A Programmer's Perspective and I understand what the code is doing but I don't understand why there are parenthesis around the formal parameter "byte_pointer" followed by &x?
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 *));
}
You are typecasting a float pointer to an unsigned char*. And then you will access each of the float variable's bytes. As pointer arithmetic is dictated by the type of thing it points to - the increment or dcrement on it will be directbed by sizeof(unsigned char) or 1 byte. You will access the float byte by byte. By casting we are basically saying that you need to consider this as unsigned char* even if it the float* that we are passing. There is a change in type information - and we are making the compiler aware that we are aware of it by providing the casting.
So I'm starting to understand the basics of generic programming in C. I'm currently building a program that says if a value occurs or not in a given sequence of number.
I think that the bug occurs in the cmpValues function. Would anyone point it out? (for example, for want=4 and v={1,2,3,4,5}, the program says that want is not in v)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void *search(const void *x, const void *t, int n, int d, int (*cmpValues)(const void *, const void *)){
char *p = (char *)t;
int i;
for(i=0;i<n;++i)
if(cmpValues(x,p+i*d))
return p+i*d;
return NULL;
}
int cmpValues(const void *a, const void *b){
if((char *)a == (char *)b)
return 1;
return 0;
}
int main() {
FILE *f = fopen("datein.txt", "r");
FILE *g = fopen("dateout.txt", "w");
int *v, n, i, want;
fscanf(f, "%d", &n);
v = (int *)malloc(n * sizeof(int));
for(i = 0; i < n; ++i)
fscanf(f, "%d", v + i);
fscanf(f, "%d", &want);
if(search(&want, v, n, sizeof(int), cmpValues))
fprintf(g, "The value %d is found at position %d.\n\n", want, search(&want, v, n, sizeof(int), cmpValues));
else
fprintf(g, "The value does bot occur in the given sequence.\n\n");
return 0;
}
In cmpValues, you are comparing 2 objects pointed by 2 void pointers (i.e. you don't know their type, nor their size). Let's assume we are having ints, and that an int has 4 bytes, which is usually the case.
Just for the sake of it, let's assume that the a pointer has value 0x100 (i.e. points to a int from 0x100 to 0x103, inclusive) and b pointer has a value of 0x104 (i.e. points to the int from 0x104 to 0x107).
Now, you are converting them to char* (char has 1 byte) and compare the value of the pointers. Now, the type of the pointer does not matter in comparisons. In that comparison, you will compare memory addresses (in my example, 0x100 and 0x104). Obviously, the only way the function will return 1 is if the pointers would point to the same variable.
Now, in order to fix it, you should compare the values at the memory addresses pointed by your pointers. However, simply dereferencing the pointers:
*((char *)a) == *((char *)b)
won't be enough, since this would compare just the first byte of a with the first byte of b (under the assumption that char has 1 byte). Also, you can't dereference void*.
So, you need to iterate over your variables and compare them byte by byte (this assumes that you know the size of the data type):
int comp(void *a, void *b, int size) {
// convert a and b to char* (1 byte data type)
char *ca = a;
char *cb = b;
// iterate over size bytes and try to find a difference
for (int i = 0; i < size; i++) {
if (*(ca + i) != *(cb + j)) {
return 0;
}
}
// if no difference has been found, the elements are equal
return 1;
}
side note: you don't need to call cauta twice in main.
I personally use a function show_bytes as follows:
#include<stdio.h>
typedef char *byte_pointer;
void show_bytes (byte_pointer x)
{
int length = sizeof(float);
int i;
for(i = 0;i <length;i++)
{
printf("%2x",*(x+i));
printf("\n");
}
}
int main()
{
float obj;
printf("please input the value of obj:");
scanf("%f",&obj);
show_bytes((byte_pointer) &obj);
}
when i input 120.45,which should be 0x42f0e666
please input the value of obj:120.45
66
ffffffe6
fffffff0
42
why so many 'f' before the e6 and f0 while i use %.2x.
Your function should be:
void show_bytes (byte_pointer x)
{
int i;
for(i = 0; i <sizeof(float); i++)
{
printf("0x%2X\n", (unsigned int)(*(x++) & 0xFF));
}
}
or
typedef uint8_t *byte_pointer;
void show_bytes (byte_pointer x)
{
int i;
for(i = 0; i <sizeof(float); i++)
{
printf("0x%2X\n", *(x++));
}
}
In your code the problem is that the pointer type is signed an is promoted to signed int by printf.
%2X format does not limit the output digit, tells only to printf that the result string must be at least 2 characters long.
First solution: the value is promoted to signed int but the passed
value is truncated to the LSB.
Second example: value is truncated by the type of pointer, that
is unsigned char.
The rule is: to raw access memory, always use unsigned types.