Как массив символов можно вычесть из указателя? - PullRequest
2 голосов
/ 06 февраля 2020

Итак, я начинаю знакомиться с C, и в этот момент я пытаюсь понять указатели. Я получил следующий код из здесь , но я не могу понять, как можно вычесть массив символов из указателя.

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

main() 
{   
char s[30], t[20];   
char *found; 

/* Entering the main string */   
puts("Enter the first string: ");   
gets(s);

/* Entering the string whose position or index to be displayed */   
puts("Enter the string to be searched: ");   
gets(t);

/*Searching string t in string s */   
found=strstr(s,t);   
if(found)
    printf("Second String is found in the First String at %d position.\n",found-s);    
else
    printf("-1");   
getch(); 
}

Разве указатель не является только адресом данного переменный / постоянный? Когда происходит вычитание, массив символов автоматически предполагает, что, поскольку операция происходит с указателем, вычитается ли его адрес? Я немного запутался здесь.

Заранее спасибо.

Ответы [ 4 ]

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

Если вы задаетесь вопросом о выражении found-s, то происходит то, что вы вычитаете два указателя.

Массивы естественным образом затухают до указателей на их первый элемент. Это означает, что обычный s равен &s[0], что здесь происходит: found-s равен found - (&s[0]).

И вычитание работает, потому что found указывает на элемент внутри массив s, поэтому указатели связаны (что является требованием для вычитания указателя). Результатом является разница (в элементах) между двумя указателями.

3 голосов
/ 06 февраля 2020

Я не могу понять, как можно вычесть массив символов из указателя.

Технически, это невозможно. Но это не делает представленный код недействительным.

Разве указатель не является только адресом данной переменной / константы?

Больше или меньше. Указатель - это адрес. Допустимым является адрес какого-либо объекта или функции.

Когда вычитание происходит, массив символов автоматически предполагает, что, поскольку операция происходит с указателем, вычитает его адрес?

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

За исключением случаев, когда это операнд оператора sizeof, оператора _Alignof или унарный оператор & или строковый литерал, используемый для инициализации массива, выражение с типом '' массив типа '' преобразуется в выражение с типом '' указатель на тип '', которое указывает на начальный элемент объекта массива и не является lvalue.

( C2011, 6.3.2.1/3)

Обратите внимание, что оператор индексации, [], не является исключением. Индексирование - это операция указатель . Выражения вызова функций также не являются исключениями, поэтому вы не можете фактически передать массив функции, независимо от того, насколько он выглядит, как вы это делаете - вместо этого вы в конечном итоге передаете соответствующий указатель. И, самое главное, оператор разности - не является исключением, поэтому рассматриваемый код выражает разницу между двумя указателями, а не между массивом и указателем.

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

Компилятор C знает типы, которые вы используете, и их размер, поэтому, когда вы делаете арифметику указателей c, компилятор C может сделать некоторые интеллектуальные вещи для вас.

Например, если int *a = 0x10, то a + 1 даст 0x14, а не 0x11. Компилятор знает, что размер int равен 4, поэтому, когда вы добавляете 1 к адресу указателя int, он дает вам адрес следующего объекта размера int, который будет.

Аналогично, когда вы вычитаете 2 указателя (т.е. found - s компилятор не дает вам количество байтов между двумя адресами, он дает вам количество объектов между ними, что в случае с char (размер 1) является одним и тем же.

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

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

Из C стандарта (6.5.6 Аддитивные операторы)

9 Когда вычитаются два указателя, оба должны указывать на элементы одного и того же объекта массива или один после последний элемент объекта массива; Результатом является разница индексов двух элементов массива. Размер результата определяется реализацией, а его тип (целочисленный тип со знаком) ptrdiff_t определен в заголовке. Если результат не может быть представлен в объекте этого типа, поведение не определено. Другими словами, если выражения P и Q указывают, соответственно, на i-й и j-й элементы объекта массива, выражение (P) - (Q) имеет значение i-j, если значение вписывается в объект типа ptrdiff_t. Более того, если выражение P указывает либо на элемент объекта массива, либо на один элемент после последнего элемента объекта массива, а выражение Q указывает на последний элемент того же объекта массива, выражение ((Q) +1) - (P) имеет то же значение, что и ((Q) - (P)) + 1 и - ((P) - ((Q) +1)), и имеет нулевое значение, если выражение P указывает на одну точку после последний элемент объекта массива, хотя выражение (Q) +1 не указывает на элемент объекта массива.106

Таким образом, возникает вопрос: что такое s в выражении found-s?

Стандартные ответы C (6.3.2.1 L-значения, массивы и обозначения функций)

3 За исключением случаев, когда это операнд оператора sizeof или унарный оператор &, или строковый литерал, используемый для инициализации массива, выражение, имеющее тип '' массив типа '', преобразуется в выражение с типом '' указатель на тип '', указывающее на начальный элемент объекта массива и не является lvalue. Если объект массива имеет класс хранения регистров, поведение не определено.

Таким образом, в приведенном выше выражении s преобразуется в указатель на его первый элемент, и фактически выражение может быть эквивалентно переписано для ясность вроде

found - &s[0]

Но, конечно, зная это неявное преобразование указателей массива в указатели, проще написать

found - s

Результатом выражения является количество элементов массива между двумя указателями.

Иногда новички допускают следующую ошибку, ничего не зная о таком преобразовании. Они пишут, например,

char s[] = "Hello";

if ( s == "Hello" )
{
    // ...do something
} 

Однако в условии оператора if сохраненные строки не сравниваются. Сравниваются адреса первого элемента массива s и первого элемента строкового литерала. Поскольку массив и литерал занимают разные области памяти, результат условия оценивается как ложное.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...