#c
Вопрос:
Я только что видел пример, который мне кажется странным, из книги https://pages.cs.wisc.edu/~ремзи/ОСТЕП/темы-api.pdf :
Во-вторых, если мы просто передаем одно значение (например, a
long long int
), нам не нужно упаковывать его в качестве аргумента …
void *mythread(void *arg) {
long long int value = (long long int) arg;
printf("%lldn", value);
return (void *) (value 1);
}
int main(int argc, char *argv[]) {
pthread_t p;
long long int rvalue;
Pthread_create(amp;p, NULL, mythread, (void *) 100);
Pthread_join(p, (void **) amp;rvalue);
printf("returned %lldn", rvalue);
return 0;
}
arg
это указатель, почемуlong long int value = (long long int) arg;
он правильный? Почему приведение не требуется от типа указателя к типу без указателя?
Разве это не должно быть:long long int value = *((long long int*)(arg));
?- Это
return (void *) (value 1);
правильно ? Чем это отличается от неправильной практики: «возвращать указатель локальной переменной» ?
Комментарии:
1.
long long int value = (long long int) arg;
это неверно. Это может привести к определенному поведению реализаций, включая повышение сигнала. Используетсяuintptr_t
для хранения указателей в целочисленных типах.2.
return (void *) (value 1);
может вызвать УБ. Это вполне возможноvalue==LLONG_MAX
. Снова используйтеuintptr_t
, чтобы избежать этой проблемы. Допускается, чтобы указатель указывал мимо допустимого объекта, поэтому1
это нормально и не должно вызывать UB или обертывание при использованииuintptr_t
.3. в стандартном C это неопределенное поведение (если
(void *)100
только оно не указывает на реальный объект), но в любом случае оно несколько распространено в программировании unix4. Если это на самом деле из книги, то вам нужно немедленно избавиться от этой книги. Если они даже не могут разобраться с функциями pthread, то вы знаете, что код никогда не был протестирован, и книга никогда не была корректно прочитана…
Ответ №1:
Техника , описанная в книге, срабатывает undefined behavior
и работает только в том случае, если sizeof(void*) == sizeof(long long int)
, что часто неверно (например: на 32-bit x86
и ARM32v7
платформах).
По сути, это ярлык, который хранит значение long long
целого числа, как если бы это был адрес указателя (и поэтому он хранится как void*
).
Я настоятельно рекомендую никогда не использовать этот ярлык, а просто передать указатель на long long
переменную, как обычно:
void* mythread(void* arg) {
long long real_arg = *((long long*) arg);
...
long long* result = malloc(sizeof(long long));
if (result == NULL) { return NULL; }
*result = 123LL;
return resu<
}
int main(void) {
...
long long value = 53LL;
pthread_create(amp;p, NULL, mythread, amp;value);
...
void* resu<
pthread_join(p, amp;result);
long long* real_result = resu<
...
free(result);
return 0;
}
РЕДАКТИРОВАТЬ: просто примечание: динамического распределения памяти, используемого в предыдущем фрагменте кода для возврата long long
результата, можно избежать, используя другие подходы. Например, вы можете повторно arg
использовать указатель, чтобы также сохранить результат:
void* mythread(void* arg) {
long long* real_arg = arg;
printf("I got: %lldn", *real_arg); // I got: 53
...
*real_arg = 123LL;
return real_arg;
}
int main(void) {
...
long long value = 53LL;
pthread_create(amp;p, NULL, mythread, amp;value);
...
void* resu<
pthread_join(p, amp;result);
long long* real_result = resu<
printf("Result: %lldn", *real_result); // Result: 123
...
return 0;
}
Комментарии:
1. Ваш код не делает то же самое. Вы разыменовываете указатель
mythread()
, он этого не делает.2. @1243123412341212121212123 Да, конечно, код, который я опубликовал, является правильной альтернативой тому, что делает код операции.
3. @124312341234123412341223 в этом весь смысл ответа. 😉
4. @LucaPolito Не уверен, что это то, чего хочет ОП. Если вы используете
uintptr_t
, вы можете исправить ответ, не изменяя поведение на платформах, гдеsizeof(void*) == sizeof(long long)
.5. Да, теперь это правильно. Поскольку поток разыменовывает адреса автоматических переменных
main
, важноmain
, чтобы эти переменные никогда не изменялись между вызовамиpthread_create
иpthread_join
и чтобы основной поток не выходил до создания потока.
Ответ №2:
arg-это указатель, почему значение long long int = (long long int) arg; правильно?
Является ли return (void *) (значение 1); правильно ?
Это не так. Передача целочисленных значений через поток void*
pthread является обычным, но грязным взломом. Все это при условии, что void*
и используемый целочисленный тип имеют одинаковый размер. Более правильным целочисленным типом для использования был intptr_t
бы тот, который в отличие long long
от гарантированно имеет тот же размер, что и a void*
.
Помимо размера, преобразование между целыми числами и указателями определяется реализацией: в некоторых системах это будет работать просто отлично, в других системах может возникнуть ловушка, если переданное целое число будет интерпретировано как несоосный адрес. Как правило, вы увидите такого рода хаки в коде Linux, который никогда не будет перенесен куда-либо еще, потому что ни x86, ни ARM не попадут в ловушку.
Что касается того, как это правильно написать… ну, вы можете передать адрес локальной переменной в потоке «создатель потока». Недостатком этого является то, что в случае, если эта переменная выходит за рамки «создателя потока», она больше не действительна. Обходной путь для этого состоит в том, чтобы использовать static
, а затем сделать локальную копию внутри потока первым делом:
long long value = ...;
pthread_create(amp;p, NULL, mythread, amp;value);
...
void *mythread(void *arg)
{
long long value = *(long long*)arg;
...
}
Однако вам все равно нужно быть осторожным, если несколько потоков используют одну и ту же переменную. Другим вариантом является динамическое выделение, но, как правило, оно выполняется медленнее.
В целом узким местом в этом коде является само создание потока, поэтому выполнять предварительную оптимизацию, подобную этому взлому, чтобы сохранить несколько инструкций, нецелесообразно.
Комментарии:
1. Здесь задействован не только C, но и POSIX. Кто-нибудь знает, гарантирует ли POSIX что-либо в указателях, что было бы уместно?