尝试将字符串分配给结构内的字符串变量时出错

Posted

技术标签:

【中文标题】尝试将字符串分配给结构内的字符串变量时出错【英文标题】:Error when trying to assign a string into a string variable inside a struct 【发布时间】:2022-01-14 23:04:57 【问题描述】:

我正在编写一个词法分析器软件,但是当我尝试将字符串分配给结构内的字符串变量时遇到问题。

--common.h--
#define TEST printf("--TEST--\n")

struct Token 
    char* ID;
    char* string;          // String variable
;

struct Token* tokenizer(char* input);

void PrintToken(struct Token* token);

--lexer.c--
#include <stdio.h>
#include <string.h>
#include "common.h"

struct Token* tokenizer(char* input)

    struct Token* token;

    int toknum = 0;

    int i = -1;

    while (1) 
        char* string;

        for (i += 1; input[i] != ' '; i++) 
            string[i] = input[i];
        

        strcpy(token[toknum].string, string);       // The problem is here.

        if (input[i] == '\n' || input[i] == '\0')
            break;        

        toknum++;
    

    return token;


void PrintToken(struct Token* token)

    for (int i = 0; i < 5; i++) 
        printf("%s\n", token[i].string);
    


--main.c--
#include <stdio.h>
#include "common.h"

int main()

    char* input = "Hello there";

    struct Token* token = tokenizer(input);

    PrintToken(token);

    return 0;

我用gcc main.c lexer.c -o final.o编译上面的程序并运行final.o后,我得到一个错误,它说:

Segmentation fault

我尝试将strcpy(token[toknum].string, string);替换为token[toknum].string = string;,但结果是一样的。

有什么办法可以避免这个错误?

【问题讨论】:

您越早意识到“字符串变量”实际上是由一个 指针 组成,它必须包含 您的程序分配的一些内存的内存地址 ,您将越早停止崩溃和疯狂行为。所有其他指针也是如此。我在这里看不到内存管理。 以下是一些额外的有用的编译器选项,您可以在编译程序时提供 gcc:-Wpedantic -fsanitize=address int i = -1;for (i += 1; 看起来很奇怪。 你应该打开警告。对 GCC 使用 -Wall -Wextra。您应该会收到一些关于在没有初始化的情况下使用变量的警告。 【参考方案1】:

您正在使用未初始化的变量:

struct Token* token;

这仅定义了一个指针变量,但没有分配任何有效内容。这意味着内容不确定,读取此变量的内容会导致未定义的行为。

此外,此变量未指向有效地址。通过strcpy 写入“随机”地址也会导致未定义的行为。

您必须为此分配动态内存:

struct Token *token = malloc(sizeof(*token));
// don't forget error handling

然后当你想要更多条目时,扩大内存:

struct Token *temp = realloc(token, (toknum+1)*sizeof (*token));
if (NULL != temp) 
  token = temp;
else
// error handling

只使用这样的数组:

struct Token token[x];

将保留内存,但您不能在函数结束时返回该地址,因为该对象的生命周期将同时结束,返回后您可能无法访问它。


string 也会出现同样的问题:

   char* string;

   for (i += 1; input[i] != ' '; i++) 
        string[i] = input[i];  // <<< string is uninitialized, does not point to valid memory.
   

您没有为字符串提供任何内存。 在这里,您可以使用本地数组来保存字符串。


token[toknum].string 也是同样的问题。

你的结构只包含指针。同样,您需要保留内存。 要么再次使用动态内存分配,要么将token.string 设为固定长度的数组。

如果token[toknum]string 都有效,那么您尝试使用token[toknum].string = string; 将起作用。


同一篇文章中还有另一个问题:

    for (i += 1; input[i] != ' '; i++) 
        string[i] = input[i];
    

    strcpy(token[toknum].string, string); // << strcpy expects a nul-terminated string

你没有正确终止你的字符串。在这种情况下,strcpy 将愉快地遍历您的内存,超出内存分配的边界,直到意外找到终止的 \0 字节。


而且...如果input 不包含另一个空格字符会怎样?这个循环最终会到达input 的终止0 并继续...

还有另外两个导致不正当行为的原因在等着你。


这只是我在tokenizer 中发现的第一眼。

PrintToken 函数中,您还有另一个问题: 是什么让你想到,你可以打印token 的 5 个元素?你永远不会为 5 个元素保留内存,即使你这样做了,你也不会初始化多余的元素以包含一些空字符串。

【讨论】:

【参考方案2】:

我认为问题不在于 strcpy,而在于 tokenizer 函数。

struct Token* token; 的行很危险,因为您正在初始化数组,但不要为它分配内存。如果您的数组具有固定数量的元素,请尝试使用struct Token token[5],或相应地使用malloc 函数。

另外,char *string也有同样的问题

【讨论】:

您对丢失内存的看法是对的,但使用数组是错误的。您不能返回本地数组的地址。而是必须使用 malloc 你应该删除那个答案,因为它是错误的。

以上是关于尝试将字符串分配给结构内的字符串变量时出错的主要内容,如果未能解决你的问题,请参考以下文章

Visual C++ 表单、简单消息框和将文本从文本字段分配给字符串时出错

将 strings.xml 资源值分配给 MainActivity 中的字符串失败

将结构的第一个变量分配给另一个

尝试在结构字符串中编辑字符串时出错

将这个复活节彩蛋分配给一个变量

如何将 JSON 字符串分配给变量?