为什么在这里使用了一个 uintptr_t 的类型转换?

huangapple go评论62阅读模式
英文:

Why is a cast of uintptr_t used here?

问题

以下是您要翻译的代码部分:

struct arguments {
    uint32_t threads;
    uint32_t size;
};

void *run_v1(void *arg) {
    uint32_t thread = (uintptr_t) arg;
    for (uint32_t j = 0; j < arguments.size; ++j) {
        size_t global_index = get_global_index(thread, j);
        char *string = get_string(global_index);
        hash_table_v1_add_entry(hash_table_v1, string, global_index);
    }
    return NULL;
}

...
int main () {
    ...
    for (uintptr_t i = 0; i < arguments.threads; ++i) {
        int err = pthread_create(&threads[i], NULL, run_v1, (void*) i);
        if (err != 0) {
            printf("pthread_create returned %d\n", err);
            return err;
        }
    }
    ...
}

关于您的问题,为什么需要将参数转换为uintptr_t类型而不是直接传递uint32_t并将其强制转换回自身,原因是pthread_create函数的第四个参数需要是void*类型。uintptr_t是一个无符号整数类型,足够大,可以容纳指针,因此可以安全地将uint32_t转换为uintptr_t,然后将其传递给pthread_create。这样做是为了符合pthread_create函数的接口要求,以避免类型不匹配的问题,不会导致未定义行为。

英文:

Here it is, in the context of pthread.h and stdint.h:

struct arguments {
	uint32_t threads;
	uint32_t size;
};

void *run_v1(void *arg) {
	uint32_t thread = (uintptr_t) arg;
	for (uint32_t j = 0; j &lt; arguments.size; ++j) {
		size_t global_index = get_global_index(thread, j);
		char *string = get_string(global_index);
		hash_table_v1_add_entry(hash_table_v1, string, global_index);
	}
	return NULL;
}

...
int main () {
    ...
	for (uintptr_t i = 0; i &lt; arguments.threads; ++i) {
		int err = pthread_create(&amp;threads[i], NULL, run_v1, (void*) i);
		if (err != 0) {
			printf(&quot;pthread_create returned %d\n&quot;, err);
			return err;
		}
	}
   ...
}

This is my professor's code, I read the specification here:

> The following type designates an unsigned integer type with the property that any valid pointer to void can be converted to this type, then converted back to a pointer to void, and the result will compare equal to the original pointer: uintptr_t

Why is the cast to this type necessary, rather than passing in a uint32_t and casting to itself? Is this undefined behavior?

答案1

得分: 1

以下是翻译好的内容:

强制转换保证指针以可逆的方式转换为无符号整数类型。

如果此类型大于uint32_t,编译器将引发警告。

直接强制转换为uint32_t可能会隐藏转换不可逆的事实。(尽管,老实说,我无法想象出一种导致这种情况的实现方式。)

英文:

The cast guarantees that the pointer be converted to an unsigned integer type in a reversible way.

In case this type is larger than a uint32_t, a compiler warning will be raised.

A direct cast to uint32_t might hide the fact that the conversion is not reversible. (Though, honestly, I cannot think of a implementation causing that.)

答案2

得分: 1

我理解了,以下是你要求的代码部分的翻译:


为什么需要将其转换为此类型,

这是不必要的。

而不是传递一个 uint32_t 并将其强制转换为自身?

这是我会做的。

这是未定义行为吗?

可能。但它肯定依赖于实现定义的行为。


在这里需要考虑通用原则和特定问题的原则。

最相关的通用原则 是你引用的 uintptr_t 的定义。它告诉你 uintptr_t 可以表示与每个独特、有效的 void * 值相对应的独特值,因此你可以确信将 void * 转换为 uintptr_t 类型不会导致信息丢失。因此,通常情况下,如果你想将一个对象指针表示为整数,uintptr_t 就是要选择的整数类型。

人们普遍认为 uintptr_t 必须与 void * 的大小相同,但尽管通常情况下如此,语言规范并未要求如此。由于 uintptr_t 只需为有效指针值提供不同的表示,并且由于不同的 void * 位模式不必表示不同的地址,因此 uintptr_t 可能比 void * 更小。另一方面,如果它比 void * 大,它也可以很好地完成其任务。

此外,语言规范要求你可以通过类型 uintptr_t 对指针进行往返转换,但并不要求你可以通过指针对任何类型的整数进行往返转换。大多数整数到指针的转换的结果是实现定义的。也就是说,鉴于这个...

uintptr_t x;
// .. 为 x 赋值 ...

... 语言规范允许这段代码打印出 "unequal":

if (x == (uintptr_t)(void *) x) {
    puts("equal");
} else {
    puts("unequal");
}

在这个具体的情况中

  • 要传达的值的上限从类型为 uint32_t 的对象中读取,因此所有要传达的值都可以由该类型表示;以及

  • 程序假设 C 实现中整数 -> 指针 -> 整数的过程会为所有要传达的整数值复制原始值。

在这些情况下,语言语义没有理由优先选择 uintptr_t 而不是涉及的整数类型。也就是说,如果所提供的代码正确运行,那么一个版本中用 uint32_t 替代 uinptr_t 也必须正确运行。而我认为后者更清晰、更简洁。

英文:

> Why is the cast to this type necessary,

It is not.

> rather than passing in a uint32_t and casting to itself?

That's what I would do.

> Is this undefined behavior?

Maybe. But it definitely relies on implementation-defined behavior.


There are both general principles and problem-specific ones to consider here.

The most relevant general principle is the definition of uintptr_t, which you quoted. It tells you that uintptr_t can represent a distinct value corresponding to each distinct, valid void * value, so you can be confident that converting a void * to type uintptr_t will not produce a loss of fidelity. In general, then, if you want to represent an object pointer as an integer, uintptr_t is the integer type to choose.

It is relatively common to conclude that uintptr_t must be the same size as a void *, but although that's often true, the language spec places no such requirement. Since uintptr_t needs only to provide distinct representations for valid pointer values, and also because distinct void * bit patterns don't have to represent distinct addresses, uintptr_t could conceivably be smaller than void *. On the other hand, it can fulfill its role just fine if it is larger than void *.

Moreover, the language spec requires that you can round-trip pointers through type uintptr_t, but it does not require that you can round-trip any variety of integer through a pointer. The results of most integer-to-pointer conversions are implementation defined. That is, given this ...

    uintptr_t x;
    // .. assign a value to x ...

... the language spec allows this to print "unequal":

    if (x == (uintptr_t)(void *) x) {
        puts(&quot;equal&quot;);
    } else {
        puts(&quot;unequal&quot;);
    }

But in this specific case,

  • the upper bound on the values to be conveyed is read from an object of type uint32_t, and therefore all values to be conveyed are representable by that type; and

  • the program is assuming a C implementation in which the integer --> pointer --> integer transit reproduces the original value for all the integer values to be conveyed.

Under these circumstances, language semantics present no reason to prefer uintptr_t over uint32_t as the integer type involved. That is, if the code presented works correctly, then a version in which uinptr_t is replaced replaced with uint32_t must also work correctly. And I find the latter alternative cleaner and clearer.

答案3

得分: 0

Here is the translation of the provided text:

> 为什么需要将其强制转换为这种类型,而不是转换为 uint32_t

这不是必需的,但如果 uint32_tuintptr_t 小,你可能会收到一个关于“从指针到不同大小的整数的转换”的警告。另一方面,uintptr_t 被定义为能够将指针值存储为整数。

当进行到 uintptr_t 的强制转换时,您可能仍然会收到一个关于“隐式转换会丢失整数精度”的警告,所以如果您最初存储在 void* 中的内容实际上是 uint32_t,则需要添加一个强制转换以避免潜在的警告:

uint32_t thread = (uint32_t)(uintptr_t)arg;

然而,我建议通过指针发送值,然后您就不需要任何显式的强制转换:

void *run(void *arg) {
    uint32_t *ptr = arg;
    uint32_t thread = *ptr;
    ...
    return NULL;
}

uint32_t value;
pthread_create(..., &amp;value);

一个更详细的示例,演示如何使用传递指针,如果您的线程需要比单个 uint32_t 更多的数据,可以看起来像这样:

#include &lt;pthread.h&gt;
#include &lt;stdint.h&gt;
#include &lt;stddef.h&gt;

#define SIZE(x) (sizeof (x) / sizeof *(x))

typedef struct {
    uint32_t value;
    // 其他线程需要的数据可以添加在这里
} thread_data;

void *run(void *arg) {
    thread_data *data = arg;
    // 在这里处理数据
    return NULL;
}

int main() {
    pthread_t th[10];
    thread_data data[SIZE(th)];
    
    for(int i = 0; i &lt; SIZE(th); ++i) {
        // 用要处理的值填充 data[i]
        pthread_create(&amp;th[i], NULL, run, &amp;data[i]);
    }

    // ... 加入等等 ...
}
英文:

> Why is the cast to this type necessary, rather than a cast to uint32_t?

It is not, but if uint32_t is smaller than uintptr_t you may get a warning about "cast from pointer to integer of different size". uintptr_t on the other hand is defined to be able to store pointer values as integers.

When the cast to uintptr_t is done you may still get a warning about "implicit conversion loses integer precision", so if what you've stored in the void* was actually an uint32_t to start with, add a cast to not get that potential warning:

uint32_t thread = (uint32_t)(uintptr_t)arg;

However, I suggest sending in the value via a pointer and then you wouldn't need any explicit casts:

void *run(void *arg) {
    uint32_t *ptr = arg;
    uint32_t thread = *ptr;
    ...
    return NULL;
}

uint32_t value;
pthread_create(..., &amp;value);

A more elaborate example of making use of passing a pointer which is easy to extend if your thread needs more data than a single uint32_t could look like this:

#include &lt;pthread.h&gt;
#include &lt;stdint.h&gt;
#include &lt;stddef.h&gt;

#define SIZE(x) (sizeof (x) / sizeof *(x))

typedef struct {
    uint32_t value;
    // other data that the thread needs can be added here
} thread_data;

void *run(void *arg) {
    thread_data *data = arg;
    // work with data here
    return NULL;
}

int main() {
    pthread_t th[10];
    thread_data data[SIZE(th)];
    
    for(int i = 0; i &lt; SIZE(th); ++i) {
        // fill data[i] with values to work with
        pthread_create(&amp;th[i], NULL, run, &amp;data[i]);
    }

    // ... join etc ...
}

huangapple
  • 本文由 发表于 2023年5月7日 03:57:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76190856.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定