PAYATHON 2026

LibX 中如何将 char * 十六进制颜色转换为 int

技术老齐

结论

0xRRGGBB 本身就是一个唯一的 24 位整数,按十六进制位权解析即可:

color = R × 256² + G × 256 + B
      = (R << 16) | (G << 8) | B

逐字符解析时,只需执行:

value = value * 16 + 当前十六进制位的值;

无须分别计算三个分量后再做普通求和,也用不到 pow()、int_pow() 或 int_abs()。

例如:

0x12AB34 = (0x12 << 16) | (0xAB << 8) | 0x34

结果仍是整数 0x12AB34,十进制值为 1223476。其中红、绿、蓝三个分量依次为 18、171、52。

原函数为什么会出错

错误来自字符串解析时的越界读取。

传入的字符表是:

0123456789abcdef

代码却执行了:

hexa++;
hexa++;

执行后,hexa 指向:

23456789abcdef

此时查找范围中已经没有字符 0 和 1。

循环还会处理颜色字符串开头的 0x:

0xffffff
^^

程序查找字符 x 或已经被跳过的 0、1 时,下面的循环无法找到匹配项:

while (color[i] != hexa[j])
    j++;

j 随后会越过字符串末尾并继续读取无效内存,造成未定义行为。部分颜色碰巧能得到正确结果,并不能说明这套转换逻辑可靠。

推荐的转换函数

下面的实现只接受 0xRRGGBB 格式,同时兼容大小写十六进制字母。函数用返回值表示解析是否成功,这样可以区分错误输入和合法颜色。

static int hex_digit_value(char c)
{
    if (c >= '0' && c <= '9')
        return (c - '0');
    if (c >= 'a' && c <= 'f')
        return (c - 'a' + 10);
    if (c >= 'A' && c <= 'F')
        return (c - 'A' + 10);
    return (-1);
}

int parse_color(const char *color, int *result)
{
    unsigned int value;
    int digit;
    int i;

    if (!color || !result)
        return (0);
    if (color[0] != '0' || (color[1] != 'x' && color[1] != 'X'))
        return (0);

    value = 0;
    i = 0;
    while (i < 6)
    {
        digit = hex_digit_value(color[i + 2]);
        if (digit < 0)
            return (0);
        value = value * 16u + (unsigned int)digit;
        i++;
    }
    if (color[8] != '\0')
        return (0);

    *result = (int)value;
    return (1);
}

调用方式如下:

int color;

if (!parse_color("0x12ab34", &color))
{
    /* 处理无效颜色 */
    color = 0xffffff;
}

中间计算使用 unsigned int,更符合这里的位运算含义。对于常见的 32 位 int,最大颜色值 0xFFFFFF 等于 16777215,仍在正数范围内。

如果项目只需返回颜色,并约定解析失败时返回白色,可以再加一层简单包装:

int hexint(const char *color)
{
    int result;

    if (!parse_color(color, &result))
        return (0xffffff);
    return (result);
}

不过,用 0xffffff 表示错误后,“无效输入”和“合法白色”会得到相同的返回值。需要区分两者时,应直接检查 parse_color() 的返回值。

为什么不同颜色不会得到相同整数

RGB 的三个分量分别占用整数中的不同字节:

位 23..........16  15...........8  7............0
       Red              Green             Blue

对应的 C 表达式为:

int color = (red << 16) | (green << 8) | blue;

也可以写成:

int color = red * 65536 + green * 256 + blue;

只要 red、green 和 blue 都在 0 到 255 之间,每种 RGB 组合都会对应唯一的整数。例如:

红色:0xFF0000 = 16711680
绿色:0x00FF00 =    65280
蓝色:0x0000FF =      255
白色:0xFFFFFF = 16777215

也可以从整数中还原三个分量:

int red;
int green;
int blue;

red = (color >> 16) & 0xff;
green = (color >> 8) & 0xff;
blue = color & 0xff;

写入 MiniLibX 图像时的注意事项

在常见的 MiniLibX Linux 环境中,颜色通常采用 0x00RRGGBB 格式,解析结果可以直接传给像素写入函数。

现有函数每次都会写入一个 unsigned int,通常占 4 字节:

*(unsigned int *)dst = color;

因此,图像的 bpp 必须是 32。地址计算虽然使用了 img->bpp / 8,实际写入的字节数却不会随 bpp 改变。至少要先确认这个条件:

void pixel_put(t_data *img, int x, int y, int color)
{
    char *dst;

    if (img->bpp != 32)
        return;
    dst = img->addr + y * img->line + x * (img->bpp / 8);
    *(unsigned int *)dst = (unsigned int)color;
}

还要确保 x、y 没有超出图像范围,否则程序会写到图像缓冲区之外。

如果转换后的整数正确,屏幕上显示的颜色却不对,应检查 mlx_get_data_addr() 返回的 bpp 和 endian。图像格式和端序都可能影响颜色在内存中的字节顺序,不能只看整数的书写形式。对于常见的 32 位 MiniLibX Linux 图像,0x00RRGGBB 通常与上面的写法匹配;其他环境应以实际返回的图像信息为准。

备注:内容仅供参考。