strtol产生了意料之外的输出

3
我正在尝试创建一个小程序,它接受一个物理内存地址并打印存储在该位置的数据。我向程序传递两个参数-地址和我想要打印的内存大小(以字节为单位)。
问题出在当我传入的地址值超过一定值时,strtol()函数就会输出一个无意义的值。下面是代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <asm-generic/fcntl.h>
#include <unistd.h>

int main(int argc, char** argv)
{
    unsigned int mem_address,mem_size;
    int loop, i;
    int *ptr, *mem_address_current;

    printf("mem_addr: %s\n",argv[1]);
    printf("mem_size: %s\n",argv[2]);

    mem_address = strtol(argv[1], NULL, 16);
    mem_size = strtol(argv[2], NULL, 16);

    printf("mem_addr: %x\n",mem_address);
    printf("mem_size: %x\n",mem_size);

    int mem_dev = open("/dev/mem", O_RDWR);

    if(mem_dev == -1)
    {
        printf("No worky\n");
        exit(1);
    }

    int alloc_mem_size, page_mask, page_size;
    void *mem_pointer, *virt_addr;

    page_size = sysconf(_SC_PAGESIZE);
    alloc_mem_size = (((mem_size / page_size) + 1) * page_size);
    page_mask = (page_size - 1);

    mem_pointer = mmap(NULL,
                   alloc_mem_size,
                   PROT_READ | PROT_WRITE,
                   MAP_SHARED,
                   mem_dev,
                   (mem_address & ~page_mask)
                   );


    if(mem_pointer == MAP_FAILED)
    {  
        printf("no_worky2\n");
        exit(1) ;
    }

    virt_addr = (mem_pointer + (mem_address & page_mask)); 
    ptr = mem_pointer;
    loop = (mem_size/16) + 1;
    for(i = 0;i < loop;i++) {
        printf("%#x: %08x %08x %08x %08x\n", ptr, *ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3));
        ptr = ptr + 4;
    }

    return 0;


}

如果运行以下命令,将获得期望的值。
root@p1025:~# ./test_prog_ppc 0100000 16                                                                                              
mem_addr: 0100000
mem_size: 16
mem_addr: 100000
mem_size: 16
0x48001000: 38210020 4e800020 9421ffe0 7c0802a6
0x48001010: 2c050000 bf410008 7c7e1b78 90010024

然而,如果我更改输入地址,该值会与预期值不同:
root@p1025:~# ./test_prog_ppc ffee0000 16
mem_addr: ffee0000
mem_size: 16
mem_addr: 7fffffff
mem_size: 16

有什么想法是为什么会发生这种情况吗?

感谢帮助。


4
strtol() 函数的名称暗示它返回一个(带符号的)long 整数,但是你却将其存储在一个无符号整型变量中。这只会导致溢出。你可能需要使用 strtoul() 函数,并将结果存储在一个无符号长整型变量中。你还应该传递一个非空的第二个参数,以便能够检查错误。 - Crowman
3
检查 errno,它应该是 ERANGE。 - user1641854
附注:最好使用 printf("%p: ...\n", ptr, ...)printf("%#lx: ...\n", (unsigned long) ptr, ...) - chux - Reinstate Monica
啊,就是这样!感谢您的帮助和建议。 - Gerardo
2个回答

5

strtol()返回一个(有符号的)long。根据您的输入,您正在尝试解析无符号32位数字,因此如果您的系统上的long为32位,则strtol()将超出范围。在这种情况下,它将返回LONG_MAX,在您的系统上似乎是0x7FFFFFFF

您应该改用strtoul(),并将结果存储在unsigned long中,而不是像现在一样使用unsigned int,因为虽然long保证至少为32位,但int则不是。

此外,您真的应该将真正指针的地址作为strtol()和友元的第二个参数,并使用它来检查错误。现在,如果argv[1]argv[2]中的任何一个没有以数字开头,您将无法区分输入是否有误,还是输入是合法的零。


3
这种行为是有文档记录的,查看strtol返回值
...
如果正确的值超出了可表示值的范围,则应根据值的符号返回{LONG_MIN}、{LONG_MAX}、{LLONG_MIN}或{LLONG_MAX},并将errno设置为[ERANGE]。
您的返回值是LONG_MAX,当您测试errno时,它应该是ERANGE
您可以使用其中一个strtoulstrtollstrtoull,这些函数都能处理更大的值。虽然strtoul仅限于unsigned long,因此,如果您在64位系统上,则应使用其中一个strto*ll函数。

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