在我正在开发的C程序中出现分段错误后,我意识到错误来自我声明的变量。
这是我第一次尝试声明下面定义的数据类型Ens_participants的变量:
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_PARTICIPANTS和LMAX为常量:
#define TMAX_MOT 250
#define LMAX 500
#define NB_MAX_PARTICIPANTS 1000
这是导致我的细分错误的行:
Ens_participants les_participants;
我是否正确创建并声明了这些变量?单个声明如何导致分段错误?如果它有帮助,使用gdb调试器,我被告知错误在此声明之前出现了两行:
int ligne_valide = 1;
然而,这是错误的,因为该程序适用于上述行。一旦我尝试声明这个新变量,我就开始遇到问题了。
当我将常量NB_MAX_PARTICIPANTS的值更改为10而不是1000时,程序编译完美。
我为这个问题道歉,因为正如@alk所说,我的问题出在其他地方。我只重写了程序的类型并进行了实验。问题不在我想象的地方。变量定义和声明不会在新程序中引起任何问题。
答案 0 :(得分:5)
可能只是因为您的变量对于平台上的可用堆栈大小而言太大了。代码在技术上适用于C(因此,不是编译时错误),但实际上操作系统没有预留足够的堆栈空间来实现这一点。
毕竟,你的langues_parles字段自带250 * 500字节的空间;即125kB。你有三个这样的字段,然后是其他一些字段,所以结构的每个实例大约需要380kB。
现在,您还没有显示NB_MAX_PARTICIPANTS的值,但我的猜测是380kB * NB_MAX_PARTICIPANTS太大了。例如,在Windows上,默认堆栈大小仅为1MB,因此如果NB_MAX_PARTICIPANTS大于2,则变量太大(假设堆栈上没有其他内容)。
您必须使用malloc()或类似的函数在堆上分配您的结构:
Ens_participants* les_participants = malloc(sizeof(Ens_participants));
/* ... */
free(les_participants);
答案 1 :(得分:2)
修改2
这里是C ++中非常的简单示例,几乎不是我的头脑,而且未经测试,所以毫无疑问会出现一些错误。
我们将使用标准map
,vector
和string
数据类型
存储我们的参与者数据map
类型是关联数据结构,
存储由任意键索引的项目。 vector
是一个线性容器
像一个数组,除了它的大小不固定,string
是一个字符串。所有这些类型都有自己的内存管理,因此您不必担心在添加或删除项目时分配或释放内存。
首先,我们需要定义Participant
类型:
#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;
}
};
定义Participants
类型后,我们可以实例化地图结构:
#include <map>
/**
* I normally wouldn't use a typedef here since I don't want to
* hide the "map-ness" of the implementation, but this will save
* some typing later on.
*/
typedef std::map< std::string, Participant > Ens_participants;
Ens_participants les_participants;
完成所有操作后,我们可以从输入文件中读取数据:
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
要遍历地图,请执行以下操作:
for ( Ens_participants::iterator it = les_participants.begin(); it != les_participants.end(); ++it )
{
std::cout << "key = " << it->first << std::endl;
std::cout << "data = " << it->second;
}
find()
,begin()
和end()
方法返回迭代器,其外观和行为与指针非常相似。 end()
函数返回的迭代器与NULL
指针值的用途基本相同;它充当定义明确的&#34;这里没有数据&#34;值。
请注意,大部分工作都是设置Participant
类型。存储和访问数据只需使用内置的map
容器及其方法。
我会把它留在那里,因为我不确切地知道你想做什么。同样,这些代码都没有经过测试,因此几乎肯定存在一些错误,而且风格并不是世界上最好的。但是,主要目的是向您展示C ++如何使这种应用程序比在C中更容易编写。首先,您不必为任何事物指定明确的大小;字符串,向量和地图将根据需要增大或缩小,而无需您采取任何操作。标准库中有一些更加流畅的工具,但这应该足以让您入门。
尽管如此,请确保在启动之前找到良好的 C ++参考。
修改强>
根据OP的其他评论,我将建议他使用不同的语言来实现此系统。 C实现这样的系统几乎是最糟糕的语言,特别是如果你没有在编程方面经验丰富的话。甚至C ++也会使这个任务更容易 。可能值得研究Python提供的内容。
原创
Ens_participants
的单个实例几乎是240 MiB宽。如果您尝试分配一个大auto
(本地)变量的对象,那么我熟悉的大多数系统都会牦牛。
在做其他事情之前,先做一些分析,然后弄清楚TMAX_MOT
,LMAX
和NB_MAX_PARTICIPANTS
需要多大的。你真的希望有1000名参与者吗?您真的希望您的任何参与者名称长度为250个字符,或者他们的电子邮件地址长吗?您真的希望您的任何参与者能够阅读或说出50种不同的语言(并且任何语言名称长度为250个字符)?等等。
如果分析显示是,那么你的数组真的需要那么大,那么有几种方法可以解决这个问题,增加难度的顺序,并且都有明显的缺点:
static
关键字添加到les_participants
的声明中:
static Ens_participants les_participants;
这将从运行时堆栈以外的某个位置预留存储(通常来自程序映像,这意味着您的可执行文件可能会变得更大)。这样做的缺点是只创建了les_participants
的单个实例,并且一旦程序启动就会创建它;每次输入声明它的函数时,您都不会创建变量的 new 实例。但是,如果您只打算拥有它的单个实例,这可能是最简单的方法。
如果你需要将它传递给任何函数,你将 传递指针,无论被调用的函数是否需要修改它的内容。
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 );
...
}
les_participants
或malloc
动态分配calloc
。但是,这样的大型请求并不能保证成功,特别是如果您的堆碎片严重碎片:
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 );
}
}
tab
的{{1}}元素声明为指针数组到Ens_participants
,并动态分配Participant
的每个实例} 有必要的:
Participant
当你完成后,你需要自己清理:
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] " );
}
您已经交换了单个大内存分配的iffiness来管理一堆较小的内存分配。代码现在有点复杂,有更多的失败点。
while ( les_participants.cardinal )
free( les_participants.tab[--les_participants.cardinal] );
tab
元素实现为列表或树,而不是数组。这是一个很多更多的工作,你可能不会这样做(即使一个简单的例子也会让这个已经太大的答案变得难以置信)。与之前的建议一样,您需要制作一堆较小的内存分配请求,而不是一个非常大的分配请求,并且您可以在插入条目时订购您的条目。额外奖励,您不仅限于固定数量的条目(如上一个建议)。但是,这会显着增加您必须编写的代码的数量和复杂性。