Присвоение строки указателю на символ допустимо, но присвоение целого числа указателю на int недопустимо в C. Почему? - PullRequest
0 голосов
/ 12 февраля 2020

Символьному указателю может быть назначена произвольная строка, но целочисленному указателю нельзя назначить целое число. Так как они оба указатели и содержат адрес. Почему присвоение строки допустимо, но целочисленное значение недействительно в C указателю перед динамическим выделением c.

#include<stdio.h>

int main()
{
        char *s = "sample_string"; // valid
        printf("%s\n", s);
        int *p = (int)5; // invalid
        printf("%d\n", *p);
        return 0;
}

, которое дает вывод:

sample_string
Segmentation fault (core dumped)

В чем причина Это? Хотя оба они недействительны в C ++.

Ответы [ 5 ]

5 голосов
/ 12 февраля 2020

В C нет «типа строки». «Строка», по определению C, представляет собой массив char с нулевым байтом в конце.

Тип "sample_string" равен char[14], который может быть назначен указателю .

Тип (int)5 - int, который не может [1] .

Ошибка сегментации возникает из-за доступа к адресу 0x00000005, что недопустимо.


[1]: Технически вы можете. Но если вы хотите разыменовать этот указатель успешно, вы должны позаботиться о том, чтобы значение адреса этого целого числа имело правильное выравнивание для типа, и ссылаются на действительный объект тип. Вот почему компиляторы генерируют предупреждение, если вы не явно приводите это целое число к типу указателя в присваивании, чтобы указать, что вы do знаете, что делаете.

2 голосов
/ 12 февраля 2020

char *s = "sample_string"; Здесь "sample_string" - строковый литерал, который является const char[] в C ++. Это неявно преобразуется в const char *. Вы получите предупреждение, так как вы назначаете его на символ *.

int *p = (int)5; Здесь 5 это просто целое число. Поскольку вы присваиваете указатель, это означает, что это недопустимое значение указателя. И, следовательно, когда на него ссылаются, вы получаете segfault.

1 голос
/ 12 февраля 2020

, но целочисленному указателю нельзя присвоить целое число.

Не совсем. В C указателю может быть присвоено целое число при определенных условиях. Тем не менее, это только устанавливает указатель на 5, а не то, что p указывает на int со значением 5. *p пытается прочитать то, что находится по адресу 5, и интерпретировать это местоположение как int. Конечно, доступ к адресу 5 недопустим и вызывает ошибку сегмента.

Даже если эти условия соблюдены (см. Ниже), это, конечно, не то, что ищет OP, который, как я предполагаю, устанавливает указатель p на укажите куда-нибудь со значением / типом 5 / int.

(int) {5} является составным составным литералом , доступным с C99. Здесь это int со значением 5, и код берет адрес этого объекта и присваивает этот адрес p.

 // int *p = (int)5;
 int *p = & ((int) {5});
 printf("%d\n", *p);  // prints 5 

Целое число может быть преобразовано в любой тип указателя. За исключением случаев, указанных ранее, результат определяется реализацией, может быть неправильно выровнен, может не указывать на объект ссылочного типа и может быть представлением прерывания. C17dr § 6.3.2.3 5

1 голос
/ 12 февраля 2020

Это просто:

  • Объект char может содержать значение char: char x = 'a';.
  • Объект int может содержать int значение: int x = 3;.
  • Объект char * может указывать на массив char: char *p = "abc";.
  • Объект int * может указывать на массив int: int *p = (int []) {1, 2, 3};.

(В этом ответе «указать на массив» означает «указать на первый элемент массива».)

В C строковый литерал, такой как "abc", фактически является массивом char, включая нулевой символ в конце. Кроме того, текст выше, (int []) {1, 2, 3}, является составным литералом, который создает массив int. Таким образом, "abc" и (int []) {1, 2, 3} являются массивами. Когда массиву присваивается указатель, реализация C автоматически преобразует его в указатель на его первый элемент. (Это преобразование происходит всякий раз, когда массив используется в любом выражении, отличном от операнда sizeof, в качестве операнда унарного & или, если это строковый литерал, в качестве инициализатора для массива.)

1 голос
/ 12 февраля 2020

Соглашение состоит в том, что строки являются массивами char (char[]), а указатель на строку указывает на первый элемент этого массива char, подобно тому, как указатель на массив всегда указывает на его первый элемент по умолчанию, то есть для массива int

 int a[10];
 int *p;
 p=&a 

указывает на первый элемент a, то есть a[0] в индексной нотации

...