简体   繁体   English

为什么我不能将 void* 转换回 int* 并取消引用它?

[英]Why cant I cast a void* back to an int* and dereference it?

Im trying to understand threading in C from a lab from my teacher and this is the way he had us do it in class.我试图从我老师的实验室中了解 C 中的线程,这就是他让我们在 class 中这样做的方式。

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

#define numberlen 1000000
int sum = 0;
int arr[numberlen];


void* Summing(void* param)
{
    int n = *((int*)param); //I'm guessing this is where my issue is but I'm still a first year so i'm not sure.
    int base = 500000 * n;
    for(int i =0; i<500000;i++)
    {
        sum += arr[i+base];
    }
}




int main()
{
    int nonthreadsum = 0;
    pthread_t thread_id_first;
    pthread_t thread_id_second;
    


    srand(time(NULL)); 
    
    for(int i = 0; i<numberlen;i++)
    {
        int r = (rand() % 10);
        arr[i] = r;
        nonthreadsum += r;
    }
    printf("%d\n",nonthreadsum);

    pthread_create(&thread_id_first, NULL, Summing, (void*)0);
    pthread_create(&thread_id_second, NULL, Summing, (void*)1);

    
    printf("%d\n", sum);


    return 1;
}

The issue I keep running into is that pthread functions only allow void*'s as arguments to pass to the actual function to be run.我一直遇到的问题是 pthread 函数只允许 void* 作为 arguments 传递给要运行的实际 function。 Otherwise I'd just put 0 or 1 in as the argument.否则,我只会输入 0 或 1 作为参数。 If anyone knows a way around this i'd appreciate it.如果有人知道解决此问题的方法,我将不胜感激。

It's allowed (in some cases) to cast from an integer to a pointer and from a pointer to an integer.允许(在某些情况下)从 integer 转换为指针,并从指针转换为 integer。 The problem is that's not what you're attempting to do.问题是这不是你想要做的。

When you call pthread_create , you're casting the integer values 1 and 0 to type void * .当您调用pthread_create时,您将 integer 值10转换为类型void * This is fine.这可以。 But then in the thread function you do this:但是然后在线程 function 你这样做:

int n = *((int*)param); 

You cast the void * argument to int * and then attempt to dereference that pointer.您将void *参数强制转换为int * ,然后尝试取消引用该指针。 But you didn't pass in an int * .但是您没有传入int * You passed an int .你通过了一个int So your program is treating the values 1 and 0 as pointers (the latter of which is actually a NULL pointer).因此,您的程序将值 1 和 0 视为指针(后者实际上是 NULL 指针)。

Since you converted from int to void * when you passed the values to the function, you want to cast back to int inside of the function.由于您在将值传递给 function 时从int转换为void * ,因此您希望在 function 内部转换回int

int n = (int)param; 

That being said, the best way to handle this would be to pass an actual int * to the function and convert it back in the function.话虽如此,处理这个问题的最佳方法是将实际的int *传递给 function 并将其转换回 function。 Converting from an int * to a void * and back is well defined in all cases.int *转换为void *并返回在所有情况下都是明确定义的。

You can do this by defining variables in the main function to dynamically create memory to hold these values:您可以通过在main function 中定义变量来动态创建 memory 来保存这些值:

int *t1_val = malloc(sizeof(*t1_val));
int *t2_val = malloc(sizeof(*t2_val));
*t1_val = 0;
*t2_val = 1;
pthread_create(&thread_id_first, NULL, Summing, t1_val);
pthread_create(&thread_id_second, NULL, Summing, t2_val);

Then using them in the thread as you were, adding a call to free :然后像你一样在线程中使用它们,添加对free的调用:

int n = *((int*)param);
free(param);

You could also do this without dynamic allocation by using variables of type int and passing their address, but you would need to make sure those variables don't go out of scope by the time the thread attempts to dereference the pointer.您也可以通过使用int类型的变量并传递它们的地址来在没有动态分配的情况下执行此操作,但是您需要确保在线程尝试取消引用指针时,这些变量不会 go 超出 scope。

Pass the Address of Your Data to Your Thread将您的数据地址传递给您的线程

Pass the address of your data to a thread.将数据的地址传递给线程。 Do not pass your data inside a pointer.不要在指针内传递数据。

The intended way to pass data to a thread is to pass a pointer to it.将数据传递给线程的预期方法是将指针传递给它。 For example, to pass data whose type is Type , you can use:例如,要传递类型为Type的数据,您可以使用:

Type x;
// … Set desired value(s) in x.
pthread_create(&thread_id_first, NULL, Summing, &x);

You can pass (void *) &x but do not need to.您可以通过(void *) &x但不需要。 The conversion to void * will be automatic in C, for a parameter declared as type void * .对于声明为 void * 类型的参数,将在 C 中自动转换为void * void *

Then, in the thread, you should restore the pointer type:然后,在线程中,您应该恢复指针类型:

void *Summing(void *param)
{
    Type *p = param;
    Type x = *p;
    // … Use x.
}

The conversion of the pointer from void * ( Type *p = param; ) and the use of the pointer ( Type x = *p; ) are shown in two lines above for illustration.上面两行显示了指针从void * ( Type *p = param; ) 的转换和指针的使用 ( Type x = *p; ) 以进行说明。 However, you can combine them into one line: Type x = * (Type *) param;但是,您可以将它们组合成一行: Type x = * (Type *) param; . .

Why void * Is Used为什么使用void *

Any kind of data can be passed to a thread, via an address.任何类型的数据都可以通过地址传递给线程。 However, C does not have good provisions for passing any kind of pointer to a routine.但是,C 没有很好的规定将任何类型的指针传递给例程。 pthread_create must be declared with a specific pointer type. pthread_create必须用特定的指针类型声明。 void * is used to serve as a generic type of pointer. void *用作泛型类型的指针。 So pthread_create has a void * parameter, but you can pass it any type of pointer to an object.所以pthread_create有一个void *参数,但您可以将任何类型的指针传递给它,指向 object。

In your case, the type can be an int , replacing Type in the sample code above.在您的情况下,类型可以是int ,替换上面示例代码中的Type Given int x;给定int x; , &x will be a pointer to an int , int * . , &x将是一个指向int , int *的指针。 When you pass it to pthread_create , it will be automatically converted to void * .当您将它传递给pthread_create时,它将自动转换为void *

Then, in the thread, you need to convert it back to an int * .然后,在线程中,您需要将其转换回int * After you do that, you can access the int that it points to.完成此操作后,您可以访问它指向的int

Passing Lots of Data传递大量数据

Threads are often passed more data than just a simple int .线程通常传递更多的数据,而不仅仅是一个简单的int The Type can be an object type. Type可以是 object 类型。 Often, a structure is used.通常使用结构。 A program will define a structure, such as:程序将定义一个结构,例如:

struct foo
{
    int start;
    int end;
    double coefficient;
    double ReferenceData[30];
}

As with the Type example, one would create an object of the desired type, struct foo x;Type示例一样,可以创建所需类型struct foo x; , fill it with data, and pass &x to pthread_create . ,用数据填充它,并将&x传递给pthread_create Then the thread will convert the void * it receives to struct foo * and use it to access the data.然后线程会将它接收到的void *转换为struct foo *并使用它来访问数据。

In this case, because there is so much data, the thread might not want to copy it, which is what struct foo x = * (struct foo *) param;在这种情况下,由于数据太多,线程可能不想复制它,这就是struct foo x = * (struct foo *) param; does.做。 Instead, the thread might just make a pointer of the right type, struct foo *p = param;相反,线程可能只是创建一个正确类型的指针, struct foo *p = param; . . Then the thread can use the data through the -> operator: p->start , p->ReferenceData[i] , and so on.然后线程可以通过->操作符使用数据: p->startp->ReferenceData[i]等等。

Threads can also pass results back to their creators this way, by putting results in the pointed-to-data.通过将结果放入指向数据中,线程也可以通过这种方式将结果传递回其创建者。

Passing Just a Little Data只传递一点数据

To pass a small amount of data, there is a shortcut: You can create an object and pass its address using a compound literal:要传递少量数据,有一个捷径:您可以创建一个 object 并使用复合文字传递其地址:

pthread_create(&thread_id_first, NULL, Summing, & (int) {1});`.

This creates an int object with the value 1 and passes its address to pthread_create .这将创建一个值为 1 的int object 并将其地址传递给pthread_create Then the thread would get the int with int x = * (int *) param;然后线程将使用int x = * (int *) param;获取int . .

Generally, the code construction ( type ) { initial values } is called a compound literal.通常,代码构造( type ) { initial values }称为复合文字。 It creates an object with type type and the given initial values.它创建一个类型为type和给定初始值的 object。 The & takes its address. &获取它的地址。

Compound literals have automatic storage duration, so the & (int) {1} will exist (have memory reserved for it) only until the end of the execution of the routine that creates it.复合文字具有自动存储持续时间,因此& (int) {1}将存在(为其保留 memory)直到创建它的例程执行结束。 You should not let that routine end until the thread is done using the compound literal.在使用复合文字完成线程之前,您不应该让该例程结束。

If you need an object to persist longer than execution of the routine that will create the thread, you can use malloc to reserve memory for the data.如果您需要 object 比执行将创建线程的例程持续更长的时间,您可以使用malloc为数据保留 memory。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM