为什么在C程序中声明变量时会出现分段错误?

为什么在C程序中声明变量时会出现分段错误?,c,struct,segmentation-fault,C,Struct,Segmentation Fault,在我正在开发的一个C程序中出现分段错误后,我意识到错误来自于我声明的一个变量 这是我第一次尝试声明以下定义的数据类型Ens_参与者的变量: typedef struct { int cardinal; Participant tab[NB_MAX_PARTICIPANTS]; } Ens_participants; typedef struct { char nom[TMAX_MOT]; char prenom[TMAX_MOT]; char em

在我正在开发的一个C程序中出现分段错误后,我意识到错误来自于我声明的一个变量

这是我第一次尝试声明以下定义的数据类型Ens_参与者的变量:

typedef struct 
{
    int cardinal;
    Participant tab[NB_MAX_PARTICIPANTS];
} Ens_participants;
typedef struct 
{ 
    char nom[TMAX_MOT];
    char prenom[TMAX_MOT];
    char email[TMAX_MOT];
    char nationalite[TMAX_MOT];
    char langues_parles[LMAX][TMAX_MOT];
    char langues_cherches[LMAX][TMAX_MOT];
    int age;
    char sexe;
    char universite[TMAX_MOT];
    char disponible[TMAX_MOT];
} Participant; 
参与者是我创建的另一种数据类型,定义如下:

typedef struct 
{
    int cardinal;
    Participant tab[NB_MAX_PARTICIPANTS];
} Ens_participants;
typedef struct 
{ 
    char nom[TMAX_MOT];
    char prenom[TMAX_MOT];
    char email[TMAX_MOT];
    char nationalite[TMAX_MOT];
    char langues_parles[LMAX][TMAX_MOT];
    char langues_cherches[LMAX][TMAX_MOT];
    int age;
    char sexe;
    char universite[TMAX_MOT];
    char disponible[TMAX_MOT];
} Participant; 
TMAX_MOT、NB_MAX_参与者和LMAX为常数:

#define TMAX_MOT 250
#define LMAX 500

#define NB_MAX_PARTICIPANTS 1000
这是导致我的分段错误的行:

Ens_participants les_participants; 
我是否正确地创建和声明了这些变量?单个声明如何导致分段错误?如果有帮助的话,使用gdb调试器,我被告知错误出现在声明前两行:

int ligne_valide = 1;
然而,这是错误的,因为该程序与上述行配合得很好。我一尝试声明这个新变量,就开始出现问题

更新编号1 当我将常量NB_MAX_参与者的值改为10而不是1000时,程序编译得非常完美

更新编号2
我为这个问题道歉,因为正如@alk所建议的,我的问题在别处。我只重写了程序的类型并进行了实验。问题不在我想的地方。变量定义和声明不会在新程序中引起任何问题。

可能只是变量太大,无法满足平台上可用的堆栈大小。该代码在技术上适用于C(因此,不是编译时错误),但实际上操作系统没有保留足够的堆栈空间来实现这一点

毕竟,langues_parles字段本身占用250*500字节的空间;i、 e.125kB。有三个这样的字段,然后是一些其他字段,所以结构的每个实例大约需要380kB

现在,您还没有显示NB_MAX_参与者的值,但我猜380kB*NB_MAX_参与者太大了。例如,在Windows上,默认堆栈大小仅为1MB,因此如果NB_MAX_参与者大于2,则该变量太大(假设堆栈上没有其他变量)

您必须使用malloc()或类似函数在堆上分配结构:

Ens_participants* les_participants = malloc(sizeof(Ens_participants));
/* ... */
free(les_participants);

可能只是因为变量太大,无法满足平台上可用的堆栈大小。该代码在技术上适用于C(因此,不是编译时错误),但实际上操作系统没有保留足够的堆栈空间来实现这一点

毕竟,langues_parles字段本身占用250*500字节的空间;i、 e.125kB。有三个这样的字段,然后是一些其他字段,所以结构的每个实例大约需要380kB

现在,您还没有显示NB_MAX_参与者的值,但我猜380kB*NB_MAX_参与者太大了。例如,在Windows上,默认堆栈大小仅为1MB,因此如果NB_MAX_参与者大于2,则该变量太大(假设堆栈上没有其他变量)

您必须使用malloc()或类似函数在堆上分配结构:

Ens_participants* les_participants = malloc(sizeof(Ens_participants));
/* ... */
free(les_participants);

编辑2

这是C++中一个非常简单的例子,几乎没有我的头脑,没有测试过,所以肯定会有一些错误。p> 我们将使用标准的

映射
向量
字符串
数据类型 存储我们的参与者数据。
map
类型是一种关联数据结构, 存储由任意键索引的项。向量是一个线性容器 与数组类似,只是它的大小不是固定的,
string
就是字符串。所有这些类型都有自己的内存管理,因此在添加或删除项目时,您不必担心分配或释放内存

首先,我们需要定义
参与者
类型:

#include <string> 
#include <vector>

// By default, all members of a struct type are public.
struct Participant 
{
  std::string nom;
  std::string prenom;
  std::string email;
  std::string nationalite;
  std::vector< std::string > langues_parles;
  std::vector< std::string > langues_cherches;
  int age;
  char sexe;
  std::string universite;
  std::string disponible;

  // Generate a key for the participant based on nom and prenom.
  // The const keyword indicates that this method will not change
  // any of the non-static members of the class.
  std::string genkey() const 
  {
    return nom + ',' + prenom;
  }

  // Overload the stream input operator; we're going to assume
  // a file structure where each item is on it's own line, and where
  // the number of languages read and spoken is explicitly specified.
  // We're also going to assume the input is always well-behaved and
  // never needs validating (which is not going to be true in the real
  // world, of course
  std::istream operator>>( std::istream& s )
  {
    s >> nom;
    // repeat for prenom, email, nationalite
    int nlang;
    s >> nlang;
    for( size_t i = 0; i < nlang; i++ )
    {
      std::string lang;
      s >> lang;
      langues_parles.push_back( lang );
    }
    // repeat for langues_cherches
    // read remaining items
    return s;
  }

  // Overload the stream output operator, write in the same format
  // that we read.
  std::ostream& operator<<( std::ostream& s )
  {
    s << nom << std::endl;
    // repeat for prenom, email, nationalite;
    s << langues_parles.size() << std::endl;
    for ( std::vector< std::string >::iterator it = langues_parles.begin();
          it != langues_parles.end(); ++it )
      s << *it << std::endl;
    // repeat for langues_cherches
    // write remaining items
    return s; 
  }
};
完成后,我们可以从输入文件中读取数据:

std::ifstream s( data_file );
Participant p;

// use the overloaded stream input operator to read from our data file;
// keep reading until we hit EOF or an error.
while ( s >> p ) 
{
  // add the new participant to the map
  les_participants.insert( std::make_pair( p.genkey(),  p ) );
}
要在地图中查找特定条目,请使用
find
方法:

std::string key = "Bode,John";

Ens_participants::iterator it = les_participants.find( key );
if ( it == les_participants.end() )
  std::cerr << "Could not find John Bode in the list of participants!" << std::endl;
else
  // process entry
这将从运行时堆栈之外的其他地方(通常是从程序映像中)留出存储空间,这意味着您的可执行文件可能会变得更大一些。这样做的缺点是,只创建了一个
les_参与者的实例,并且在程序启动时就创建了该实例;不是每次输入声明变量的函数时都创建该变量的新实例。但是,如果您只打算使用一个实例,那么这可能是最简单的方法。
如果需要将其传递给任何函数,则无论被调用函数是否需要修改其内容,都必须传递指向该函数的指针。
void dump( Ens_participants *lp )
{
  for ( size_t i = 0; i cardinal; i++ )
  {
    printf( "Participant %zu: nom %s, prenom %s\n", i, lp-tab[i].nom, lp->tab[i].prenom);
    // print remaining fields
  }
}

int main( void )
{
  static Ens_participants les_participants;
  ...
  dump( &les_participants );
  ...
}
  • 使用
    malloc
    calloc
    动态分配
    les\u参与者。但是,这样大的请求不一定会成功,尤其是在堆碎片严重的情况下:
    
    int main( void )
    {
      /**
       * calloc initializes the allocated memory to all 0s, which is
       * useful since you are going to be storing a lot of 0-terminated
       * strings
       */
      Ens_participants *les_participants = calloc( NB_MAX_PARTICIPANTS, sizeof *les_participants );
      if ( les_participants )
      {
        /**
         * use the -> operator to access members of les_participants, like so:
         */
        les_participants->cardinal = some_value;
        les_participants->tab[i].age = 24;
        ...
        /**
         * Make sure to release the memory when you are done
         */
        free( les_participants );
      }
    }
    
  • Ens_参与者
    选项卡
    元素声明为指向
    参与者
    的指针数组,并根据需要动态分配
    参与者
    的每个实例:
    typedef struct 
    {
      int cardinal;
      Participant *tab[NB_MAX_ENTRIES];
    } Ens_participants;
    
    /**
     * This particular implementation assumes that participant data
     * is manually entered from standard input
     */
    Participant *newParticipant( void )
    {
      /**
       * Dynamically allocate a single instance of a Participant,
       * calloc initializes all elements to 0
       */
      Participant *p = calloc( 1, sizeof *p );
      if ( p )
      {
        printf( "nom: " );
        fgets( p->nom, sizeof p->nom, stdin );
        printf( "prenom: " );
        fgets( p->prenom, sizeof p->prenom, stdin );
        ...
      }
      return p;
    }
    
    int main( void )
    {
      Ens_participants les_participants = {0}; // make sure everything is initialized to 0 or NULL.
    
      printf( "Add participant [y/n]: " );
      while ( tolower( getchar() ) == 'y' && les_participants.cardinal < NB_MAX_ENTRIES )
      {
        les_participants.tab[les_participants.cardinal++] = newParticipant();
        printf( "Add another participant [y/n] " );
      }
    
    您已经将单个大内存分配的不确定性与管理一组较小的内存分配进行了交换。代码现在有点复杂了,有更多的失败点。
  • Ens_参与者的
    选项卡
    元素实现为列表或树,而不是数组。这是一个更多的工作,你可能不准备去做(即使是一个简单的例子也会让这个已经太大的答案变得难以理解)。与前面的建议一样,您正在进行一系列较小的内存分配请求,而不是一个非常大的分配请求,另外,您可以在插入条目时对其进行排序。额外奖金,您不限于固定数量的参赛作品(如前面的建议)。然而,这大大增加了您必须编写的代码的数量和复杂性
    
    while ( les_participants.cardinal )
      free( les_participants.tab[--les_participants.cardinal] );