将char *设置为NULL segfault

时间:2017-02-13 23:55:43

标签: c pointers segmentation-fault

在函数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.
}

2 个答案:

答案 0 :(得分:4)

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

pthread_create()的最后一个参数是参数。从联机帮助页:

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

因此,当通过pthread机制调用aa()时, a 参数为NULL,因为使用NULL arg调用 pthread_create()

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

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

所以给 pthread_create()一个非NULL参数,你的轨道就更好了。

答案 1 :(得分:1)

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

一种非常简单的方法可以阻止崩溃,即在主线程中将睡眠顺序和调用交换为last_letter

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

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

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

您仍然希望在join上调用t1,否则主文件可能会在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.
}