修复分段错误

时间:2013-06-12 04:37:07

标签: c string casting

我遇到了分段错误,我已经缩小到回调函数中的for循环。这很奇怪,因为该程序以前工作,现在不是!

struct debuggerth_command debuggerth_protocol[] = { /*
    * Note: These strings are NOT null-terminated. The
    * strings are 4 bytes long for memory alignment and
    * integer-cast comparisons.
    */

    { "run ", debuggerth_startprocess },
    { "stop", 0 },
    { "inp ", 0 },
    { "sig ", 0 },
    { 0, 0 }
};

这是代码:

int debuggerth_callback (struct libwebsocket_context * context,
                    struct libwebsocket * wsi,
                    enum libwebsocket_callback_reasons reason,
                    void * user,
                    void * in,
                    size_t len){

switch (reason) {

case LWS_CALLBACK_RECEIVE:

    if (len < 4){
        /* send error */
        return -1;
    }

    /* Getting a segmentation fault
     * within this loop.
     */

    // I used this break to determine where the seg fault starts
    // break

    int i = 0;
    for (; debuggerth_protocol[i].cmd; i++)
        if (cmpcmd (debuggerth_protocol[i].cmd, in)) break;


    //break;

    if (!debuggerth_protocol[i].cmd){
        int byteswritten = sprintf
            (debuggerth_message,
             debuggerth_format,
             debuggerth_headers[0],
             debuggerth_errors [0]);

         libwebsocket_write (wsi, debuggerth_message,
                                      byteswritten,
                                      LWS_WRITE_TEXT);
         return -1;
    }

    break;

这是字符串比较宏:

#define cmpcmd(cmd, str) ((*(int*)(cmd)) == (*(int*)(str)))

有人有什么想法吗?

3 个答案:

答案 0 :(得分:3)

一个想法:依赖于你的字符串正好是int的大小这一事实是非常可怕的。

人们经常尝试做一些聪明的事情,只是在基础假设发生变化时被严重咬伤,例如转移到int类型为8个字节的平台。

我放弃了那个宏并重写它以使用strcmpstrncmp (a)


还有其他一些事情要做。

首先,在尝试使用它们之前打印出(或使用调试器检查)所有变量。可能是in为NULL。

或许您尝试调用stopsig之类的NULL命令,或者即使您获得的命令不在您的表中,并且在i相等时您也会盲目地调用它到4。这些特殊的可能性在代码中没有显示,循环之后,所以它是纯粹的,尽管我想在我看来受过教育,猜测。


另一种可能性是,您运行的是不允许未对齐访问的体系结构。某些体系结构针对特定边界上的访问进行了优化(例如从32位对齐的地址获取32位值),如果违反该对齐,则运行速度会慢一些。

然而,某些架构根本不允许未对齐的访问,而是在尝试时给出类似BUS错误的内容。

由于您现在已在评论中指出您正在使用ARM,因此几乎可以肯定。有关更多信息,请参阅here

如果是这种情况,甚至更多理由摆脱棘手的宏并使用更传统的解决方案。


(a):您可能还想在某些时候调查术语“严格别名”,因为这在技术上可能是未定义的行为。

答案 1 :(得分:0)

鉴于这是在ARM上运行,我认为你的问题是它正在进行未对齐的内存访问,这将失败或者非常慢。这不完全是一个错误。 See this question for example,根据建议,-Wcast-align可能会将其标记为有风险。您可以打开软件解决方法,但这可能比在代码中修复它更慢。

一种选择是使用memcmp gcc可以编译成几乎与单词读取一样简单的东西,如果它是对齐的。

另一个选择,如果性能至关重要,则将循环展开为由命令的第一个字节切换的case语句。然后只需检查以下字符是否符合预期。

答案 2 :(得分:0)

我查看了我的代码的一些更改,正如@Jonothan Leffler建议的那样。这是我做的改变:

struct debuggerth_command {
    char * cmd;
    int (*function)(struct debuggerth_session *, char * input);
};

struct debuggerth_command {
    char cmd[4]; // changed this an array
    int (*function)(struct debuggerth_session *, char * input);
};

所以,当我在这里初始化结构时:

struct debuggerth_command debuggerth_protocol[] = { /*
    * Note: These strings are NOT null-terminated. The
    * strings are 4 bytes long for memory alignment and
    * integer-cast comparisons.
    */

    { "run ", debuggerth_startprocess },
    { "stop", 0 },
    { "inp ", 0 },
    { "sig ", 0 },
    { 0, 0 } /* Zero used to be a pointer value,
              * but now it's the first element in a 
              * 4 byte array
              */
};

这改变了for循环的评估:

int i = 0;
for (; debuggerth_protocol[i].cmd; i++)
    if (cmpcmd (debuggerth_protocol[i].cmd, in)) break;

要始终评估true,因为cmd现在是指向4字节数组的有效指针 - 其中第一个值为0

我将删除宏,因为它可能在某些体系结构上表现不佳。但是,使用C11的{​​{1}}功能无法解决这个问题吗?