Почему getpid () возвращает pid_t вместо int?

Какова логика таких вызовов, как getpid() возврат значения типа pid_t вместо unsigned int? Или int? Как это помогает?

Полагаю, это связано с переносимостью? Гарантировать, что pid_t имеет одинаковый размер на разных платформах, которые могут иметь разные размеры int и т. Д.?


person ntl0ve    schedule 11.09.2011    source источник


Ответы (6)


Я думаю, что все наоборот: сделать программу переносимой на разные платформы, независимо от того, например, PID 16 или 32 бит (или даже больше).

person zvrba    schedule 11.09.2011
comment
Ввод целых чисел за пределы xiint8_t xint16_t xint32_t xint64_t не решает никаких проблем: есть автоматические целочисленные преобразования. Следовательно, каждая реальная программа разумного возраста (скажем, 10 лет) и размера (скажем, 1MLOC) содержит тысячи необнаруживаемых безмолвных предположений о целочисленных размерах. - person zzz777; 08.08.2017

Причина в том, чтобы позволить неприятным историческим реализациям все еще соответствовать. Предположим, в вашей исторической реализации было (довольно часто):

short getpid(void);

Конечно, современные системы хотят, чтобы pid был хотя бы 32-битным, но если стандарт требует:

int getpid(void);

тогда все исторические реализации, которые использовали short, станут несовместимыми. Это было сочтено неприемлемым, поэтому было создано pid_t, и реализации было разрешено определять pid_t по своему усмотрению.

Обратите внимание, что вы ни в коем случае не обязаны использовать pid_t в своем собственном коде, если вы используете тип, достаточно большой для хранения любого pid (например, intmax_t будет работать нормально). Единственная причина, по которой pid_t должен существовать, заключается в том, что стандарт определяет getpid, waitpid и т. Д. В терминах этого.

person R.. GitHub STOP HELPING ICE    schedule 11.09.2011
comment
Ах, стандарты. Понятно. Но как насчет стиля / лучших практик? Лучше использовать pid_t, чем int для удержания pid? С точки зрения читабельности (имеется в виду ответ @Maz)? Или это настолько банальная и бессмысленная вещь, что не заслуживает такого внимания? - person ntl0ve; 11.09.2011
comment
Если бы я мог дважды проголосовать ... но ответ zvrbas является своего рода сжатой версией этого ответа, поэтому я тоже буду голосовать за zvrba. - person Prof. Falken; 11.09.2011
comment
Использование int, вероятно, не является хорошей идеей, поскольку оно не будет поддерживать гипотетические будущие реализации с 64-битными pid. Я бы просто использовал pid_t для большинства применений, но имейте в виду, что если у вас есть структура данных, которая может хранить общие целые числа с большим типом (т.е. intmax_t), было бы приемлемо хранить pid таким образом. - person R.. GitHub STOP HELPING ICE; 11.09.2011
comment
Лучшим примером бесполезного типа будет socklen_t, который был добавлен по той же причине (разногласия по поводу того, должны ли определенные функции принимать int или size_t аргументы). - person R.. GitHub STOP HELPING ICE; 11.09.2011

На разных платформах и операционных системах разные типы (например, pid_t) могут быть 32-битными (unsigned int) на 32-битной машине или 64-битными (unsigned long) на 64-битной машине. Или по какой-то другой причине операционная система может выбрать другой размер. Кроме того, при чтении кода становится ясно, что эта переменная представляет собой «объект», а не просто произвольное число.

person Maz    schedule 11.09.2011
comment
fork() возвращает -1 в случае ошибки, поэтому pid_t должен быть целочисленного типа со знаком. - person Przemek; 17.06.2018

Его цель - сделать pid_t или любой другой тип сортировки независимым от платформы, чтобы он работал должным образом независимо от того, как он на самом деле реализован. Эта практика используется для любого типа, который должен быть независимым от платформы, например:

  • pid_t: Должен быть достаточно большим, чтобы хранить PID в системе, для которой вы кодируете. Насколько мне известно, соответствует int, хотя я не очень хорошо знаком с библиотекой GNU C.
  • size_t: переменная unsigned, способная хранить результат оператора sizeof. Обычно равен размеру слова системы, для которой вы кодируете.
  • int16_t (intX_t): должно быть ровно 16 бит, независимо от платформы, и не будет определяться на платформах, которые не используют 2 n -битных байта (обычно 8- или 16-битные) или, что гораздо реже, предоставить средства доступа ровно к 16 битам из большего типа (например, «байты» PDP-10, которые могут быть любым количеством смежных битов из 36-битного слова и, таким образом, могут быть ровно 16 бит) и, следовательно, не поддерживает 16-битные целочисленные типы с дополнением до двух (например, 36-битная система). Обычно соответствует short на современных компьютерах, хотя может быть int на старых.
  • int_least32_t (int_leastX_t): должен быть наименьшего возможного размера, который может хранить не менее 32 бит, например 36 бит в 36-битной или 72-битной системе. Обычно соответствует int на современных компьютерах, хотя может быть long на старых.
  • int_fastX_t: должен быть максимально быстрым типом, который может хранить не менее X бит. Как правило, это системный размер слова, если (X <= word_size) (или иногда char для int_fast8_t), или действует как int_leastX_t, если (X > word_size))
  • intmax_t: Должна быть максимальной шириной целого числа, поддерживаемой системой. Как правило, в современных системах это будет не менее 64 бит, хотя некоторые системы могут поддерживать расширенные типы, размер которых превышает long long (и если да, то intmax_t должен быть самым большим из этих типов).
  • И больше...

С механической точки зрения он позволяет установщику компилятора typedef установить соответствующий тип идентификатора (будь то стандартный тип или внутренний тип с неудобным названием) за кулисами, будь то создание соответствующих файлов заголовков, кодирование его в исполняемый файл компилятора или какой-либо другой метод. . Например, в 32-битной системе Microsoft Visual Studio будет реализовывать intX_t и подобные типы следующим образом (примечание: комментарии, добавленные мной):

// Signed ints of exactly X bits.
typedef signed char int8_t;
typedef short int16_t;
typedef int int32_t;

// Unsigned ints of exactly X bits.
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;

// Signed ints of at least X bits.
typedef signed char int_least8_t;
typedef short int_least16_t;
typedef int int_least32_t;

// Unsigned ints of at least X bits.
typedef unsigned char uint_least8_t;
typedef unsigned short uint_least16_t;
typedef unsigned int uint_least32_t;

// Speed-optimised signed ints of at least X bits.
// Note that int_fast16_t and int_fast32_t are both 32 bits, as a 32-bit processor will generally operate on a full word faster than a half-word.
typedef char int_fast8_t;
typedef int int_fast16_t;
typedef int int_fast32_t;

// Speed-optimised unsigned ints of at least X bits.
typedef unsigned char uint_fast8_t;
typedef unsigned int uint_fast16_t;
typedef unsigned int uint_fast32_t;

typedef _Longlong int64_t;
typedef _ULonglong uint64_t;

typedef _Longlong int_least64_t;
typedef _ULonglong uint_least64_t;

typedef _Longlong int_fast64_t;
typedef _ULonglong uint_fast64_t;

Однако в 64-битной системе они не обязательно могут быть реализованы одинаково, и я могу гарантировать, что они не будут реализованы таким же образом в архаичной 16-битной системе, если вы найдете версию, совместимую с MSVS. с одним.

В целом, это позволяет коду работать должным образом независимо от специфики вашей реализации и соответствовать тем же требованиям в любой совместимой со стандартами системе (например, pid_t может быть гарантированно достаточно большим, чтобы содержать любой действительный PID в рассматриваемой системе, нет независимо от того, для какой системы вы кодируете). Это также избавляет вас от необходимости разбираться в деталях и искать внутренние имена, с которыми вы, возможно, не знакомы. Короче говоря, он гарантирует, что ваш код работает одинаково, независимо от того, реализован ли pid_t (или любой другой аналогичный typedef) как int, short, long, long long или даже __Did_you_really_just_dare_me_to_eat_my_left_shoe__, поэтому вам не нужно.


Кроме того, он служит формой документации, позволяющей с первого взгляда сказать, для чего предназначена данная переменная. Учтите следующее:

int a, b;

....

if (a > b) {
    // Nothing wrong here, right?  They're both ints.
}

А теперь давайте попробуем еще раз:

size_t a;
pid_t b;

...

if (a > b) {
    // Why are we comparing sizes to PIDs?  We probably messed up somewhere.
}

Если он используется как таковой, он может помочь вам найти потенциально проблемные сегменты кода до того, как что-то сломается, и может значительно упростить устранение неполадок, чем это было бы в противном случае.

person Justin Time - Reinstate Monica    schedule 22.01.2016
comment
byte == char в C. byte может быть 16-битным, и тогда int16_t может существовать. - person Antti Haapala; 12.11.2018
comment
Я говорил об аппаратном обеспечении, @AnttiHaapala, а не о языке. Следовательно, указание, что тип определен только на платформах, которые поддерживают 8-битные байты, поскольку они являются расширением платформ, которые позволили бы использовать точно 16-битные типы. Обратите внимание, что int16_t определяется не как длина двух char, а как точно 16 бит. Тем не менее, я забыл о требовании дополнения 2, так что спасибо за эту часть. - person Justin Time - Reinstate Monica; 15.11.2018
comment
Платформа в вопросе, который я связал, не имеет никаких средств для 8-битной адресации. Число 8 (почти) так же бессмысленно, как 4 на x86 ... - person Antti Haapala; 15.11.2018
comment
Возможно, я неправильно прочитал ваш комментарий, @AnttiHaapala. Хм ... Тогда я сделаю быструю правку. - person Justin Time - Reinstate Monica; 27.11.2018
comment
Хорошо, отредактировал его, а также учел еще один странный случай, который я вспомнил (некоторые старые системы, такие как PDP-10, имели хранилище переменной длины, а байт мог быть где угодно от 1-36 бит (в частности, у него было 36 -битовое слово, и любое количество смежных битов в одном слове может быть доступно как байт)). - person Justin Time - Reinstate Monica; 27.11.2018

У каждого процесса в программе есть определенный идентификатор процесса. Вызывая pid, мы узнаем назначенный идентификатор текущего процесса. Знание pid исключительно важно, когда мы используем fork(), потому что он возвращает значения 0 и !=0 для дочерних и родительских копий в ответ. Эти два видео имеют четкие объяснения: видео № 1 Видео № 2

Пример: Предположим, у нас есть следующая программа на c:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>


int main (int argc, char *argv[])
{

  printf("I am %d\n", (int) getpid());
  pid_t pid = fork();
  printf("fork returned: %d\n", (int) pid);
  if(pid<0){
    perror("fork failed");
  }
  if (pid==0){
    printf("This is a child with pid %d\n",(int) getpid());
  }else if(pid >0){
    printf("This is a parent with pid %d\n",(int)getpid());
  }

  return 0;
}

Если вы запустите его, вы получите 0 для дочернего элемента и не zero/greater than zero для родительского.

person Community    schedule 16.07.2016

Следует отметить, что в большинстве ответов я видел что-то вроде "использование pid_t заставляет код работать в разных системах", что не обязательно верно.

Я считаю, что точная формулировка должна быть такой: это заставляет код «компилироваться» в разных системах.

Как, например, компиляция кода в системе, которая использует 32-битный pid_t, создаст двоичный файл, который вероятно сломается, если будет запущен в другой системе, использующей 64-битный pid_t.

person 0xcurb    schedule 14.01.2018