Может ли указатель (адрес) быть отрицательным?

Объект response не имеет прототипа. Это должно сработать (принимая идею Райана поместить его в промежуточное ПО):

var wrapRender = function(req, res, next) {
  var _render = res.render;
  res.render = function(view, options, callback) {
    _render.call(res, "testViews/" + view, options, callback);
  };
};

Однако лучше взломать ServerResponse.prototype:

var express = require("express")
  , http = require("http")
  , response = http.ServerResponse.prototype
  , _render = response.render;

response.render = function(view, options, callback) {
  _render.call(this, "testViews/" + view, options, callback);
};
34
задан phuclv 23 May 2019 в 00:12
поделиться

11 ответов

Нет, адреса не всегда положительные - на x86_64 указатели расширены по знаку, а адресное пространство сгруппировано симметрично вокруг 0 (хотя обычно "отрицательные" адреса - это адреса ядра).

Однако этот вопрос в основном спорный, так как C определяет значение сравнений указателей < и > только между указателями, которые относятся к части одного и того же объекта или к одному, находящемуся за концом массива. Указатели на совершенно разные объекты нельзя осмысленно сравнивать, кроме как на точное равенство, по крайней мере, в стандартном C - if (p < NULL) не имеет четко определенной семантики.

Вам следует создать фиктивный объект со статической длительностью хранения и использовать его адрес в качестве неинциализированного значения:

extern char uninit_sentinel;
#define UNINITIALISED ((void *)&uninit_sentinel)

он гарантированно будет иметь единственный, уникальный адрес во всей вашей программе.

71
ответ дан 27 November 2019 в 06:28
поделиться

NULL - единственный допустимый возврат ошибки в этом случае, это верно в любое время, когда возвращается беззнаковое значение, такое как указатель. Возможно, в некоторых случаях указатели не будут достаточно большими, чтобы использовать знаковый бит в качестве бита данных, однако, поскольку указатели контролируются ОС, а не программой, я бы не стал полагаться на такое поведение.

Помните, что указатель - это в основном 32-битное значение; является ли это возможным отрицательным или всегда положительным числом - это просто вопрос интерпретации (т.е.) интерпретируется ли 32 nd бит как знаковый бит или как бит данных. Итак, если вы интерпретируете 0xFFFFFFF как число со знаком, это будет -1, если вы интерпретируете его как беззнаковое число, это будет 4294967295. Технически маловероятно, что указатель когда-либо был бы таким большим, но этот случай следует рассматривать в любом случае.

В качестве альтернативы вы можете использовать дополнительный параметр out (возвращающий NULL для всех сбоев), однако это потребует от клиентов создания и передачи значения, даже если они не Нет необходимости различать конкретные ошибки.

Другой альтернативой может быть использование механизма GetLastError / SetLastError для предоставления дополнительной информации об ошибке (это будет специфично для Windows, не знаю, проблема это или нет. ) или вместо этого генерировать исключение при ошибке.

-1
ответ дан 27 November 2019 в 06:28
поделиться

Фактически (по крайней мере, на x86) исключение NULL-указателя генерируется не только разыменованием указателя NULL, но и большим диапазоном адресов (например, первые 65 КБ). Это помогает отловить такие ошибки, как

int* x = NULL;
x[10] = 1;

. Таким образом, существует больше адресов, которые гарантированно генерируют исключение указателя NULL при разыменовании. Теперь рассмотрим этот код (сделанный компилируемым для AndreyT):

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

#define ERR_NOT_ENOUGH_MEM (int)NULL
#define ERR_NEGATIVE       (int)NULL + 1
#define ERR_NOT_DIGIT      (int)NULL + 2

char* fn(int i){
    if (i < 0)
        return (char*)ERR_NEGATIVE;
    if (i >= 10)
        return (char*)ERR_NOT_DIGIT;
    char* rez = (char*)malloc(strlen("Hello World ")+sizeof(char)*2);
    if (rez)
        sprintf(rez, "Hello World %d", i);
    return rez;
};

int main(){
    char* rez = fn(3);
    switch((int)rez){
        case ERR_NOT_ENOUGH_MEM:    printf("Not enough memory!\n"); break;
        case ERR_NEGATIVE:          printf("The parameter was negative\n"); break;
        case ERR_NOT_DIGIT:         printf("The parameter is not a digit\n"); break;
        default:                    printf("we received %s\n", rez);
    };
    return 0;
};

он может быть полезен в некоторых случаях. Он не будет работать на некоторых архитектурах Гарварда, но будет работать на архитектурах фон Неймана.

0
ответ дан 27 November 2019 в 06:28
поделиться

В чем разница между отказом и унифицированным. Если unitialized не является еще одним видом сбоя, вы, вероятно, захотите изменить интерфейс, чтобы разделить эти два условия.

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

void* func();

void* result=func();
if (result==0)
  /* handle error */
else if (result==-1)
  /* unitialized */
else
  /* initialized */

Измените это на

// sets the *a to the returned object
// *a will be null if the object has not been initialized
// returns true on success, false otherwise
int func(void** a);

void* result;
if (func(&result)){
  /* handle error */
  return;
}

/*do real stuff now*/
if (!result){
  /* initialize */
}
/* continue using the result now that it's been initialized */
1
ответ дан 27 November 2019 в 06:28
поделиться

Указатели могут быть отрицательными, так же как целое число без знака может быть отрицательным. То есть, конечно, в интерпретации с дополнением до двух вы можете интерпретировать числовое значение как отрицательное, потому что самый старший бит включен.

4
ответ дан 27 November 2019 в 06:28
поделиться

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

Однако более важно рассмотреть (в качестве примера возможного выбора реализации) случай, когда вы работаете на 32-битной платформе с 32-битным размером указателя. Любое значение, которое может быть представлено этим 32-битным значением, может быть допустимым указателем. Кроме нулевого указателя, любое значение указателя может быть допустимым указателем на объект.

Для вашего конкретного случая использования вам следует рассмотреть возможность возврата кода состояния и, возможно, использования указателя в качестве параметра функции.

20
ответ дан 27 November 2019 в 06:28
поделиться

Ответ James, вероятно, правильный, но, конечно, описывает выбор реализации, а не выбор, который вы можете сделать.

Лично я думаю, что адреса "интуитивно" беззнаковые. Нахождение указателя, который сравнивается как меньший, чем нулевой указатель, кажется неправильным. Но ~0 и -1, для одного и того же целого типа, дают одно и то же значение. Если это интуитивно беззнаковое число, то ~0 может быть более интуитивно понятным значением для специального случая - я часто использую его для ошибочных беззнаковых целых. Это не действительно отличается (ноль по умолчанию является int, поэтому ~0 - это -1, пока вы не приведете его), но это выглядит иначе.

Указатели на 32-битных системах могут использовать все 32 бита BTW, хотя -1 или ~0 - это крайне маловероятный указатель для настоящего выделения на практике. Существуют также правила, специфичные для конкретной платформы - например, в 32-битной Windows процесс может иметь только 2 ГБ адресного пространства, и существует много кода, который кодирует какой-либо флаг в верхний бит указателя (например, для балансировки флагов в сбалансированных двоичных деревьях).

0
ответ дан 27 November 2019 в 06:28
поделиться

@James, конечно, прав, но я хотел бы добавить, что указатели не всегда представляют абсолютные адреса памяти, которые теоретически всегда должны быть положительными. Указатели также представляют относительные адреса к некоторой точке в памяти, часто это указатель стека или фрейма, и они могут быть как положительными, так и отрицательными.

Поэтому лучше всего, если ваша функция будет принимать указатель на указатель в качестве параметра и заполнять этот указатель действительным значением указателя при успехе, возвращая при этом код результата от фактической функции.

0
ответ дан 27 November 2019 в 06:28
поделиться

Обычно плохой дизайн - пытаться мультиплексировать специальные значения в возвращаемое значение ... вы пытаетесь сделать слишком много с одним значением. Было бы проще вернуть «указатель успеха» через аргумент, а не возвращаемое значение. Это оставляет много неконфликтного пространства в возвращаемом значении для всех условий, которые вы хотите описать:

int SomeFunction(SomeType **p)
{
    *p = NULL;
    if (/* check for uninitialized ... */)
        return UNINITIALIZED;
    if (/* check for failure ... */)
        return FAILURE;

    *p = yourValue;
    return SUCCESS;
}

Вам также следует выполнить обычную проверку аргументов (убедитесь, что 'p' не равно NULL).

17
ответ дан 27 November 2019 в 06:28
поделиться

В языке C не определено понятие «отрицательность» для указателей. Свойство «быть отрицательным» является главным образом арифметическим и никоим образом не применимо к значениям типа указателя.

Если у вас есть функция, возвращающая указатель, вы не можете осмысленно вернуть значение -1 из этой функции. В языке C целочисленные значения (отличные от нуля) не могут быть неявно преобразованы в типы указателей. Попытка вернуть -1 из функции, возвращающей указатель, является немедленным нарушением ограничения, которое приведет к диагностическому сообщению. Короче, это ошибка. Если ваш компилятор позволяет это, это просто означает, что он не слишком строго применяет это ограничение (большую часть времени они делают это для совместимости с предстандартным кодом).

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

Если вы хотите создать зарезервированное значение указателя, не нужно ничего malloc . Вы можете просто объявить глобальную переменную желаемого типа и использовать ее адрес в качестве зарезервированного значения. Гарантия уникальности.

5
ответ дан 27 November 2019 в 06:28
поделиться

Не используйте для этой цели malloc . Это может удерживать ненужную память (например, если много памяти уже используется, когда вызывается malloc и дозор выделяется по высокому адресу), и это сбивает с толку отладчики памяти / детекторы утечек. Вместо этого просто верните указатель на локальный объект static const char . Этот указатель никогда не будет сравниваться с любым указателем, который программа могла бы получить каким-либо другим способом, и он тратит только один байт bss.

0
ответ дан 27 November 2019 в 06:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: