Указатели и массивы в c, в чем разница?

Я изучаю указатели. Я не понимаю, в чем разница между переменными char.

#include <stdio.h>

int main() {
    char *cards = "JQK";
    char cards2[] = "JQK";

    printf("%c\n", cards[2]);
    printf("%c\n", cards2[2]);
}

Я экспериментировал с ними в printf (), и, похоже, они работают одинаково, за исключением того, что карты card2 [] не могут быть уволены, в то время как карты могут. Почему это так?

вы можете найти это полезным: c-faq.com/aryptr/index.html

yano 15.03.2018 06:15
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
99
1

Ответы 1

Во-вторых, разница заключается в том, что объект массива инициализируется содержимым строкового литерала. Первый - это char*, который содержит адрес строкового литерала. Строковые литералы представляют собой массив - этот массив преобразуется в указатель на первый элемент, а затем присваивается char*.

Дело в том, что массивы не являются изменяемым lvalue - оно не может отображаться в левой части оператора присваивания =. Указатель может (без маркировки const) может. И поскольку указатель указывает на строковый литерал, вам не следует пытаться его изменить. Это вызовет неопределенное поведение.

Указатель и массивы - это не одно и то же - в большинстве случаев массивы превращаются в указатель. Здесь также произошло то же самое с этими строковыми литералами, когда они используются в правой части присваивания в случае инициализации указателя. Второй вариант отличается тем, что явно упоминается, что это скопирует содержимое строкового литерала в объявленный массив - вот почему это можно изменить, в отличие от предыдущего случая (здесь cards2).

Чтобы немного уточнить - сначала давайте узнаем, что происходит и в чем разница между массивом и указателем?

Здесь я сказал, что строковые литералы - это массивы. Из §6.4.5¶6

In translation phase 7, a byte or code of value zero is appended to each multibyte character sequence that results from a string literal or literals.78) The multibyte character sequence is then used to initialize an array of static storage duration and length just sufficient to contain the sequence. For character string literals, the array elements have type char, and are initialized with the individual bytes of the multibyte character sequence. For UTF-8 string literals, the array elements have type char, and are initialized with the characters of the multibyte character sequence, as encoded in UTF-8.

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

char *cards = "JQK";

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

Теперь перейдем к концепции распада массива. Большинство массивов case преобразуется в указатели на его первый элемент. Сначала это может показаться странным, но вот что происходит. Например,

int a[] = {1,2,3};

Теперь, если вы напишете a[i], это эквивалентно *(a+i), а a - это устаревший указатель на первый элемент массива. Теперь вы просите перейти к позиции a+i и указать значение, которое находится в этом адресе, то есть 3 для i=2. Здесь произошло то же самое.

Указатель на первый элемент массива литералов присваивается cards. Что случилось потом? cards указывает на первый элемент массива литералов, которым является J.

История на этом не заканчивается. Теперь стандарт наложил ограничение на строковый литерал. Вы не должны изменять его - если вы попытаетесь это сделать, это будет неопределенное поведение - неопределенное поведение, как следует из названия, не определено стандартом. Вам следует избегать этого. Так что это значит? Вы не должны пытаться изменить строковый литерал или то, на что указывает cards. Большинство реализаций помещают строковые литералы в раздел только для чтения, поэтому попытка записи в него будет ошибочной.

При этом, что произошло во втором случае? Единственное - на этот раз мы говорим, что

char cards2[] = "JQK";

Теперь cards2 - это объект массива - справа от оператора присваивания снова стоит строковый литерал. Что случится? От §6.7.9¶14

An array of character type may be initialized by a character string literal or UTF-8 string literal, optionally enclosed in braces. Successive bytes of the string literal (including the terminating null character if there is room or if the array is of unknown size) initialize the elements of the array.

Дело в том, что теперь это означает, что - вы можете поместить строковый литерал справа от присваивания. Массив char будет инициализирован. Вот что и делается. И это можно изменить. Таким образом, вы можете изменить его в отличие от предыдущего случая. Это ключевое различие.

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

Из §6.3.2.1¶3

Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type 'array of type' is converted to an expression with type 'pointer to type' that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

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

Пожалуйста, сэр, дайте мне знать о моих ошибках. Помогает. Пожалуйста.

user2736738 15.03.2018 07:25

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

Steve Summit 15.03.2018 08:41

@SteveSummit: Я принял вызов. Надеюсь, сейчас все не так уж плохо. Не самый лучший, как здесь отвечает большинство людей, но да, возможно, OP получит некоторую помощь.

user2736738 15.03.2018 10:19

Другие вопросы по теме