【问题标题】:Having an array that points to multiple LUT with different sizes in C在 C 中有一个指向多个不同大小的 LUT 的数组
【发布时间】:2021-11-30 00:58:16
【问题描述】:

我正在做一个程序,该程序使用两个堆栈对一个数字堆栈进行排序,用于学校项目,我想通过蛮力方法处理堆栈大小低于或等于 5 的情况,为此我有不同的结构,其中包含查找表,其中包含表示数字顺序的 int 数组数组(对于每个可能的顺序),以及包含相应操作序列的函数指针数组数组以对它们进行排序,并且每个结构对应于特定的数组大小(2、3、4、5)。并且为了避免一个巨大的if 森林,我想使用带有指向不同数据的指针的公共结构 (t_combi) 的 while 循环来访问它们,但显然它不起作用,我不知何故失去了我的全局数组的地址,因为当我比较订单时,我在第一次迭代时遇到了段错误(尝试使用 valgrind 访问未分配的数据)。

代码:

//  brute_force.h

#ifndef BRUTE_FORCE_H
# define BRUTE_FORCE_H

# include "resolver.h"

# define CNT_2 2
# define CNT_3 6
# define CNT_4 24
# define CNT_5 120

typedef struct s_mlx    *t_mlxptr;
typedef const char      *(*t_op)(t_stack *, t_stack *);

typedef struct  s_combi {
    int     cnt;
    int     **order;
    t_op    **op;
}   t_combi;

static const struct {
    int     cnt;
    int     order[CNT_2][2];
    t_op    op[CNT_2][2];
}   g_2combi = {
        .cnt = CNT_2,
        .order = {
            {0, 1},
            {1, 0}
        },
        .op = {
            {NULL},
            {sa, NULL}
        }
};

static const struct {
    int     cnt;
    int     order[CNT_3][3];
    t_op    op[CNT_3][3];
}   g_3combi = {
        .cnt = CNT_3,
        .order = {
            {0, 1, 2},
            {0, 2, 1},
            {1, 0, 2},
            ...
        },
        .op = {
            {NULL},
            {rra, sa, NULL},
            {sa, NULL},
            ...
        }
};

static const struct {
    int     cnt;
    int     order[CNT_4][4];
    t_op    op[CNT_4][7];
}   g_4combi = {
    .cnt = CNT_4,
    .order = {
        {0, 1, 2, 3},
        {0, 1, 3, 2},
        {0, 2, 1, 3},
        ...
    },
    .op = {
        {NULL},
        {pb, rra, sa, pa, NULL},
        {ra, sa, rra, NULL},
        ...
    }
};

static const struct {
    int     cnt;
    int     order[120][5];
    t_op    op[120][10];
}   g_5combi = {
    .cnt = CNT_5,
    .order = {
        {0, 1, 2, 3, 4},
        {0, 1, 2, 4, 3},
        {0, 1, 3, 2, 4},
        ...
    },
    .op = {
        {NULL},
        {rra, rra, sa, ra, ra, NULL},
        {pb, pb, sa, pa, pa, NULL},
        {pb, pb, rra, pa, pa, NULL},
        ...
    }
};

int brute_force(t_mlxptr mlx, t_stack *st[2], t_queue *instr);

#endif

// brute_force.c

#include <brute_force.h>
#include <resolver.h>

bool    same_order(const int *a, const int *b, size_t size)
{
    size_t  i = 0;

    while (++i < size)
    {
        if ((a[i - 1] < a[i] && b[i - 1] > b[i])
        || (a[i - 1] > a[i] && b[i - 1] < b[i]))
            return (false);
    }
    return (true);
}

void    apply_instructions(t_mlxptr mlx, t_stack *st[2], t_op *op, t_queue *instr)
{
    while (*op)
    {
        add_op(mlx, *op, st, instr); // function from resolver.h
        ++op;
    }
}

void    init_combi(t_combi combi[4])
{
    combi[0] = (t_combi) {
        .cnt = g_2combi.cnt,
        .order = (int**)g_2combi.order,
        .op = (t_op **)g_2combi.op
    };
    combi[1] = (t_combi) {
        .cnt = g_3combi.cnt,
        .order = (int**)g_3combi.order,
        .op = (t_op **)g_3combi.op
    };
    combi[2] = (t_combi) {
        .cnt = g_4combi.cnt,
        .order = (int**)g_4combi.order,
        .op = (t_op **)g_4combi.op
    };
    combi[3] = (t_combi) {
        .cnt = g_5combi.cnt,
        .order = (int**)g_5combi.order,
        .op = (t_op **)g_5combi.op
    };
}


int brute_force(t_mlxptr mlx, t_stack *st[2], t_queue *instr)
{
    const int *const    a_raw = stkcbegin(st[ST_A]); // pointer to a int array
    const size_t        size = stksize(st[ST_A]); // nbcount
    int                 i;

    t_combi             combi[4];
    
    init_combi(combi);
    if (stksorted(st[ST_A]))
        return (0);
    if (size > 5)
        return (1);
    i = -1;
    while (++i < combi[size - 2].cnt)
    {
        if (same_order(combi[size - 2].order[i], a_raw, size))
            apply_instructions(mlx, st, combi[size - 2].op[i], instr);
    }
    return (0);
}

【问题讨论】:

  • 指针和数组是不同的东西,有不同的布局。 init_combi 中的类型转换掩盖了错误。
  • 例如,g_combi2.order 的类型 int [CNT_2][2] 衰减为 int (*)[2],但您将其转换为 int **int ** 是“指向int 的指针”,但数组对象g_combi2.order 不包含指针。
  • 另外,作为一种风格,在头文件中声明静态变量是个坏主意,除非您真的希望每个 .c 文件(包括头文件)都有自己的变量本地副本.

标签: c pointers segmentation-fault lookup-tables


【解决方案1】:

主要问题是您将指向某种类型的数组的指针转换为指向某种类型的指针的指针。例如,在这段代码中:

void    init_combi(t_combi combi[4])
{
    combi[0] = (t_combi) {
        .cnt = g_2combi.cnt,
        .order = (int**)g_2combi.order,
        .op = (t_op **)g_2combi.op
    };
    /* ... */
}

g_2combi.orderint 的二维数组:int [CNT_2][2]。在初始化器中,值被转换为指向其第一个元素的指针。元素的类型是int [2]int的数组长度2),所以指向元素的指针的类型是int (*)[2]int的数组长度2的指针)。但是,类型转换操作将其转换为指向不兼容类型int ** 的指针(指向int 的指针的指针)。 combi[0].order[0] 应该是 int * 类型,它与底层对象的类型不兼容:int [2]

同样,g_2combi.opt_op 的二维数组:t_op [CNT_2][2]。在初始化器中,该值被转换为指向其第一个类型为t_op [2]t_op 的数组长度为2)的元素的指针,因此该指针的类型为t_op (*)[2](指向t_op 的数组长度为2 的指针) .类型转换操作将指针转换为t_op **(指向t_op 的指针)。 combi[0].op[0] 应该是 t_op * 类型,它与底层对象的类型不兼容:t_op [2]

解决问题的一种方法是将所有变量g_2combig_3combi 等定义为同一类型t_combi。保持一切不变,compound literals 可用于初始化 g_2combi.order 等中的指针。例如:

typedef struct  s_combi {
    int     cnt;
    const int * const *order;
    const t_op * const *op;
} t_combi;

static const t_combi g_2combi = {
    .cnt = CNT_2,
    .order = (const int * const []){
        (const int []){0, 1},
        (const int []){1, 0}
    },
    .op = (const t_op * const []){
        (const t_op []){NULL},
        (const t_op []){sa, NULL}
    }
};

/* define g_3combi etc. in a similar way to the above. */

void    init_combi(t_combi combi[4])
{
    combi[0] = g_2combi;
    combi[1] = g_3combi;
    combi[2] = g_4combi;
    combi[3] = g_5combi;
}

(由于上面添加了constness,apply_instruction 函数的op 参数需要从t_op *op 更改为const t_op *op。)

【讨论】:

  • 谢谢!我有点想做这样的事情,但我不知道该怎么做
  • @Fayeure 复合文字对于这类事情非常有用。没有它们也可以做同样的事情,但是指向复合文字数组的指针需要替换为指向在当前对象之外初始化的命名数组的指针。复合文字基本上是匿名的初始化对象。
【解决方案2】:

一个大问题是您将指针隐藏在 typedef 后面。这样做的目的只有一个:混淆程序员和其他阅读代码的人。

例如 t_op **op; 没有 typedef 的扩展创建一个类型的函数指针

typedef const char      *(***t_op)(t_stack *, t_stack *);

正如您所希望的那样,这简直是疯了。您需要摆脱这些类型定义和不必要的多重间接层,然后才能执行其他任何操作。

函数指针的合理typedef 可能看起来像这样(它实际上是函数的 typedef):

typedef char* t_op (t_stack*, t_stack*);

然后您将其用作t_op* op

总体而言,您的程序方式太复杂了,对于听起来像是相当微不足道的任务而言。另一个例子,任何地方都不应该有任何演员表——所有这些都是为了隐藏错误。

【讨论】:

  • 总的来说,看看“KISS原则”。还要记住 C 大师 Brian Kernighan 的这句话:“每个人都知道,调试的难度是一开始就编写程序的两倍。所以,如果你在编写程序时尽可能聪明,你将如何调试它?”
  • 好吧,这是个问题,但这不是主要问题,如果我不能将它转换为指针,我怎么会有指向不同大小数组的指针
  • @Fayeure 有几种方法。将它们包装在结构中,使用 malloc 或使用指向 VLA 的指针等。但坦率地说,代码是不可挽救的,你应该从需求开始:这段代码应该做什么?然后研究实现它的最佳方法。因为这里的许多额外复杂性源于您发现了一些问题,但使用了过于复杂的功能来解决它​​。
猜你喜欢
  • 2010-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多