【问题标题】:Unable to pin down a bug in a simple multithreading program无法确定简单多线程程序中的错误
【发布时间】:2016-11-17 17:46:55
【问题描述】:

我正在从事一个涉及多线程的项目。虽然我对多线程有相当的了解,但我没有写过很多这样的代码。以下代码只是我为动手编写的一个简单代码。使用 gcc -pthread 编译时效果很好。

要在此代码的基础上进行构建,我需要包含一些已经包含并链接了 pthread 的库。如果我通过包含和链接这些库进行编译,则 5 次中有 3 次会出现分段错误。 main() 中的第一个 for 循环存在一些问题——用多个语句替换这个 for 循环就可以了。

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

#define NUM_THREADS 3

pthread_mutex_t m_lock = PTHREAD_MUTEX_INITIALIZER;

typedef struct{
    int id;
    char ip[20];
} thread_data;

void *doOperation(void* ctx)
{
    pthread_mutex_lock(&m_lock);
    thread_data *m_ctx = (thread_data *)ctx;
    printf("Reached here\n");
    pthread_mutex_unlock(&m_lock);
    pthread_exit(NULL);
}

int main()
{
    thread_data ctx[NUM_THREADS];
    pthread_t threads[NUM_THREADS];

    for (int i = 0; i < NUM_THREADS; ++i)
    {
        char ip_n[] = "127.0.0.";
        char ip_h[4];
        sprintf(ip_h, "%d", i+1);
        strcpy(ctx[i].ip, strcat(ip_n, ip_h));
    }

    for (int i = 0; i < NUM_THREADS; ++i)
    {
        pthread_create(&threads[i], NULL, doOperation, (void *)&ctx[i]) 
    }

    for (int i = 0; i < NUM_THREADS; ++i)
    {
        pthread_join(threads[i], NULL);
    }

    pthread_exit(NULL);

}

【问题讨论】:

  • strcat(ip_n, ip_h) - 调用 未定义的行为,除非 ip_h 没有内容。
  • 但是 ip_h 确实有内容——(i+1) 的字符串版本。如果 i 为 0,我的意思是“1”。
  • @SonuMishra,是的,这就是问题所在。
  • 一般来说,在调试分段错误(和一般的错误)时,调试器非常有用。看看 gdb - 周围有很多教程:google.com/search?q=gdb%20tutorial

标签: c multithreading segmentation-fault


【解决方案1】:

我刚刚在pthread_create(&amp;threads[i], NULL, doOperation, (void *)&amp;ctx[i])末尾添加了;

而这个分段错误可能是因为

char ip_n[] = "127.0.0.";

在上面,sizeof(ip_n) 仅返回 9。但是您至少需要10 字符来存储127.0.0.3 之类的字符串(最后包括空字符)。未经授权的内存访问可能会导致分段错误。尝试将其替换为 char ip_n[10] = "127.0.0.";

【讨论】:

  • 当我在这里写问题时,这只是一个错字。你发现第一个 for 循环有什么问题吗?正如我在问题中提到的,当使用 pthread 显式编译时,代码确实可以正常工作。
【解决方案2】:

ip_n[] 指向一个常数;编译器保留了 9 个字节,包括 NULL 字节。不应访问这 9 个字节之后的任何内容,如果这样做,结果是未定义的(它可能在某些时候有效,但可能并非一直有效)。当你这样做时:

strcat(ip_n, ip_h)

您正在溢出 ip_n 指向的缓冲区。也许这就是导致您的问题的原因。如果不是,我仍然建议修复此问题。

【讨论】:

  • 是的,他在溢出ip_n。不,ip_n 不指向常量。它并不指向任何东西,事实上,因为它是一个数组,而不是一个指针。 ip_n 声明中的字符串字面量是 initializer
  • C 中的数组变量包含其第一个元素的地址。它是一个常量指针,声明后不能更改。此外,您的数组变量指向一个常量字符串字面量。您可以执行以下操作:p[0] 或 *p,这将为您提供 printf("%c", *ip_n); 中的第一个元素。如果你想要第二个元素,你可以做 printf("%c", *(ip_n + 1));我将您推荐给“指针和数组”标题下的en.wikibooks.org/wiki/C_Programming/Pointers_and_arrays 干杯:-)
  • 数组不是指针,句号。就 wikibook 的不同建议而言,它过于简单化了。在大多数情况下,当数组出现在表达式中时,它衰减为指向其第一个元素的指针,但这与它成为指向第一个元素的指针有很大不同.您可以通过多种方式进行区分,其中最重要的是 sizeof 运算符。
【解决方案3】:

您说您提供的代码“工作正常”,但它有问题。特别是,第一个for 循环是错误的,因此在某些情况下它会给您带来麻烦也就不足为奇了。这是一个细分:

    char ip_n[] = "127.0.0.";

您已将ip_n 声明为char 的数组,其长度足以容纳给定的初始化程序,包括其终止的空字符。

    char ip_h[4];
    sprintf(ip_h, "%d", i+1);

假设sprintf()成功,你已经将一个非空字符串写入char数组ip_h

    strcpy(ctx[i].ip, strcat(ip_n, ip_h));

您尝试通过strcat()ip_h 的内容附加到ip_n 的末尾,但是没有空间——这超出了ip_n 的范围,产生了未定义的行为。

解决此问题的最简单方法可能是声明 ip_n 具有足以容纳完整数据的显式长度。一般来说,一个点分四组的 IP 地址字符串可能需要多达 16 个字节,包括终止符:

    char ip_n[16] = "127.0.0.";

【讨论】:

    【解决方案4】:

    您不能strcat(ip_n, ip_h),因为数组ip_n 只够容纳字符串"127.0.0."。这是手册页的内容,强调了

    strcat() 和 strncat() 函数附加一个副本 以空字符结尾的字符串 s2 到以空字符结尾的字符串的结尾 s1,然后添加一个终止 `\0'。 字符串 s1 必须有足够的 保存结果的空间。

    声明应该是

    char ip_n[20] = "127.0.0.";
    

    【讨论】:

      猜你喜欢
      • 2014-01-15
      • 2018-08-30
      • 2012-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-15
      相关资源
      最近更新 更多