【问题标题】:Pointer changes at runtime运行时指针变化
【发布时间】:2018-05-22 23:55:07
【问题描述】:

我有以下文件test.c

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

#define LEXER_INC(l) ( (l)->pos++ )

#define isCidstart(c) (isalpha(c) || (c)=='_')
#define isCident(c) (isalnum(c) || (c)=='_')

typedef struct LexerState
{
    const char *fileName;
    const char *sourceText;
    int sourceLength;
    const char *pos;
    const char *end;
    int line;
} LexerState;

typedef enum LexerToken
{
    TokenHalt,
    TokenPush,
    TokenPop,
    TokenEndOfLine,
    TokenEOF,
    TokenNone
} LexerToken;

typedef struct ReservedWord
{
    const char *word;
    LexerToken token;
} ReservedWord;

static ReservedWord reservedWords[] =
{
    {"halt", TokenHalt},
    {"push", TokenPush},
    {"pop", TokenPop}
};

void lexerInit(LexerState *lexer)
{
    lexer->fileName = "test.s";
    lexer->sourceText = "pop\r\npush\r\nhalt\r\n"; // read from file
    lexer->sourceLength = strlen(lexer->sourceText); // 17
    lexer->pos = lexer->sourceText; // pointing to first char of lexer->sourceText
    lexer->end = lexer->sourceText + lexer->sourceLength; // end of lexer->sourceText string, i.e. '\0'
    lexer->line = 1;
}

LexerToken lexerCheckReservedWord(const char *word)
{
    for (int count = 0; count < (sizeof(reservedWords) / sizeof(ReservedWord)); count++)
    {
        if (!strcmp(word, reservedWords[count].word))
            return reservedWords[count].token;
    }

    return TokenNone;
}

LexerToken lexerGetWord(LexerState *lexer)
{
    const char *startPos = lexer->pos;
    LexerToken token = TokenNone;
    char *word;
    int len;

    do
    {
        LEXER_INC(lexer);
    }
    while (lexer->pos != lexer->end && isCident(*lexer->pos));

    len = lexer->pos - startPos;
    word = malloc(len + 1); /* (len + 1) for '\0' ending */
    strncpy(word, startPos, len);
    word[len] = '\0';

    token = lexerCheckReservedWord(word);

    return token;
}

LexerToken lexerGetToken(LexerState *lexer)
{
    char thisChar;

    /* Skip white characters */
    while (lexer->pos != lexer->end && isspace(*lexer->pos))
    {
        if (*lexer->pos == '\n')
        {
            /* New line found */
            lexer->line++;
            LEXER_INC(lexer);
            return TokenEndOfLine;
        }

        LEXER_INC(lexer);
    }

    if (lexer->pos == lexer->end || *lexer->pos == '\0')
    {
        return TokenEOF;
    }

    thisChar = *lexer->pos;

    if (isCidstart(thisChar))
    {
        return lexerGetWord(lexer);
    }

    return TokenEOF;
}

int main(int argc, const char *argv[])
{
    LexerState *lexer;
    LexerToken token;

    lexer = malloc(sizeof(lexer));
    lexerInit(lexer);

    while ((token = lexerGetToken(lexer)) != TokenEOF)
    {
        printf("token %d\n", token);
    }

    return EXIT_SUCCESS;
}

在使用gdb函数lexerGetWord进行调试时,我意识到在执行word = malloc((len + 1) * sizeof(char));行后,lexer-&gt;pos指针地址变为某个数字(在我的情况下为0x23):

82              len = lexer->pos - startPos;
(gdb) next
83              word = malloc(len + 1);   // (len + 1) for '\0' ending
(gdb) print len
$1 = 3
(gdb) print lexer->pos
$2 = 0x60003b1b3 "\r\npush\r\nhalt\r\n"

这里lexer-&gt;pos从字符串lexer-&gt;sourceText("pop\r\npush\r\nhalt\r\n")中得到字"pop"后指向这个地址

(gdb) next
84              strncpy(word, startPos, len);
(gdb) print lexer->pos
$3 = 0x23 <error: Cannot access memory at address 0x23>

但是malloc被执行后,指针改变了它的地址,导致后面是Segmentation fault

$ gcc test.c -o test
$ ./test
token 2
Segmentation fault (core dumped)

我做错了什么?

编辑

这只发生在 Cygwin gcc 上。我尝试了 MinGW,一切正常

$ uname -a
CYGWIN_NT-6.3 Stepan 2.9.0(0.318/5/3) 2017-09-12 10:18 x86_64 Cygwin

【问题讨论】:

  • 代码是否存在实际问题?您在gdb 中观察到的可能是优化的结果,并不是真正的问题。如果有问题,请发帖minimal reproducible example
  • 使用 valgrind 之类的代码运行您的代码 - 它会标记任何与内存相关的问题,这些问题通常是您所看到的问题的原因。
  • @EugeneSh。我禁用了所有优化,它仍然不起作用
  • strncpy 不会复制字符串末尾的 '\0' 如果它不在复制的长度中。在您的情况下,字符 pop 被复制,但字符串未关闭。添加word[len]='\0'
  • 至于strncpy() 问题,您的问题应该是分心错误,因为从形式上看,您显示的代码似乎是正确的。 malloc() 和指针损坏之间的唯一交互可能与空闲内存的重用等有关。简而言之,从您向我们展示的内容中看,没有任何线索。再次检查所有代码流

标签: c pointers gcc cygwin malloc


【解决方案1】:

问题出在main:

lexer = malloc(sizeof(lexer));

您为指针分配了足够的空间,而不是它指向的地方。结果,您最终会写入超过分配内存的末尾。这会调用undefined behavior

lexer 指向的内容需要空间:

lexer = malloc(sizeof(*lexer));

【讨论】:

  • 回答here我的代码是正确的。
  • @StepanKlymonchuk 这是正确的。在链接的答案中,给出了类型名称。在您的情况下,给出了一个变量名称,并且该变量的类型为LexerState *
  • 不,不是。 lexer 是一个指针。在那个答案中,count_tablestruct(类型名称)
  • @StepanKlymonchuk 未定义的行为。表现出来的一种方式是,事情似乎工作正常,然后像重新编译这样简单的事情可能会导致它崩溃。
猜你喜欢
  • 2014-03-13
  • 2020-06-09
  • 2021-10-07
  • 2011-09-13
  • 2023-03-28
  • 2015-03-08
  • 2013-08-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多