【问题标题】:Setting char* to NULL segfault将 char* 设置为 NULL 段错误
【发布时间】:2017-02-13 23:55:42
【问题描述】:

在函数last_letter()中 c = NULL 行将导致该程序在 while (*c) 处出现段错误 评论它不会。什么原因?将 char 指针设置为 NULL 并重新分配一些东西给它?我认为将指针设置为 NULL 并将它们重新分配给其他东西是可以接受的吗?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

char s1[] = "abcdefg";
char s2[] = "xyz";
char* c;

void last_letter(char* a, int i) {
  printf("last_letter ( a is %s and i is %d)\n", a, i);
  sleep(i);
  c = NULL;   // comment out, what is different?
  sleep(i);
  c = a;
  sleep(i);
  while (*c) {
    c++;
  }
  printf("%c\n", *(c-1));
  return;
}


// This function will run concurrently.

void* aa(void *ptr) {
  last_letter(s2, 2);
  return NULL;
}

int main() {
  pthread_t t1;
  int iret1 = pthread_create(&t1, NULL, aa, NULL);
  if (iret1) {
    fprintf(stderr,"Cannot create thread, rc=%d\n", iret1);
  }
  last_letter(s1, 5);
  sleep(10);
  printf("Ended nicely this time\n");
  return 0; //never reached when c = NULL is not commented out.
}

【问题讨论】:

  • 如果您将c 设置为NULL,那么其他线程 可能会在c 为NULL 时尝试使用*c

标签: c pointers segmentation-fault


【解决方案1】:

首先,这是一个经典的比赛条件。该程序取决于调用 aa() 的顺序。

pthread_create() 的最后一个参数是参数。从手册页:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);

所以当通过 pthread 机制调用 aa() 时,a 参数为 NULL,因为 pthread_create() 是使用 NULL 参数调用的。

然后这个循环取消引用一个NULL指针,导致崩溃:

c = a;
sleep(i);
while (*c) {
    c++;
}

所以给 pthread_create() 一个非 NULL 参数,这样你就会走上更好的轨道。

【讨论】:

    【解决方案2】:

    您的变量c 是全局变量,因此被所有线程共享。它用于last_letter,它有可能同时在主线程和新线程中被调用。由于无法知道每个线程执行last_letter 的顺序,因此两者都可能对c 进行交错更改。所以当另一个线程期望它是一个有效值时,一个线程可以将它设置为 NULL。

    很可能停止崩溃的一种非常简单的方法是在主线程中交换睡眠顺序和对last_letter 的调用

    sleep(10);
    last_letter(s1, 5);
    

    这意味着t1 需要 10 秒才能完成,希望这已经足够了。更好的方法是在主线程中调用last_letter 之前加入t1。更好的方法是将c 移动到last_letter 中,因此每个线程都有一个唯一版本的c,并且不会踩到其他线程的脚趾。如果必须共享 c,则需要使用互斥锁来保护它。

    这样你就不需要阻止 main 执行 last_lettert1 也执行它。由于 main 和 t1 对不同的数据进行操作,并且不会改变该数据,因此它是安全的。

    尽管如此,您仍然希望在 t1 上调用 join,否则 main 可能会在 t1 完成之前完成并退出。

    #include <stdio.h>
    #include <unistd.h>
    #include <pthread.h>
    
    char s1[] = "abcdefg";
    char s2[] = "xyz";
    
    void last_letter(char* a, int i) {
    
      char* c;
      printf("last_letter ( a is %s and i is %d)\n", a, i);
      sleep(i);
      c = NULL; // pointless but if you must
      sleep(i);
      c = a;
      sleep(i);
      while (*c) {
        c++;
      }
      printf("%c\n", *(c-1));
    }
    
    // This function will run concurrently.
    void* aa(void *ptr) {
      last_letter(s2, 2);
      return NULL;
    }
    
    int main() {
      pthread_t t1;
    
      int iret1 = pthread_create(&t1, NULL, aa, NULL);
      if (iret1) {
        fprintf(stderr,"Cannot create thread, rc=%d\n", iret1);
        return 0;
      }  
    
      last_letter(s1, 5);
      pthread_join(t1, NULL); // should check return value in production code
    
      printf("Ended nicely this time\n");
      return 0; //never reached when c = NULL is not commented out.
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-01
      • 2011-02-23
      • 2012-12-05
      • 2019-12-10
      • 2011-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多