Бесполезные броски указателя в C

NullPointerException s - исключения, возникающие при попытке использовать ссылку, которая указывает на отсутствие местоположения в памяти (null), как если бы она ссылалась на объект. Вызов метода по нулевой ссылке или попытка получить доступ к полю нулевой ссылки вызовет функцию NullPointerException. Они наиболее распространены, но другие способы перечислены на странице NullPointerException javadoc.

Вероятно, самый быстрый пример кода, который я мог бы придумать для иллюстрации NullPointerException, be:

public class Example {

    public static void main(String[] args) {
        Object obj = null;
        obj.hashCode();
    }

}

В первой строке внутри main я явно устанавливаю ссылку Object obj равной null. Это означает, что у меня есть ссылка, но она не указывает на какой-либо объект. После этого я пытаюсь обработать ссылку так, как если бы она указывала на объект, вызывая метод на нем. Это приводит к NullPointerException, потому что нет кода для выполнения в местоположении, на которое указывает ссылка.

(Это техничность, но я думаю, что она упоминает: ссылка, которая указывает на null, равна 't то же, что и указатель C, указывающий на недопустимую ячейку памяти. Нулевой указатель буквально не указывает на в любом месте , который отличается от указаний на местоположение, которое оказывается недопустимым.)

16
задан Community 23 May 2017 в 12:02
поделиться

12 ответов

Это кажется установкой, я отправляю ответ, так как я оставил комментарий: P

В основном, если Вы забываете включать stdlib.h компилятор, примет malloc возвраты int. Без кастинга Вы получите предупреждение. С кастингом Вас не будет.

Так путем кастинга Вас ничего не получают и рискуют подавлять законные предупреждения.

Много записано об этом, быстрый поиск Google поднимет более подробные объяснения.

редактирование

утверждалось, что

TYPE * p;
p = (TYPE *)malloc(n*sizeof(TYPE));

делает его очевидным, когда Вы случайно не выделяете достаточно памяти, потому что говорят, Вы думали p, был TYPe не TYPE, и таким образом мы должны бросить malloc, потому что преимущество этого метода переопределяет меньшую стоимость случайного подавления предупреждений компилятора.

я хотел бы указать на 2 вещи:

  1. необходимо записать p = malloc(sizeof(*p)*n);, чтобы всегда гарантировать Вам malloc правильная сумма пространства
  2. с вышеупомянутым подходом, необходимо внести изменения в 3 местах, если Вы когда-либо изменяете тип p: однажды в объявлении, однажды в эти malloc, и однажды в броске.

Короче говоря, я все еще лично полагаю, что нет никакой потребности в кастинге возвращаемого значения [1 110], и это - конечно, не лучшая практика.

17
ответ дан 30 November 2019 в 16:01
поделиться

Этот вопрос отмечен и для C и для C++, таким образом, это имеет по крайней мере два ответа, по моему скромному мнению:

C

Гм... Сделайте то, что Вы хотите.

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

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

, Если Вы хотите иметь безопасность типов, можно или переключиться на C++ или записать собственную функцию обертки, как:

int * malloc_Int(size_t p_iSize) /* number of ints wanted */
{
   return malloc(sizeof(int) * p_iSize) ;
}

C++

Иногда, даже в C++, необходимо получить прибыль malloc/realloc/free utils. Тогда необходимо будет бросить. Но Вы уже знали это. Используя static_cast< > (), будет лучше, как всегда, чем бросок C-стиля.

И в C, Вы могли переопределить malloc (и перевыделение, и т.д.) через шаблоны для достижения безопасности типов:

template <typename T>
T * myMalloc(const size_t p_iSize)
{
 return static_cast<T *>(malloc(sizeof(T) * p_iSize)) ;
}

, Который использовался бы как:

int * p = myMalloc<int>(25) ;
free(p) ;

MyStruct * p2 = myMalloc<MyStruct>(12) ;
free(p2) ;

и следующий код:

// error: cannot convert ‘int*’ to ‘short int*’ in initialization
short * p = myMalloc<int>(25) ;
free(p) ;

не скомпилирует, таким образом, никакой problemo.

, В целом, в чистом C++, у Вас теперь нет оправдания, если кто-то находит больше чем один C malloc в Вашем коде...:-)

C + перекрестное соединение C++

Иногда, Вы хотите произвести код, который скомпилирует и в C и в C++ (по любым причинам... Разве это не точка C++ extern "C" {} блок?). В этом случае C++ требует броска, но C не поймет static_cast ключевого слова, таким образом, решением будет бросок C-стиля (который все еще законен в C++ для точно этого вида причин).

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

Так, чтобы быть на безопасной стороне, пишут код, который скомпилирует чисто в C++, изучить и исправить предупреждения, и затем использовать компилятор C для создания заключительного двоичного файла. Это означает, снова, запишите броску в броске C-стиля.

11
ответ дан 30 November 2019 в 16:01
поделиться

Одна возможная ошибка, которую это может представить, состоит в том, если Вы компилируете в 64-разрядной системе с помощью C (не C++).

В основном, если Вы забываете включать stdlib.h, международное правило по умолчанию будет применяться. Таким образом компилятор счастливо предположит, что malloc имеет прототип int malloc(); Во Многих 64-разрядных системах, интервал составляет 32 бита, и указатель составляет 64 бита.

Мм, о, значение становится усеченным, и Вы только получаете более низкие 32 бита указателя! Теперь при кастинге возвращаемого значения malloc эта ошибка скрыта броском. Но если Вы не сделаете то Вы получите ошибку (что-то к природе "не может преобразовать интервал в T *").

Это не относится к C++, конечно, по 2 причинам. Во-первых, это не имеет никакого международного правила по умолчанию, во-вторых, это требует броска.

, В целом, хотя, Вы должны просто новый в коде C++ так или иначе:-P.

7
ответ дан 30 November 2019 в 16:01
поделиться

Ну, я думаю, что это - полная противоположность - всегда непосредственно бросает его к необходимому типу. продолжает читать здесь!

6
ответ дан 30 November 2019 в 16:01
поделиться

"Забыл, что stdlib.h" аргумент является соломенным человеком. Современные компиляторы обнаружат и предупредят о проблеме (gcc - Стена).

необходимо всегда бросать результат malloc сразу. Не выполнение так нужно считать ошибкой, и не только, потому что оно перестанет работать как C++. При предназначении для архитектуры машины с различными видами указателей, например, Вы могли бы волновать с очень хитрой ошибкой, если Вы не вставляете бросок.

Редактирование : комментатор Evan Teran корректен. Моя ошибка думала, что компилятор не должен был делать никакой работы над пустым указателем ни в каком контексте. Я бешусь, когда я думаю об ошибках указателя FAR, таким образом, моя интуиция должна бросить все. Спасибо Evan!

2
ответ дан 30 November 2019 в 16:01
поделиться

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

int int_array[10];
/* initialize array */
int *p = &(int_array[3]);
short *sp = (short *)p;
short my_val = *sp;

в этом случае преобразование в короткий отбросило бы некоторые данные из интервала И затем этого случая:

struct {
    /* something */
} my_struct[100];

int my_int_array[100];
/* initialize array */
struct my_struct *p = &(my_int_array[99]);

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

, Но в целом, и если Вы знаете то, что Вы делаете, нормально делать кастинг. Еще больше, когда Вы получаете память от malloc, который, оказывается, возвращает пустой указатель, который Вы не можете использовать вообще, если Вы не бросите его, и большинство компиляторов предупредит Вас, если Вы бросите к чему-то, что lvalue (значение к левой стороне присвоения) не может взять так или иначе.

1
ответ дан 30 November 2019 в 16:01
поделиться
#if CPLUSPLUS
#define MALLOC_CAST(T) (T)
#else
#define MALLOC_CAST(T)
#endif
...
int * p;
p = MALLOC_CAST(int *) malloc(sizeof(int) * n);

или, поочередно

#if CPLUSPLUS
#define MYMALLOC(T, N) static_cast<T*>(malloc(sizeof(T) * N))
#else
#define MYMALLOC(T, N) malloc(sizeof(T) * N)
#endif
...
int * p;
p = MYMALLOC(int, n);
1
ответ дан 30 November 2019 в 16:01
поделиться

Кастинг функции, которая возвращается (пусто *), чтобы вместо этого быть (интервал *) безопасен: Вы бросаете один тип указателя на другого.

Кастинг функции, которая возвращает целое число, чтобы вместо этого быть указателем, является, скорее всего, неправильным. Компилятор отметил бы его, имел, Вы не явно бросаете его.

0
ответ дан 30 November 2019 в 16:01
поделиться

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

int *temp = (int *)malloc(sizeof(double));

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

0
ответ дан 30 November 2019 в 16:01
поделиться

Я думаю, что необходимо вставить бросок. Полагайте, что существует три места для типов:

T1 *p;
p = (T2*) malloc(sizeof(T3));

две строки кода могли бы быть широко разделены. Поэтому хорошо, что компилятор будет осуществлять это T1 == T2. Легче визуально проверить это T2 == T3.

, Если Вы пропускаете бросок T2, тогда необходимо надеяться это T1 == T3.

, С другой стороны, у Вас есть пропавшие без вести stdlib.h аргумент - но я думаю, что это, менее вероятно, будет проблема.

0
ответ дан 30 November 2019 в 16:01
поделиться

С другой стороны, если когда-нибудь необходимо портировать код на C++, намного лучше использовать 'новый' оператор.

-1
ответ дан 30 November 2019 в 16:01
поделиться

Люди уже цитировали причины, которые я обычно приводил: старый (уже не применимый к большинству компиляторов) аргумент об отказе от включения stdlib.h и использовании sizeof * p для создания убедитесь, что типы и размеры всегда совпадают независимо от последующего обновления. Я хочу указать еще на один аргумент против кастинга. Это небольшая проблема, но я думаю, что она применима.

C довольно слабо типизирован. Большинство безопасных преобразований типов происходит автоматически, а для большинства небезопасных требуется приведение типов. Подумайте:

int from_f(float f)
{
    return *(int *)&f;
}

Это опасный код. Технически это неопределенное поведение, хотя на практике оно будет делать то же самое почти на каждой платформе, на которой вы его запускаете. И приведение помогает сказать вам «Этот код - ужасный взлом».

Подумайте:

int *p = (int *)malloc(sizeof(int) * 10);

Я вижу состав, и мне интересно: «Зачем это необходимо? Где взлом?» У меня волосы на шее встают дыбом от того, что происходит что-то злое, хотя на самом деле код совершенно безвреден.

Пока мы используем C, приведение типов (особенно приведения указателей) - это способ сказать: «Здесь происходит что-то злое и легко разрушаемое». Они могут выполнить то, что вам нужно, но они указывают вам и будущим сопровождающим, что с детьми не все в порядке.

Использование приведения типов для каждого malloc уменьшает "взломать" индикацию приведения указателя. Это делает менее неприятным видеть такие вещи, как * (int *) & f; .

Примечание: C и C ++ - разные языки. C слабо типизирован, C ++ более строго типизирован.Приведения необходимы в C ++, даже если они вообще не указывают на взлом, из-за (по моему скромному мнению) излишне сильной системы типов C ++. (На самом деле, этот конкретный случай - единственное место, где я считаю систему типов C ++ «слишком сильной», но я не могу придумать ни одного места, где она была бы «слишком слабой», что делает ее в целом слишком сильной на мой вкус.)

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

Если вы не можете сделать ничего из этого, ваш код не будет красивым, что бы вы ни делали, поэтому на самом деле не имеет значения, как вы решите выполнить приведение в этот момент. Мне нравится идея использования шаблонов для создания нового распределителя, возвращающего правильный тип, хотя по сути это просто новое изобретение ключевого слова new .

1
ответ дан 30 November 2019 в 16:01
поделиться