views:

135

answers:

3

Char is 1 byte unsigned short is 2 bytes

So if I cast a char * to unsigned short *, will it change the length of the buffer?

For example I am passing char * and length to a VUMeter function. The function casts the char * to unsigned short *:

short* pln = (short*) buffer;`

Now I loop through the buffer, so can I use same length which was passed?

int peak=0;
short* pln = (short*) buffer;
for (int i=0; i<len; i++)
{
  if(abs(pln[i]) > peak)
     peak = abs(pln[i]);
}  

For some reason I am getting application faults within the loop.

+1  A: 

If the size of the elements doubles, then you have room for half the elements. Casting a pointer does not resize it, is basically assumes you know what you are doing with the pointer.

So no, you can't really cast a char * to a short * and expect things to work. If you need a short * with the same values as in the char *, you need to allocate a short array and copy the elements individually from the char *.

Arve
A: 

unsigned short may or may not be 2 bytes. Let's look at the memory for your example.

+---+---+---+     +---+---+---+
|   |   |   | ... |   |   |   |
+---+---+---+     +---+---+---+

|<-------- len bytes -------->|

If unsigned short is 2 bytes long, you have space worth of len/2 unsigned short values. Or, more generally, you have space for len/n unsigned short values, where n is equal to sizeof(unsigned short).

You can't cast an unsigned char * to unsigned char * and expect things to work portably. Now, to calculate peak, it depends upon what you are trying to do. If you want to find the maximum of len unsigned char values, and save that into peak, looping over the values will work:

size_t i;
unsigned short peak = 0;
for (i=0; i < len; ++i) {
    if (buffer[i] > peak) {
        peak = buffer[i];
    }
}

If, however, you want to "combine" sizeof(unsigned short) values into one unsigned short value, then your best bet would be to calculate the numbers by hand.

Assuming that len is divisible by n, and big-endian storage, you can do something like this (untested):

#include <stdio.h>
#include <limits.h> 

size_t factor = sizeof(unsigned short);
size_t n = len / factor;
size_t i;
unsigned short peak = 0;
if (len % factor != 0) {
    fprintf(stderr, "Extra data at the end\n");
}   
for (i=0; i < n; ++i) {
    size_t j;
    unsigned short test = 0;
    for (j=0; j < factor; ++j) {
        test = (test << CHAR_BIT)  + buffer[i*factor+j];
    }
    if (test > peak) {
        peak = test;
    }
}
Alok
A: 

The memory that's allocated will not change until you call *alloc(). The fact that the size of the data type is of different size will only influence the "step size" ("x+1" will point to x + 2 bytes), but you'll have space for half as many elements.

Personally, I'd avoid simply casting a pointer. It'll probably make the data already present in the memory block misread (two separate bytes will be now read as a single value, completely different from what you had in mind when filling the buffer with 1-byte values: two subsequent values 127, 127 will become a single value: 65535, that sort of thing). And i think it will also raise a warning in the compiler (at least GCC complains about such a cast). I think what you should do is this:

int x = SOMEVAL;
char * cptr = malloc(x*sizeof(char));
/* some code here - fill cptr with data */
uint16 * sptr = malloc(x*sizeof(uint16));
int i;
for (i = 0; i < x; i++) {
    *(sptr+i) = (uint16)(*(cptr+i));
}
free(cptr);

Unless my quickly made up code has some bug in it (I didn't bother to check it), it should do what you want to achieve: "cast" a pointer from char* to uint16*, but also safely copy the data without altering values (unless there's a negative value among the chars, ofc).

mingos