【问题标题】:Getting Strange Segmentation Fault In Code在代码中出现奇怪的分段错误
【发布时间】:2014-04-28 13:57:53
【问题描述】:

我遇到了一个奇怪的分段错误。我几乎忽略了它,因为它只在传递给函数的字符串以字母 v 或更大字母开头时出现。该程序由一个由 26 个链表指针组成的数组和一个函数组成,该函数创建一个新节点,对它们进行排序(使用另一个函数,然后将它们添加到链表数组中)。这是遇到分段错误的代码。

 void sort(contact* newContact,int ind, contact** array[]){

    contact* current; //pointer to current contact

    //Case to test if the contact belongs at the head ###SEGMENTATION FAULT IF name begins with 'v'
    if ((*array)[ind] == NULL || strcmp(&(*array)[ind]->name[0],&newContact->name[0])>0){

        newContact->next=(*array)[ind]; // contect next pointer is equal to the pointer at index 'ind'

       (*array)[ind]=newContact; //goes to array and change the pointer at the index 'ind'   
    }

    else{

        current=(*array)[ind]; //updates the current pointer to the first pointer in the index 'ind'

        //finds the position to insert contact
        while(current->next!=NULL&&strcmp(&current->next->name[0],&newContact->name[0])<0){

            current=current->next; //updates pointer if current is not NULL or current is smaller than new contact
        }

        //the loop will break at the insertion point. 
        //inserts contact
        newContact->next=current->next;
        current->next=newContact;



   }

    printf("ADDED\n");
    }

这是创建节点的代码,以及在某处出错时收集输入的代码

void addContact(contact* list[]){
contact* new= malloc(sizeof(contact)); // creates new contact and allocates memory for it

printf("NAME: ");
new->name=GetInput(1); //saves the name
printf("NUMBER: ");
new->number=GetInput(0); //saves the number

int x=GetIndex(new->name);
contact** ptr=&list[x];
sort(new,x, &ptr);

printf("Contact Added!\n");
}

对于此函数,如果类型为 1,则确保输入的字符串以字母字符开头。如果没有,那就别管它了

char* GetInput(int type){
int loop=0;
char* buffer; //stores the entire input from the user 

if (type==1){

    do {
        scanf("%ms",&buffer); //takes input from user

        //checks if alphabetical
        if (isalpha(buffer[0]))
            loop=1;

        else{
            printf("Try again\nThe first character must be alphabetical\n");
            free(buffer);
        }
    }
    while(loop==0);    
}

//when 1 isnt specified, the input is left untreated
else{
    scanf("%ms",&buffer);
}

return buffer;

}

【问题讨论】:

  • 分段错误是运行时错误,因此尝试执行“gdb”至少您会确切知道发生在哪些行分段错误。之后就可以轻松解决了。
  • 我确实在 gdb 中运行过,这就是为什么我可以在代码中查明分段错误所在的原因,但我无法理解它是如何仅在 name[0] 字符为 v 或更大时出现分段错误的.它与除 v-z 中的字母外的其他字母完美配合
  • 首先,您的GetInput 函数完全错误!!! 1.你没有初始化buffer变量,所以它很可能指向了一个无效的内存地址。 2. 你到底想在do/while 循环中实现什么仍然是一个谜(检查第一个字符是否是字母?其他字符呢?)。 3.如果你想从用户那里扫描字符串,那么你应该用buffer调用scanf,而不是用buffer地址 buffer...
  • 您已将 array 声明为 contact ***,但您将其视为已声明为 contact **(*)[N]。那会引起胃灼热。您需要退后一步,确保所有类型都同步。
  • @barakmanos scanf 将初始化缓冲区指针。使用 %ms 说明符,scanf 将 malloc 数据。在继续之前,我试图检查输入的第一个字符是否是字母。

标签: c arrays pointers linked-list segmentation-fault


【解决方案1】:

这段代码有很多问题。您需要做的第一件事是将编译器上的警告设置调到最高。这里有很多东西不是语法错误,但仍然是错误。

一个主要问题(我怀疑您的运行时错误的根源)在getInput

char* GetInput(int type){
int loop=0;
char* buffer; //stores the entire input from the user 

没有。所有buffer 存储都是单个指针值;你实际上并没有留出任何 输入的存储。 buffer 没有指向任何有意义的地方。由于它被声明为auto 而您没有显式地初始化它,buffer 最初将包含一个随机位模式,该模式可能对应于可写地址、受保护地址或陷阱表示;即,不能是有效指针值的位模式。

尝试通过无效的指针值写入将调用未定义的行为,这仅意味着编译器不需要以任何特定方式处理编码错误。它可以发出诊断并停止翻译,发出诊断并继续翻译,或完全忽略问题。如果它完成翻译并生成一个二进制文件,该二进制文件可能会立即崩溃,或者它可能会在糟糕的状态下蹒跚前行,直到其他原因导致它稍后崩溃,或者它可能看起来正常运行。

但是,您巧妙地回避了整个问题,如下所示:

if (type==1){

    do {
        scanf("%ms",&buffer); //takes input from user
                    ^^^^^^^

不是传递buffer 作为你的参数(就像你应该的那样),你传递&amp;buffer。与buffer 不同,&amp;buffer 一个有效指针(它是buffer 变量的地址),因此您不会尝试通过无效指针进行写入。不幸的是,&amp;buffer 有错误的类型 - char ** 而不是char *。你的编译器应该已经警告过你了;如果没有,请提高警告级别(另外,m 不是 %s 转换说明符 AFAIK 的有效修饰符;它应该做什么?)。

问题是,buffer 变量仅足以存储单个 char * 值(宽度为 2 到 4 到 8 个字节,具体取决于平台);因此,如果您的平台上的char * 是 4 字节宽,您可以存储最多 3 个字符加上终止 0 字符的字符串,并且不会发生任何“坏”(也就是说,您不会覆盖属于不同的内存)目的)。再长一点,你就有可能破坏一些重要的东西(可能会导致“在糟糕的状态下一瘸一拐,直到后来有其他东西导致崩溃”的结果)。

这是修复该代码的一种方法,如果不一定是最好的方法(重新格式化以使我快速老化的眼睛更容易;显然,格式化在很大程度上是一个品味问题,所以你可以将其重新格式化为您喜欢的格式):

#define FIXED_BUFFER_LEN 20 // Initial buffer size; make this large enough to
                            // handle most of your expected inputs

char* GetInput( int type )
{
  int loop = 1; // yes, I'm changing the sense of this test

  /**
   * Create a local, temporary buffer for input, large enough to handle
   * most expected inputs
   */
  char tempBuffer[FIXED_BUFFER_LEN + 1];

  /**
   * Output buffer pointer, initialized to a known *invalid* value
   */
  char* buffer = NULL;

  /**
   * Build a format string for our scanf statements; a %s without a 
   * maximum length specifier is a gaping security hole.
   */
  char fmt[15]; // stores a string of the form "%DDD...s", where DDD... is 
                //the field width specifier; for example, "%20s"

  /**
   * Honestly, you want to check the result of the following operation,
   * but I've already spent more time on this than I expected.
   */
  sprintf( fmt, "%%%ds", FIXED_BUFFER_LEN );

  if (type==1)
  {
    do 
    {
      scanf( fmt, tempBuffer ); //takes input from user

      //checks if alphabetical
      if ( isalpha( tempBuffer[0] ))
        loop=0;

      else
      {
        printf( "Try again\nThe first character must be alphabetical\n" );
      }
  }
  while( loop );    
}

//when 1 isnt specified, the input is left untreated
else
{
  scanf( fmt, tempBuffer );
}

/**
 * Only now, after we've validated our input, do we allocate the 
 * dynamic memory for the buffer. 
 */ 
buffer = calloc( strlen( tempBuffer ) + 1, sizeof *buffer );
if ( buffer )
  strcpy( buffer, tempBuffer );

return buffer;

}

这段代码仍然存在一些问题(scanf很多 问题),但这应该会让你朝着正确的方向前进。将编译器上的警告设置调到最高,并分析和修复每一个。

【讨论】:

  • 好吧,我还没有通读整个事情。到目前为止我明白你在说什么。 %ms 或 %as(在一本手册中指定为 m,在另一本手册中指定为 a)说明符使 scanf 自动分配足够的内存来存储传入的字符串并将地址返回给说明符的对应变量,该变量应该是地址char*,因此它可以存储指向指定 char* 的字符串指针。另外,我怎样才能提高我的警告设置?我通常在编译时使用 Werror 和 Wall。另外,比较使用 malloc 创建的字符串的字符是否有问题?
  • 另外,很抱歉占用了您这么多时间
  • 您可能也想养成使用-std=c99 编译的习惯。 %m 由 POSIX 支持,但它不是 ISO C,而且 scanf() 首先有它所做的所有问题,让它也这样做对我来说似乎有点错误,但其他人可能有不同的意见。
  • 哇,我经常认为 %m 说明符是黄金,因为我不必担心内存分配问题
  • @AJPennster:有趣的是,我不知道m 修饰符%s(它不是标准的),它基本上把我所说的一切都抛到了窗外。没关系。
猜你喜欢
  • 1970-01-01
  • 2011-12-16
  • 1970-01-01
  • 2015-10-26
  • 1970-01-01
  • 1970-01-01
  • 2020-12-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多