Почему разрешено изменять константу с помощью указателя в C? [Дубликат]

Что такое NumberFormatException?

Это исключение указывается, что приложение попыталось преобразовать string в один из числовых типов, но string не имеет соответствующего формата.

В вашем случае, согласно вашей трассировке стека это исключение было выбрано Integer.parseInt(String) , что означает, что предоставленный String не содержит синтаксический анализ integer. И все же, согласно трассировке стека, это связано с тем, что вы пытались разобрать String « Ace of Clubs » как целое число, которое не может работать, поскольку оно не является String представлением целого числа.

Как это исправить?

Самый простой и общий способ - уловить исключение NumberFormatException

int value = -1;
try {
    value = Integer.parseInt(myString);
} catch (NumberFormatException e) {
    // The format was incorrect
}

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

Или вы можете использовать regular expression для проверки, если String matches с Integer, но он довольно подвержен ошибкам, так как вы можете легко использовать неправильный regular expression.


В вашем случае следует использовать более OO-подход вместо работы с String, для Например, вы могли бы использовать class или enum для представления ваших карт вместо простого String, потому что это гораздо больше подвержено ошибкам, как вы уже заметили.

Итак, если вы решили использовать выделенный класс для своей карты, ваш код может быть:

public class Card {

    private final Rank rank;
    private final Suit suit;

    public Card(final Rank rank, final Suit suit) {
        this.rank = rank;
        this.suit = suit;
    }

    public Rank getRank() {
        return this.rank;
    }

    public Suit getSuit() {
        return this.suit;
    }
}

Для костюма и ранга карты мы можем использовать enum, поскольку существуют ограниченные количества существующих рангов и костюмов.

public enum Rank {
    ACE(1), TWO(2), THREE(3), FOUR(4), FIVE(5), SIX(6), SEVEN(7), HEIGHT(8),
    NINE(9), TEN(10), JACK(11), QUEEN(12), KING(13);

    private final int value;

    Rank(final int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }
}

public enum Suit {
    SPADE, HEART, DIAMOND, CLUB
}

Тогда cards будет массивом Card вместо массива String и может быть инициализирован следующим образом :

Rank[] ranks = Rank.values();
Suit[] suits = Suit.values();
Card[] cards = new Card[ranks.length * suits.length];
for (int i = 0; i < ranks.length; i++) {
    for (int j = 0; j < suits.length; j++) {
        cards[i * suits.length + j] = new Card(ranks[i], suits[j]);
    }
}

Если вам нужно перетасовать свой массив карт, вы можете действовать следующим образом (обратите внимание, что если вы решили использовать List карт вместо массива, просто используйте Collections.shuffle(list) )

List allCards = Arrays.asList(cards);
Collections.shuffle(allCards);
allCards.toArray(cards);

Тогда вы сможете напрямую получить доступ к значению вашей карты с помощью cards[index].getRank().getValue(), не рискуя получить исключение (кроме IndexOutOfBoundsException, если вы не используете правильный индекс).

26
задан 2501 16 October 2016 в 14:43
поделиться

10 ответов

Это «неопределенное поведение», что означает, что на основе стандарта вы не можете предсказать, что произойдет, когда вы попробуете это. Он может делать разные вещи в зависимости от конкретной машины, компилятора и состояния программы.

В этом случае чаще всего произойдет то, что ответ будет «да». Переменная const или нет - это просто место в памяти, и вы можете нарушать правила константы и просто перезаписывать ее. (Конечно, это вызовет серьезную ошибку, если какая-то другая часть программы зависит от постоянных константных данных!) [/ ​​G2]

Однако в некоторых случаях - чаще всего для данных const static - компилятора могут помещать такие переменные в область памяти, доступную только для чтения. MSVC, например, обычно ставит константные статические ints в .text сегменте исполняемого файла, что означает, что операционная система будет вызывать ошибку защиты, если вы попытаетесь записать на нее, и программа выйдет из строя.

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

30
ответ дан Crashworks 19 August 2018 в 16:13
поделиться
  • 1
    Я пробовал аналогичный идеал кодеина, и он разбился. Есть ли надежный метод для фактического изменения переменной? – Tomáš Zato 10 January 2017 в 00:31
  • 2
    Поскольку значение считается постоянным, компилятор также может оптимизировать обращения к памяти, используя значение как непосредственное значение в инструкции, а не используя дополнительную команду загрузки для извлечения из памяти. Таким образом, здесь вы можете получить много странных переменных: переменная была полностью устранена оптимизатором, использует в некоторых местах ссылку на переменную, но в других местах использует немедленную (которую ваш хак не изменил) и т. Д. (Я не уверен, насколько вероятно, что константа только частично изменена на немедленную, но я уверен, что немедленная замена возможна). – zstewart 27 February 2017 в 17:38

Плохая идея BAD.

Кроме того, поведение зависит от платформы и реализации. Если вы работаете на платформе, где константа хранится в незаписываемой памяти, это, очевидно, не сработает.

И, почему бы вы хотели этого? Либо обновите константу в своем источнике, либо сделайте ее переменной.

0
ответ дан 3Dave 19 August 2018 в 16:13
поделиться

Вы не можете изменить значение константы с помощью указателя, указывающего на него. Этот тип указателя называется Pointer to a constant.

Существует также другое понятие, называемое Constant Pointer. Это означает, что, как только указатель указывает на ячейку памяти, вы не можете указывать на другое местоположение.

0
ответ дан Abhijeet Pathak 19 August 2018 в 16:13
поделиться

Он действительно работает с gcc. Это ему не понравилось:

test.c: 6: warning: присваивание отбрасывает квалификаторы из целевого типа указателя

Но значение изменилось, когда казнены. Я не буду указывать на очевидное нет-нет ...

5
ответ дан Alexander Sagen 19 August 2018 в 16:13
поделиться

Здесь тип указателя p - int*, которому присваивается значение типа const int* (&a => адрес переменной const int).

Неявный бросок исключает константу, хотя gcc выдает предупреждение (обратите внимание, что это во многом зависит от реализации).

Поскольку указатель не объявлен как const, значение может (g3), если указатель будет объявлен как const int* p = &a, вы не сможете сделать *p = 70.

1
ответ дан Hemant 19 August 2018 в 16:13
поделиться

Это неопределенное поведение. Доказательство:

/* program.c */

int main()
{
        const int a = 12;
        int* p;
        p = &a;
        *p = 70;
        printf("%d\n", a);
        return 0;
}


gcc program.c

и запустите его. Выход будет равен 70 (gcc 4.3)

Затем скомпилируйте его следующим образом:

gcc -O2 program.c

и запустите его. Выход будет 12. Когда он выполняет оптимизацию, компилятор предположительно загружает 12 в регистр и не утруждает себя повторной загрузкой, когда ему нужно получить доступ к a для printf, потому что он «знает», что не может измениться.

13
ответ дан JeremyP 19 August 2018 в 16:13
поделиться
  • 1
    Забавно, когда я запускаю эту программу, результат равен 42. – abelenky 27 September 2010 в 17:09
  • 2
    @abelenky: да, но вы перестали думать о том, что вопрос? Может быть, вы должны построить компьютер, чтобы узнать. – JeremyP 27 September 2010 в 17:15
  • 3
    Тот факт, что gcc ведет себя странно с куском кода, будет означать, что код вызывает Undefined Behavior, если gcc оставил в силе стандарт в каждом случае. На практике, однако, gcc неспособна эффективно обрабатывать определенную семантику, необходимую для соответствующего компилятора, и вместо того, чтобы обрабатывать их таким образом, который является совместимым, но неэффективным, или фиксируя свою модель исполнения для эффективного и согласованного управления, gcc ops вместо для обработки их способом, который является «эффективным». но сломанной и несоответствующей. – supercat 17 September 2017 в 18:37
  • 4
    @supercat В этом случае gcc ведет себя в соответствии со стандартом. Использование указателя для записи в объект const - UB. Причина, по которой это UB, заключается в том, что на практике трудно обеспечить логически правильное поведение без защиты памяти уровня ОС. – JeremyP 18 September 2017 в 09:00
  • 5
    @JeremyP: В этом конкретном случае код вызывает вызов UB, и я не хотел подразумевать иначе. Я задавал общее предположение, что фрагмент кода, который не работает на gcc, вызывает UB, а не просто показывает место, где gcc не соответствует стандарту. – supercat 18 September 2017 в 14:30

да, вы можете сделать это, используя такой код. но код не применяется, когда a является глобальным (gcc-скомпилированная программа дала мне segmentation fault.)

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

Но размышление о бедном парне (может быть, мне через 6 месяцев) поддерживает наш код, я часто предпочитаю не делать этого.

3
ответ дан Jokester 19 August 2018 в 16:13
поделиться

Этот код содержит нарушение ограничения :

const int a = 12;
int *p;
p = &a;

Нарушенное ограничение - C11 6.5.16.1/1 «Простое назначение»; если оба операнда являются указателями, то тип, на который указывает левый, должен иметь все определители типа, на который указывает справа. (И типы, sans qualifiers, должны быть совместимы).

Таким образом, ограничение нарушается, потому что &a имеет тип const int *, который имеет const в качестве определителя; но этот определитель не отображается в типе p, который является int *.

Компилятор должен испустить диагностику и не может генерировать исполняемый файл. Поведение любого исполняемого файла полностью не определено, так как программа не соответствует правилам языка.

0
ответ дан M.M 19 August 2018 в 16:13
поделиться

Модификация const квалифицированного объекта через указатель вызывает неопределенное поведение, и таков результат. Скорее всего, это то, чего вы ожидаете, т.е. предыдущее значение не изменилось, если оно помещено в .text и т. д.

10
ответ дан Michael Foukarakis 19 August 2018 в 16:13
поделиться
  • 1
    Другим возможным результатом является «некоторые выражения ведут себя так, как будто a все еще равно 12, а другие ведут себя так, как будто a теперь составляет 70», что может быть или не быть «то, что вы ожидаете». – caf 27 September 2010 в 08:38

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

#include <stdio.h>

int main()
{
    const  int x=10;

    int *p;
    p=(int*)&x;
    *p=12;
    printf("%d",x);
}
-2
ответ дан Vineet Jain 19 August 2018 в 16:13
поделиться
Другие вопросы по тегам:

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