如何在C语言中将十六进制数读入无符号整型变量?

4
我想从文本文件中读取十六进制数字到无符号整数中,以便我可以执行机器指令。这只是一个模拟类型的东西,它查看文本文件并根据值及其相应的指令输出寄存器中的新值。
例如,指令可能是:
1RXY->将寄存器R保存为内存地址XY中的值 2RXY->将寄存器R保存为值XY BRXY->如果xy是这样和那样等等,则跳转到寄存器R ARXY->将寄存器R与内存地址XY处的值进行AND运算
文本文件包含类似以下内容的每个内容都在新行中。(十六进制)
120F B007 290B
我的问题是将每个单独的指令复制到无符号整数中...我该如何做到这一点?
#include <stdio.h>
int main(){
    FILE *f;
    unsigned int num[80];

    f=fopen("values.txt","r");
    if (f==NULL){
        printf("file doesnt exist?!");
    }

    int i=0;
    while (fscanf(f,"%x",num[i]) != EOF){
        fscanf(f,"%x",num[i]);
        i++;
    }
    fclose(f);
    printf("%x",num[0]);
}

那是CHIP8吗? - LiraNuna
5个回答

7
你已经朝着正确的方向前进了。以下是我看到的问题:
  • 如果fopen()返回NULL,你需要退出 - 你正在打印错误消息,然后继续执行。
  • 你的循环应该在i >= 80时终止,这样你就不会读取超过空间大小的整数。
  • 你需要传递num[i]的地址而不是值给fscanf
  • 你在循环中调用了两次fscanf(),这意味着你浪费了一半的值而没有存储它们。
修复以上问题后,代码如下:
#include <stdio.h>

int main() {
    FILE *f;
    unsigned int num[80];
    int i=0;
    int rv;
    int num_values;

    f=fopen("values.txt","r");
    if (f==NULL){
        printf("file doesnt exist?!\n");
        return 1;
    }

    while (i < 80) {
        rv = fscanf(f, "%x", &num[i]);

        if (rv != 1)
            break;

        i++;
    }
    fclose(f);
    num_values = i;

    if (i >= 80)
    {
        printf("Warning: Stopped reading input due to input too long.\n");
    }
    else if (rv != EOF)
    {
        printf("Warning: Stopped reading input due to bad value.\n");
    }
    else
    {
        printf("Reached end of input.\n");
    }

    printf("Successfully read %d values:\n", num_values);
    for (i = 0; i < num_values; i++)
    {
        printf("\t%x\n", num[i]);
    }

    return 0
}

+1 我会将“rv”声明为循环中的本地变量,但这并不是什么大问题。 - Chris Lutz
1
在循环外部使用rv可以让你区分匹配失败和读取失败/EOF。 - caf

5

您还可以使用函数strtol()。如果您使用16进制,它将把您的十六进制字符串值转换为int/long。

errno = 0;
my_int = strtol(my_str, NULL, 16);
/* check errno */

编辑:另外需要注意的是,一些静态分析工具可能会标记像atoi()和scanf()这样的函数为不安全。因为atoi()无法像strtol()那样检查错误,所以已经过时了。而scanf()则可能会出现缓冲区溢出的问题,因为它没有检查传递给它的类型。例如,你可以将一个short型指针传递给scanf(),但读取的值实际上是一个long型...然后就会崩溃。


这是因为如果 strtol 成功,它不会改变 errno,你需要检查这种情况以区分一个 ERANGE 错误和一个转换成功但结果为 LONG_MAXLONG_MIN 的情况。 - caf
如果您计划在调用函数后立即检查errno,则最好在函数调用之前将其设置为零。在调用之前,errno的值可能已经因其他原因而更改。 - John
从strtol手册中: errno = 0; /调用后区分成功/失败/ val = strtol(str,&endptr,base);/检查各种可能的错误/ - KFro
sscanf和相关函数的代价非常高。在callgrind的调查中,它给了我们81.44的代价,而使用strtol只有0.12。因此,我们按照@KFro的建议从scanf切换到了strtol。 - Gonzalo Aguilar Delgado

2
在这种情况下,您的所有输入都是大写十六进制,而您正在尝试读取小写十六进制。
要解决此问题,请将“%x”更改为“%X”。

2
或者您可以使用 strtol(),它可以任意控制您想要的进制,并且可能会稍微快一些,因为它不必解析格式字符串。并不是因为我天生倾向于避开 scanf()printf(),只是因为需要解析格式字符串而产生的轻微开销... - Chris Lutz
Chris:你说得对,是我的错!我忘记了为什么会这样想...已经修改过了。 - LiraNuna
1
这完全是错误的 - %x%X 格式说明符是完全相同的 - caf

2

你正在将两个数字读入数组的每个元素中(因此你会覆盖其中一半的数字)。尝试只使用

while (i < 80 && fscanf(f,"%x",&num[i]) != EOF)
    i++;

对于你的循环

编辑

你还缺少'&'来获取数组元素的地址,所以你正在向scanf传递一个随机垃圾指针,可能会导致崩溃。-Wall选项是你的朋友。


0
你想把每一行(每4个字符为一组)分别放在4个不同的数组元素中吗?如果是的话,我会尝试这样做:
/* read the line */
/* fgets(buf, ...) */

/* check it's correct, mind the '\n' */
/* ... strlen ... isxdigit ... */

/* put each separate input digit in a separate array member */
num[i++] = convert_xdigit_to_int(buf[j++]);

函数convert_xdigit_to_int()的作用是将字符'0'转换为整数0,'1'转换为1,'2'转换为2......'9'转换为9,'a'或'A'转换为10等。

当然,这段伪代码位于一个循环内,循环执行直到文件被读取完毕或数组被填满。也许可以将fgets()作为while(...)的条件。

while(/*there is space in the array && */ fgets(...)) {
}

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