在C语言中将无符号数转换为有符号char类型

5
我正在使用lame将输入的raw pcm流转换为mp3。该库中的编码函数以unsigned char类型的数组返回mp3编码样本。现在需要将这个mp3编码流放入一个flv容器中,该容器使用一个将编码样本写入char数组的函数。我的问题是,我正在将来自lame的数组(类型为unsigned char)传递到flv库中。以下代码片段(仅为符号)说明了我的问题:
/* 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;
}

我的问题如下:
1. 在下面的代码中(通过将buff传递给函数display来演示),隐式类型转换是否安全?是否有可能发生一些奇怪的行为?
2. 鉴于我别无选择,只能使用现有的函数,有没有一种“安全”的方法将unsigned char数组转换为char

3个回答

7
unsigned char * 转换成 char *(或反之亦然)的唯一问题是,这本应是一个错误。 通过强制转换来修复它。
display((char *) buff, len);

注意:这个转换是不必要的:

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

这是好的:

printf("char = %c", i);
<代码>%c格式化器需要首先接受一个<代码>int参数,因为无论如何都不可能将<代码>char传递给<代码>printf()(它总是会被转换为<代码>int,或在极少数情况下,转换为<代码>unsigned int)。

4
你似乎过于担心没有必要关注类型安全性。由于这是C语言而不是C++,因此没有强类型系统可用。从unsigned char到char的转换通常是无害的,只要“符号位”永远不被设置。避免问题的关键在于真正理解它们。以下问题/特性存在于C语言中:
- 默认char类型具有实现定义的有符号性。人们不应该对其符号做出任何假设,也不应该在任何运算中使用它,特别是不能进行位运算。char只应用于存储/打印ASCII字母。它不应与十六进制文字混合使用,否则可能会导致微妙的错误。 - C语言中的整数提升会隐式地将所有小整数类型(包括char和unsigned char)提升为可以容纳其结果的整数类型。在实践中,这总是int类型。 - 形式上,在不同类型之间的指针转换可能是未定义行为。但是,在unsigned char和char之间进行的指针转换在实践中是安全的。 - 字符文字'\0'等在C语言中属于int类型。 - printf等函数默认将所有字符参数升级为int类型。
你还对malloc的void*结果进行了类型转换,这在C语言中完全没有意义,并且在旧版本的C标准中可能是有害的,因为如果没有函数原型,则将函数转换为“默认int”。
然后你还有各种奇怪的逻辑漏洞和不良实践,我已经修复了它们,但不会详细评论。使用这个修改后的代码:
#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;
}

位操作中没有任何的解释,尤其是没有符号位。例如,在toupper中使用c&0xdf有什么问题? - Rainald62
哦,我明白了。这是一个“仅仅理论上”的问题。 - Rainald62

1

C/C++的任何整数类型转换到同样或更大的整数类型都保证不会产生数据丢失。有符号和无符号字段之间的转换通常会创建溢出和下溢危险,但你要转换的缓冲区实际上指向的是类型为void*的原始数据。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接