首页 > 解决方案 > Cpp:分段错误核心转储

问题描述

我正在尝试编写一个词法分析器,当我尝试将 isdigit 缓冲区值复制到一个 char 数组中时,我得到了这个核心转储错误,尽管我对标识符做了同样的事情而没有出错。

#include<fstream>
#include<iostream>
#include<cctype>
#include <cstring>
#include<typeinfo>

using namespace std;



int isKeyword(char buffer[]){
    char keywords[22][10] = {"break","case","char","const","continue","default", "switch",
                            "do","double","else","float","for","if","int","long","return","short",
                            "sizeof","struct","void","while","main"};
    int i, flag = 0;
    
    for(i = 0; i < 22; ++i){
        if(strcmp(keywords[i], buffer) == 0)
        {
            flag = 1;
            break;
        }
    }
    
    return flag;
}
int isSymbol_Punct(char word)
{
    int flag = 0;
    char symbols_punct[] = {'<','>','!','+','-','*','/','%','=',';','(',')','{', '}','.'};
    for(int x= 0; x< 15; ++x)
    {
        if(word==symbols_punct[x])
           {
               flag = 1;
               break;
           }
            
    }
    return flag;
}

int main()
{
    char buffer[15],buffer1[15];
    char identifier[30][10];
    char number[30][10];
    memset(&identifier[0], '\0', sizeof(identifier));
    memset(&number[0], '\0', sizeof(number));
    char word;
    ifstream fin("program.txt");
    if(!fin.is_open())
    {
        cout<<"Error while opening the file"<<endl;
    }
    int i,k,j,l=0;
    while (!fin.eof())
    {
        word  = fin.get();

        if(isSymbol_Punct(word)==1)
        {
            cout<<"<"<<word<<", Symbol/Punctuation>"<<endl;
        }
       
        if(isalpha(word))
        {        
            buffer[j++] = word;
            // cout<<"buffer: "<<buffer<<endl;
        }
        else if((word == ' ' || word == '\n' || isSymbol_Punct(word)==1) && (j != 0))
        {
            buffer[j] = '\0';
            j = 0;
                            
            if(isKeyword(buffer) == 1)
                cout<<"<"<<buffer<<", keyword>"<<endl;
            else
                {
                cout<<"<"<<buffer<<", identifier>"<<endl;
                strcpy(identifier[i],buffer);
                i++;
                }
                    
        } 
           
        else if(isdigit(word))
        {
            buffer1[l++] = word;
            cout<<"buffer: "<<buffer1<<endl;
        }
        else if((word == ' ' || word == '\n' || isSymbol_Punct(word)==1) && (l != 0))
        {
            buffer1[l] = '\0';
            l = 0;
            cout<<"<"<<buffer1<<", number>"<<endl;
            // cout << "Type is: "<<typeid(buffer1).name() << endl;
            strcpy(number[k],buffer1);
            k++;
                        
        } 

    }
    cout<<"Identifier Table"<<endl;
    int z=0;
    while(strcmp(identifier[z],"\0")!=0) 
    {       
        cout <<z<<"\t\t"<< identifier[z]<<endl; 
        z++;
        
    }  
    // cout<<"Number Table"<<endl;
    // int y=0;
    // while(strcmp(number[y],"\0")!=0) 
    // {       
    //     cout <<y<<"\t\t"<< number[y]<<endl; 
    //     y++;
        
    // }   

    
}

当我使用 strcpy 在 number[k] 中复制 buffer1 时出现此错误。我不明白为什么它没有被复制。当我打印 buffer1 的类型以查看 strcpy 是否没有生成错误时,我得到了 A_15,我搜索它,但没有找到任何相关信息。

标签: c++

解决方案


原因在这里(第 56 行):

int i,k,j,l=0;

您可能认为这会初始化i, j,klto 0,但实际上它只是初始化l0i, j, 和k在这里声明,但没有初始化为任何东西。结果,它们包含随机垃圾,因此如果您将它们用作数组索引,您最终可能会超出相关数组的边界。

到那时,任何事情都可能发生——换句话说,这是未定义的行为。一种可能发生在您身上的可能结果是,您的程序尝试访问操作系统尚未分配给它的内存,此时它崩溃(分段错误)。

为了具体说明我的意思,请考虑以下程序:

#include <iostream>

void print_var(std::string name, int v)
{
    std::cout << name << ": " << v << "\n";
}

int main(void)
{
    int i, j, k, l = 0;

    print_var("i", i);
    print_var("j", j);
    print_var("k", k);
    print_var("l", l);

    return 0;
}

当我运行它时,我得到以下信息:

i: 32765
j: -113535829
k: 21934
l: 0

如您所见,i,jkall 的出现使得将它们用作您声明的任何数组的索引都会超出它们的范围。除非你很幸运,否则这也会发生在你身上。

您可以通过分别初始化每个变量来解决此问题:

int i = 0;
int j = 0;
int k = 0;
int l = 0;

在自己的行上初始化每个使初始化更容易看到,有助于防止错误。

一些旁注:

  • 我能够立即发现这个问题,因为我将开发环境配置为标记引起编译器警告的行。如果您使用的是合理的编译器,那么在初始化之前使用变量应该会引发这样的警告,因此您可以在遇到此类问题时解决此类问题。您的开发环境可能支持相同的功能(如果不支持,您可能会考虑切换到支持的功能)。如果不出意外,您可以在编译期间打开警告(通过传递-Wall -Wextra给您的编译器等 - 检查其文档以获取详细信息)。
  • 由于您将索引声明为int,它们是有符号整数,这意味着它们可以包含负值(如j我的演示中所做的那样)。如果您尝试使用负索引对数组进行索引,最终将取消引用指向内存中数组开头“后面”位置的指针,因此即使索引为-1(请记住,C -style 数组基本上只是指向数组开头的指针)。此外,您的环境中可能只有 32 位,因此如果您正在编写 64 位代码,那么即使您要从中间索引到数组,也int可以定义太大而无法完全覆盖的数组。int由于这些原因,将原始数组索引键入为std::size_t通常是一个好主意,它始终能够表示目标环境中可能的最大数组的大小,并且也是无符号的。
  • 您将其描述为 C++ 代码,但除了 I/O 流之外,我在这里看不到太多 C++。与 C 风格的代码(必须非常小心地编写)相比,C++ 有许多可以帮助您防范错误的便利设施。例如,你可以用std::array的实例替换你的 C 样式数组,它有一个成员函数at(),它通过边界检查进行下标;在这种情况下,这会引发一个有用的异常,而不是让您的程序出现段错误。此外,在这种情况下,您似乎并不特别需要固定大小的数组,因此您最好使用std::vector;这将自动增长以适应新元素,帮助您避免超出向量范围的写入。两者都支持基于范围的 for 循环,让您无需手动处理索引。您可能会喜欢 Bjarne 的A Tour of C++,它对惯用的 C++ 进行了很好的概述,并使所有的毛茸茸的参考资料更易于解析。(如果你想养成一些好的 C 习惯,K&R和 Kernighan 以及 Pike 的The Practice of Programming都可以为你省去很多痛苦和眼泪)。

推荐阅读