Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting from unsigned into signed char in C

I am converting an input raw pcm stream into mp3 using lame. The encoding function within that library returns mp3 encoded samples in an array of type unsigned char. This mp3-encoded stream now needs to be placed within an flv container which uses a function that writes encoded samples in char array. My problem is that I am passing the array from lame (of type unsigned char) into the flv library. The following piece of code (only symbolic) illustrates my problem:

/* cast from unsigned char to char. */

#include <stdio.h>
#include <stdlib.h>

void display(char *buff, int len) {

  int i = 0;
  for(i = 0; i < len; i++) {
    printf("buff[%d] = %c\n", i, buff[i]);
  }
}

int main() {

  int len = 10;
  unsigned char* buff = (unsigned char*) malloc(len * sizeof(unsigned char));
  int i = 0;

  for(i = 65; i < (len + 65); i++) {
    buff[i] = (unsigned char) i;
    printf("char = %c", (char) i);
  }

  printf("Displaying array in main.\n");
  for(i = 0; i < len; i++) {
    printf("buff[%d] = %u\n", i, 'buff[i]');
  }

  printf("Displaying array in func.\n");
  display(buff, len);

  return 0;
}

My question(s):
1. Is the implicit type conversion in the code below (as demonstrated by passing of buff into function display safe? Is some weird behaviour likely to occur?
2. Given that I have little option but to stick to the functions as they are present, is there a "safe" way of converting an array of unsigned chars into chars?

like image 858
Sriram Avatar asked Dec 27 '22 09:12

Sriram


2 Answers

The only problem with converting unsigned char * into char * (or vice versa) is that it's supposed to be an error. Fix it with a cast.

display((char *) buff, len);

Note: This cast is unnecessary:

printf("char = %c", (char) i);

This is fine:

printf("char = %c", i);

The %c formatter takes an int arg to begin with, since it is impossible to pass a char to printf() anyway (it will always get converted to int, or in an extremely unlikely case, unsigned int.)

like image 139
Dietrich Epp Avatar answered Dec 28 '22 23:12

Dietrich Epp


You seem to worry a lot about type safety where there is no need for it. Since this is C and not C++, there is no strong typing system in place. Conversions from unsigned char to char are usually harmless, as long as the "sign bit" is never set. The key to avoiding problems is to actually understand them. The following problems/features exist in C:

  • The default char type has implementation-defined signedness. One should never make any assumptions of its signedness, nor use it in arithmetic of any kind, particularly not bit-wise operations. char should only be used for storing/printing ASCII letters. It should never be mixed with hex literals or there is a potential for subtle bugs.
  • The integer promotions in C implicitly promote all small integer types, among them char and unsigned char, to an integer type that can hold their result. This will in practice always be int.
  • Formally, pointer conversions between different types could be undefined behavior. But pointer conversions between unsigned char and char are in practice safe.
  • Character literals '\0' etc are of type int in C.
  • printf and similar functions default promote all character parameters to int.

You are also casting the void* result of malloc, which is completely pointless in C, and potentially harmful in older versions of the C standard that translated functions to "default int" if no function prototype was visible.

And then you have various weird logic-related bugs and bad practice, which I have fixed but won't comment in detail. Use this modified code:

#include <stdio.h>
#include <stdlib.h>

void display(const char *buff, int len) {

  for(int i = 0; i < len; i++) {
    printf("buff[%d] = %c\n", i, buff[i]);
  }
}

int main() {

  int len = 10;
  unsigned char* buff = malloc(len * sizeof(unsigned char));

  if(buff == NULL)
  {
    // error handling
  }

  char ch = 'A';
  for(int i=0; i<len; i++)
  {
    buff[i] = (unsigned char)ch + i;
    printf("char = %c\n", buff[i]); 
  }


  printf("\nDisplaying array in main.\n");
  for(int i = 0; i < len; i++) {
    printf("buff[%d] = %u\n", i, buff[i]);
  }

  printf("\nDisplaying array in func.\n");
  display((char*)buff, len);

  free(buff);

  return 0;
}
like image 37
Lundin Avatar answered Dec 28 '22 23:12

Lundin