代码之家  ›  专栏  ›  技术社区  ›  ci7i2en4

由于无效的赋值导致的错误?

c
  •  -1
  • ci7i2en4  · 技术社区  · 6 年前

    我在公共结构中声明了一个数组,如下所示:

    uint16_t *registers;
    

    在一个函数中,我正在检索一个字符字符串(存储在缓冲区中,请参阅下面的代码),其中包含用逗号分隔的数值(例如,“1,12,0136,5,761243”)。我的目标是得到每个单独的数值,并将其一个接一个地存储在数组中。

    i = 0;
    const char delimiter[] = ",";
    char *end;
    
    tmp.vals = strtok(buffer, delimiter);
    while (tmp.vals != NULL) {
        tmp.registers[i] = strtol(tmp.vals, &end, 10);
        tmp.vals = strtok(NULL, delimiter);
        i++;
    }
    

    问题是,包含strtol的行产生了一个分段错误(核心转储)错误。我很确定这是由于试图将无符号长值放入uint16\t数组槽中造成的,但不管我怎么做,都无法修复它。


    unsigned long num = 0;
    size_t size = 0;
    i = 0;
    
    size = 1;
    tmp.vals = (char *)calloc(strlen(buffer) + 1, sizeof(char));
    tmp.registers = (uint16_t *)calloc(size, sizeof(uint16_t));
    tmp.vals = strtok(buffer, delimiter);
    while (tmp.vals != NULL) {
        num = strtoul(tmp.vals, &end, 10);
        if (0 <= num && num < 65536) {
            tmp.registers = (uint16_t *)realloc(tmp.registers, size + i);
            tmp.registers[i] = (uint16_t)num;
        } else {
            fprintf(stderr, "==> %lu is too large to fit in register[%d]\n", num, i);
        }
        tmp.vals = strtok(NULL, delimiter);
        i++;
    }
    
    1 回复  |  直到 6 年前
        1
  •  2
  •   Schwern    6 年前

    A long integer is at least 32 bits -Wall -Wshadow -Wwrite-strings -Wextra -Wconversion -std=c99 -pedantic )它应该告诉你。

    test.c:20:28: warning: implicit conversion loses integer precision: 'long' to 'uint16_t'
          (aka 'unsigned short') [-Wconversion]
            tmp.registers[i] = strtol(tmp.vals, &end, 10);
                             ~ ^~~~~~~~~~~~~~~~~~~~~~~~~~
    

    但是,这不会导致故障。你将失去16位,标志的变化会做一些有趣的事情。

    #include <stdio.h>
    #include <inttypes.h>
    
    int main() {
        long big = 1234567;
        uint16_t small = big;
        printf("big = %ld, small = %" PRIu16 "\n", big, small);
    }
    

    知道 strtoul 读一本书 未签名 long ,验证它是否足够小以适合,并显式强制转换它。

        unsigned long num = strtoul(tmp.vals, &end, 10);
        if( 0 <= num && num < 65536 ) {
            tmp.registers[i] = (uint16_t)num;
        }
        else {
            fprintf(stderr, "%lu is too large to fit in the register\n", num);
        }
    

    更有可能 tmp.registers (可能的话) buffer )没有正确初始化并将点分配给垃圾。如果你只是宣布 tmp 在堆栈上这样:

    Registers tmp;
    

    这只分配内存给 tmp公司 ,而不是它所指的东西。而且里面会有垃圾。 tmp.寄存器 会指向记忆中的某个随机点。当你试着给它写信时,它会出错。。。最终。

    需要分配寄存器数组。

    size_t how_many = 10;
    uint16_t *registers = malloc( sizeof(uint16_t) * how_many );
    Thing tmp = {
        .registers = registers,
        .vals = NULL
    };
    

    how_many 次。但在阅读输入时,你不能确定这一点。您的循环可能正在读取无限多个寄存器。如果它超过了我们分配的10,它将再次开始写入其他人的内存和segfault。

    registers 它到底有多远。我们可以在循环中完成,但它确实属于结构。

    typedef struct {
        uint16_t *registers;
        char *vals;
        size_t max;
        size_t size;
    } Registers;
    

    当我们在做的时候,把初始化放到一个函数中,这样我们就可以确保每次都能可靠地完成。

    void Registers_init( Registers *registers, size_t size ) {
        registers->registers = malloc( sizeof(uint16_t) * size );
        registers->max = size;
        registers->size = 0;
    }
    

    和我们的边界检查一样。

    void Registers_push( Registers *registers, uint16_t num ) {
        if( registers->size == registers->max ) {
            fprintf(stderr, "Register has reached its limit of %zu\n", registers->max);
            exit(1);
        }
        registers->registers[ registers->size ] = (uint16_t)num;
        registers->size++;
    }
    

    现在我们可以安全地添加寄存器了。或者至少它会很好地出错。

    Registers registers;
    Registers_init( &registers, 10 );
    
    tmp.vals = strtok(buffer, delimiter);
    while (tmp.vals != NULL) {
        unsigned long num = strtoul(tmp.vals, &end, 10);
        if( 0 <= num && num < 65536 ) {
            Registers_push( &tmp, (uint16_t)num );
        }
        else {
            fprintf(stderr, "%lu is too large to fit in the register\n", num);
        } 
        tmp.vals = strtok(NULL, delimiter);
        i++;
    }
    

    此时,我们正在实现一个大小绑定数组。这是一个很好的练习,但是对于生产代码来说 use an existing library such as GLib 它提供了自增长阵列和更多功能。