有时候struct里面的数组声明失败了

时间:2018-02-19 23:25:53

标签: c arrays struct segmentation-fault

在某些Linux / gcc环境中,以下代码失败的原因是什么?我声明了一个结构,然后创建了这个结构的“实例”。问题在于数组,它应该包含单独的字符串并且可以调整大小。详细说明:

typedef struct {
    char (*pointer)[256];
    int used;
    int size;
} Array;

Array *createArray(int start_size) {
    Array *array = malloc(sizeof( *array));
    array->used = 0;
    array->size = start_size;
    array->pointer = malloc(start_size * sizeof *array->pointer); // <--- here
    return array;
}

使用未初始化值的行标有<--以下(strcpy)。 使用parse函数append函数append到数组。

void append(Array *array, char* elem) {
    if (array->used >= array->size) {
        // expand table
        array->pointer = realloc(array->pointer, array->size * 2 * sizeof *array->pointer);
        array->size *= 2;
    }
    // append new arg
    strcpy(array->pointer[array->used] , elem); // <-- here
    array->used++;
}

Array *parse(char* command) {
    // split command by space and store each arg in array
    char *args_str;
    Array *args_list = createArray(DEFAULT_SIZE); // expandable array holding arguments
    args_str = strtok(command, " ");
    append(args_list, args_str);
    args_str = strtok(NULL, " ");
}

Valgrind表示,由于使用了未初始化的值,上面用<--- here标记的行上有一个错误,但代码在Valgrind内部运行良好,并且还在:

CentOS Linux release 7.4.1708 (Core) 3.10.0-693.11.6.el7.x86_64, gcc version 4.8.5 20150623 (Red Hat 4.8.5-16)
Ubuntu 16.04.3 LTS 4.4.0-112-generic, gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.6)

但失败了:

Linux kali 4.13.0-kali1-amd64, gcc version 7.2.0 (Debian 7.2.0-14)

在第一次输入程序后立即抛出SEGFAULT 11,在编译过程之后,它会在每个环境中编译好。

2 个答案:

答案 0 :(得分:2)

根据Pablo评论,需要根据parse修正realloc逻辑和改进!如上所述,请在NULLmalloc返回时检查realloc

这是一个MCVE,可以进一步改进:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define DEFAULT_SIZE 256

typedef struct {
    char (*pointer)[256];
    int used;
    int size;
} Array;

Array *createArray(int start_size) {
    Array *array = malloc(sizeof(*array));
    array->used = 0;
    array->size = start_size;
    array->pointer = malloc(start_size * sizeof * array->pointer); // 
    return array;
}

void append(Array *array, char* elem) {
    if (array->used >= array->size) {
        // expand table
        array->pointer = realloc(array->pointer, array->size * 2 * sizeof * array->pointer);
        array->size *= 2;
    }
    // append new arg
    strcpy(array->pointer[array->used] , elem); // <-- here
    array->used++;
}


Array *parse(char* command) {
    // split command by space and store each arg in array
    char *args_str;
    Array *args_list = createArray(DEFAULT_SIZE); // expandable array holding arguments

    args_str = strtok(command, " ");

    append(args_list, args_str);

    args_str = strtok(NULL, " ");

    append(args_list, args_str);

    return(args_list);
}

int main(void)
{
    char str[] = { '1', '2', '3', ' ', '5', '6', '7', ' ', 0 };

    Array *p = parse(str);

    printf("%s\n",p->pointer[0]);
    printf("%s\n",p->pointer[1]);

    return 0;
}

输出:

123
567

答案 1 :(得分:2)

我将从评论中引用自己:

  

在附加中,您应该声明char (*tmp)[256];并像这样调用realloc:tmp = realloc(array->pointer, ...);

     

然后,您检查是否tmp == NULL并处理错误案例,如果它不是NULLarray->pointer = tmp;。   如果源字符串elem长度超过255个字符,也可能会溢出指针,   这将导致未定义的行为。使用

strncpy(array->pointer[array->used] , elem, sizeof *array->pointer[array->used]);
array->pointer[array->used][sizeof(array->pointer[array->used]) - 1] = 0;
     

代替。

您的append功能存在一个小问题,它不会返回 任何东西。我修改为:

Array *parse(char* command) {
    // split command by space and store each arg in array
    char *args_str;
    Array *args_list = createArray(DEFAULT_SIZE); // expandable array holding arguments

    args_str = strtok(command, " ");

    do {
        append(args_list, args_str);
    } while(args_str = strtok(NULL, " "));


    return args_list;
}

我接受了您的代码并修复了parse函数并从main调用了它 功能。

int main(void)
{
    Array *array;
    char cmd[] = "ls -alh --color /tmp";

    array = parse(cmd);

    if(array == NULL)
        return 0;

    printf("Array->used: %d, array->size: %d\n", array->used, array->size);
    for(int i = 0; i < array->used; ++i)
        puts(array->pointer[i]);

    free(array->pointer);
    free(array);
}

我得到的输出是:

==11553== Memcheck, a memory error detector
==11553== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==11553== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==11553== Command: ./a
==11553== 
Array->used: 4, array->size: 10
ls
-alh
--color
/tmp
==11553== 
==11553== HEAP SUMMARY:
==11553==     in use at exit: 0 bytes in 0 blocks
==11553==   total heap usage: 3 allocs, 3 frees, 3,600 bytes allocated
==11553== 
==11553== All heap blocks were freed -- no leaks are possible
==11553== 
==11553== For counts of detected and suppressed errors, rerun with: -v
==11553== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

代码工作正常,问题可能发生,因为未定义 调用parse之前的行为。我已将测试代码放在此处:https://ideone.com/9VYyYH

正如您在ideone上看到的那样,代码也会产生正确的结果。