【问题标题】:Strange issues regarding unsigned char * in C关于 C 中 unsigned char * 的奇怪问题
【发布时间】:2012-11-18 05:21:36
【问题描述】:

所以我有一个方法可以返回一个无符号字符 *

unsigned char* someMethod(num)
unsigned short num;
{
    //do some stuff with num and change values of a
    unsigned char * a = (unsigned char*) malloc(4);
    printf("a0 is %x\n",a[0]);
    printf("a1 is %x\n",a[1]);
    printf("a2 is %x\n",a[2]);
    printf("a3 is %x\n",a[3]);
    return a;
}

当我调用 someMethod(128) 时:

unsigned char* s = someMethod(128);
printf("s0 is %x\n",s[0]);
printf("s1 is %x\n",s[1]);
printf("s2 is %x\n",s[2]);
printf("s3 is %x\n",s[3]);

它会打印出来

a0 is 30
a1 is 1
a2 is 31
a3 is 30
s0 is 30
s1 is 14
s2 is ffffff9d
s3 is 0

这对我来说毫无意义,因为我分配了 s = someMethod(128)。 a 和 s 不应该具有相同的值吗?!? 任何帮助表示赞赏。谢谢!

【问题讨论】:

  • K&R 风格的函数定义?这段代码有多少年了?
  • 我怀疑你没有#included stdlib.h,所以假设返回类型是int。除此之外,%xunsigned char 的错误格式(尽管在这种情况下,它可能会起作用)。
  • 你能重现所发布的函数的问题吗? (函数看起来很简化,可能漏掉了实际问题)
  • 如果您的问题是您缺少malloc() 的原型,正如 Daniel Fischer 所建议的那样,那就让这成为有关转换 malloc() 的结果的教训(在 C 中,您不应该这样做)。
  • @gl3829:更好:unsigned char *a = malloc(4 * sizeof *a);。强制转换是不必要的(void* 结果被隐式转换为适当的类型)并且在某些情况下可以掩盖错误。使用sizeof *a 而不是sizeof (unsigned char) 使代码更易于维护;如果a 的类型发生变化,您不需要更改两件事。

标签: c unsigned-char


【解决方案1】:

参考@gl3829 的评论,我会选择

unsigned char *a = malloc(4 * sizeof(*a))

使大小“自动”正确。

更重要的是,我认为一个问题是,在someMethod 中,您在实际分配任何内容之前打印出分配数组中的值。这会调用未定义的行为并允许产生任何结果。在打印出来之前尝试存储一些东西。

对于以十六进制打印unsigned chars,正确的格式说明符是%hhx。使用错误的说明符也可能引发未定义的行为。

【讨论】:

  • 这段代码的行为不是未定义的。根据 C 2011 7.22.3.4 2,malloc 返回一个具有不确定值的对象。而unsigned char 很特别。根据 6.2.6.2 1,它具有二进制表示(因此没有陷阱表示)并且没有填充位。将使用 malloc 分配的 unsigned char 传递给带有适当说明符的 printf 必须打印一些值。
  • 说明符没有问题。传递给printfunsigned char 在传递之前转换为int,使用%x 打印它与使用%hhx 打印它产生相同的结果。
  • 有趣。我从here 中获得了关于初始化的想法,特别是“取消引用尚未明确初始化的指针”——我猜malloc 做了一些特别的事情。至于printf,使用错误的说明符确实会调用未定义的行为,例如here,但不清楚是否需要长度说明符hh
  • 当然,“不清楚”是指“不清楚对我而言”。我会很高兴得到启发。
  • “取消引用尚未明确初始化的指针”表示char *p; *p;——尚未初始化的是p,因此评估*p 会尝试使用p。在这种情况下,指针a 被初始化为malloc 的值;是a内容没有被初始化。
【解决方案2】:

当您在%x 中打印时,printf() 函数会读取int。在 32 位计算机中,int 的长度为 4 个字节。因此,第二个、第三个和第四个printf() 正在读取malloc() 区域的外部和内部。您不能期望 malloc 之外的区域始终保持不变。

解决方案是,填充它。 Malloc 比您当前想要的多几个字节,可能是sizeof(int)


解决方案 2:先将 s[i] 类型转换为 int,然后再将其传递给 printf()

【讨论】:

  • 传递给printf的表达式是a[0]a[1]等等。由于a 是指向unsigned char 的指针,因此这些是unsigned char 对象,并且它们在分配的数组中。它们在传递给printf之前被转换为int,并且数组没有溢出。
  • 通常,函数参数会在输入函数之前将自身转换为预期的类型。但是,printf() 函数无法在其函数声明中指定它将接收哪些类型的变量,因为这些参数类型取决于格式字符串,即参数本身。
  • 对于使用省略号声明的函数,例如 printf,默认参数提升在相应的参数上执行,根据 C 2011 6.5.2.2 7. 默认参数提升包括整数提升,根据6.5.2.2 6,整数提升将 unsigned char 转换为 int 每 6.3.1.1 2 (除了,在某些不正当的 C 实现中,unsigned char 可以提升为 unsigned int)。
  • 无论如何,a 的地址并没有传递给printf,所以printf 无法定位a 中的任何内容或超出空间。 printf 只接收传递给它的值,而不接收这些值的地址。
猜你喜欢
  • 2017-03-13
  • 2011-07-04
  • 2016-08-02
  • 2016-04-04
  • 1970-01-01
  • 2019-08-28
  • 2021-08-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多