在代码中获取奇怪的分段错误

时间:2014-04-28 13:57:53

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

我得到一个奇怪的分段错误。我几乎忽略了它,因为只有当传递给函数的字符串以字母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;

}

1 个答案:

答案 0 :(得分:2)

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

一个主要问题(我怀疑运行时错误的来源)位于getInput

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

没有。所有buffer存储都是单个指针值;你实际上并没有放弃任何东西 存储输入。 buffer并未指出任何有意义的内容。由于它已声明auto并且您没有明确初始化它,buffer最初将包含一个随机位模式,可能对应于可写地址,受保护地址或< em>陷阱表示;也就是说,一个位模式不能成为有效的指针值。

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

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

if (type==1){

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

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

问题是,buffer变量仅足以存储单个char *值(宽度在2到4到8个字节之间,具体取决于平台);因此,如果您的平台上的char *宽度为4个字节,那么您可以存储最多3个字符的字符串以及终止0字符,并且不会出现任何&#34;坏&#34;会发生(也就是说,你不会覆盖属于不同对象的内存)。任何更长的时间,你都有可能破坏一些重要的事情(可能导致&#34;跛行状态一直处于糟糕的状态,直到其他事情导致事故再次崩溃&#34;结果)。

这里有一种方法,如果不一定是最好的方式,修复该代码(重新格式化以使我的眼睛快速老化更容易;显然,格式化很大程度上取决于品味,所以你可以重新格式化,但你喜欢):

#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 许多问题),但这应该会让您朝着正确的方向前进。将编译器上的警告设置尽可能高,然后分析并修复它们中的每一个。