Подтвердить что ты не робот

Для цикла с указателем в C

Я не понимаю, что делает указатель в цикле for. Что делает *p в следующем цикле?

char str[128] = "Some Text";
char *p;

for (p = str; *p /*what does this mean?*/; p++)
{
    // Code
}

Я понимаю все остальное, но почему не *p как p > 3 или что-то в этом роде?
Почему это одно?
Почему это написано именно так?

4b9b3361

Ответ 1

В булевом контексте, таком как условие цикла for, каждое выражение в C имеет значение true (отличное от нуля) или false (ноль).

Вы хотите, чтобы цикл for завершился, когда он достигнет конца строки.

В C каждая строка заканчивается символом '\0', который практически равен 0. Итак, когда цикл for достигает конца строки, *p оценивается как '\0', который равен 0, который вычисляется как false, что завершает цикл for.

Ответ 2

Цикл for завершится, если все, что находится между двумя ; в инструкции, равно нулю (false). *p dereferences p и возвращает теги char, p. Согласно Деннис Ричи "C обрабатывает строки как массивы символов, условно заканчивающихся маркером". Этот маркер является нулевым символом с нулевым значением (ASCII). Итак, это для цикла:

for (p = str; *p; p++)

эквивалентно этим

for (p = str; *p != '\0'; p++)
for (p = str; *p != 0; p++)
for (p = str; p[0] != '\0'; p++)

Другое имя для нуль-завершающего символа является дозорным или согласно Дональду Кнуту "фиктивная ценность" (Art of Computer Programming, Volume 1). Ниже приведена диаграмма строки str, индексы (смещения от начала) каждого символа и значения в каждом индексе:

введите описание изображения здесь

Для полноты и после запроса в комментариях здесь находится то, что отладчик видит в блоке памяти, который занимает str:

0x00007fffffffe6a0:
  0x53 0x6f 0x6d 0x65 0x20 0x54 0x65 0x78 0x74 0x00 0x00 0x00 0x00 0x00 0x00 0x00
     S    o    m    e         T    e    x    t
  • Шестнадцатеричное значение в первой строке - это адрес (64 бит) этого блока памяти. То, что p указывает на начало цикла for.
  • Во второй строке вы видите шестнадцатеричные значения букв в строке. Вы можете увидеть таблицу ASCII здесь. Последний char в вашей строке t с шестнадцатеричным значением 0x74. После этого вы получите нулевой символ строки 0x00. Затем вы видите еще несколько нулевых символов, потому что я построил в режиме отладки и инициализировал нулевой инициализатор компилятора. Обычно вы видите мусор (по-видимому, случайные значения)
  • На третьей строке я добавил символы вашей строки для справки

Я понимаю, что на данный момент вы находитесь на стремительной кривой обучения с указателями на C, но в конце концов вы сможете сказать "I C the point"

Ответ 3

Это можно было бы переписать так:

for (p = str; *p != '\0'; p++)
{
    // Code
}

В C строка всегда должна быть завершена нулевым символом, который является таким же, как "\ 0" или 0.

Ответ 4

Давайте проанализируем его сухим, но глухим способом!

Или как D. Ritchie сказал бы: пусть это сделает с силой языка ассемблера и удобством... ассемблера.


Я попытаюсь объяснить все необходимые аспекты, ссылаясь на стандарт ISO/IEC: 9899 (основное внимание) - C99. (Стиль почты мотивирован фразой Дональда Кнута "Наука - это то, что мы хорошо понимаем, чтобы объяснить компьютеру. Искусство - это все, что мы делаем".)

Прежде всего, давайте посмотрим, что именно должен делать for -loop!

Ссылаясь на ISO/IEC: 9899 6.8.5 "Итерационные утверждения"

Семантика

4 Оператор итерации приводит к тому, что оператор, который повторяется тело цикла, будет выполняться повторно , пока контрольное выражение не сравнится с 0.

Пока ничего нового я не догадываюсь, так что давайте его получим:

6.8.5.3 Оператор for

1 Утверждение for ( clause-1 ; expression-2 ; expression-3 ) statement

ведет себя следующим образом: выражение выражение-2 является управляющим выражением, которое оценивается перед каждым выполнением тела цикла....

Итак, теперь мы знаем, что тело (в вашем случае // Code) будет выполняться так долго, как предварительно оцененное значение вашего *p не равно нулю.

... Выражение выражение-3 оценивается как выражение void после каждого выполнения тела цикла. [...]

Итак, теперь мы знаем (я полагаю, что определение p++ не требуется?!), что для каждой итерации p увеличивается, поэтому может быть изменение в *p.

Следующий пункт не связан, но я добавляю его, так как это делает семантическую часть for полной и ее хорошо знать, так как причина, почему for(;;) является inf-loop.

2 (---) Оба предложения-1 и выражение-3 могут быть опущены. Пропущенное выражение-2 заменяется ненулевой константой.

Хорошо, что сухая, но обогащенная информацией часть того, что делает цикл for в вашем случае.

Теперь перейдем к арифметике указателя:

6.5.6 Аддитивные операторы

Ограничения

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

Итак, в вашем случае вы добавляете 1 (целое число) к типу "указатель на объект".

Что эквивалентно увеличению адреса по размеру его указательного типа, как показано на этом рисунке tomislav kostic:

CC BY-SA 3.0 от tomislav kostic

Теперь посмотрим, что на самом деле делает *p.

6.5.3.2 Операторы адреса и косвенности

Ограничения

[...]

2 Операнд унарного * оператора должен иметь тип указателя.

Семантика

[...]

4 Оператор унарного * обозначает косвенность. Если операнд указывает на функцию, результат будет обозначать функцию; если указывает на объект, результатом будет lvalue, обозначающий объект. Если операнд имеет тип '' указатель на тип, результат имеет тип ''. Если для указателя присвоено недопустимое значение, поведение унарного * оператора undefined.

Это немного сухое снова 1 но для лучшего понимания это может быть обратное проектирование:

6.5.2.1 Подстрока массива

[...]

Семантика

2 Постфиксное выражение, за которым следует выражение в квадратных скобках [], является индексированным обозначением элемента объекта массива. Определение индексного оператора [] состоит в том, что E1 [E2] идентичен (* ((E1) + (E2))).

Итак, *((p)+(0)) то, что (поскольку p+0 совпадает с p... очевидным), равно p[0], ничего не делает для оценки объекта p.

И поскольку мы знаем, что expression-2 цикла for прерывает итерацию, если она оценивает 0, мы можем сказать, что она такая же, как p[0] != 0.

Теперь последний шаг

Позволяет просто взглянуть на друга C-Coder; JSSCA... Нет, подождите... наш друг был вызван... ASCII Теперь, когда это выяснено, мы можем выяснить, что 0 представляет.

Это NULL-токен, который в C обозначает конец строки.


Итак, окончательный:

Все, что делает это:

Итерирование тела этого for -loop, пока p фактически не укажет на адрес, где объект оценивает "конец строки" -token.

Или:

Пусть p проходит строку до конца.


А теперь просто для того, чтобы ссылаться на себя; Что-то, чего вы никогда не должны забывать:
(внимание мое.....)

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

Это ни больше, ни меньше!


1 То есть, что я обещал!;)

Ответ 5

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

Когда C требует булевское значение выражения, значение false выводится, когда выражение сравнивается с ноль и значением true в противном случае. То есть, всякий раз, когда вы пишете

if(expr)

где expr - любое выражение вообще, компилятор по существу действует так, как если бы он был написан как

if((expr) != 0)  

Теперь на ваш вопрос:

Что делает *p в следующем цикле?

В C строки заканчиваются нулевым символом '\0'.

введите описание изображения здесь

Каждый символ имеет десятичный эквивалент. Этот '\0' является ASCII escape-символом. Десятичный эквивалент '\0' равен 0.

Итак, выражение *p в цикле просто проверяет, что десятичный эквивалент символа в адресе памяти, указанном p, является либо нулевым, либо ненулевым. Когда p достигает конца строки и находит первый символ '\0', выражение *p возвращает 1 нулевое значение. Нуль означает false в C. Это эквивалентно тестированию *p != '\0' или *p != 0, как указано выше.

Вот как это работает:

введите описание изображения здесь


1 Когда *p оценивается, тогда значение *p извлекается из памяти. Это значение является значением выражения *p.

Ответ 6

The * p Haiku

Поэтично я попытался представить борьбу в * p в цикле:

Храбрый C * p (rogrammers)

В петле безвкусности

NUL остановит их

Это стихотворение хайку, оно состоит из трех строк, причем первая и последняя строки имеют 5 слогов, а средняя строка имеет 7. Другой пример @Samidamaru (Поэма Учителя Хайку, см. комментарий ниже): First p равно str, Затем p увеличивается, до * p - NUL.


Немного поп

введите описание изображения здесь

Час посла Кода, Джессика Альба


Что делает * p в цикле?

Следуя мнимому совету Джессики (который цитирует Д. Кнута (1)), мы попытаемся увидеть значение * p в цикле for:

for (p = str; *p; p++)

Для этой цели мы сначала рассмотрим, как работает унарный оператор "*" в C: "Унарный оператор * является оператором косвенности или отсрочки, и когда применяется к указателю, он обращается к объекту, на который указывает указатель". (Б. Керниган и Д. Ричи (2))

So * p - это просто значение, обозначенное p:

введите описание изображения здесь

1.1 Более пристальный взгляд на цикл for

Цикл for состоит из трех команд:

  • p = str
  • * р
  • р ++

В 1. мы назначаем указатель на массив str на p. В C следующие назначения имеют тот же эффект:

p = &str[0];
p = str; 

"По определению значение переменной или выражения массива типа является адресом элемента нуль массива" (K и R (2)). Кроме того, мы имеем: "При оценке a [i] C немедленно преобразует его в * (a + i)...... следует, что & a [i] и a + я идентичны" (K и R (2)), Если положить я = 0, мы получим указанные выше задания.

Теперь мы можем указать, что в начале цикла for p указывает на первый элемент str.

1.2 Ядро вопроса

Перейдем к точке 2., суть вашего вопроса. Второе выражение цикла управляет условием выхода: вычисляется команда "* p", а если ложь - выход цикла. Это означает, что "* p" эквивалентно "* p!= 0" или в словах: когда значение, указанное p, равно нулю, выйдите.

Теперь, чтобы понять, когда * p равно нулю, напомним, что массив str был инициализирован следующим образом:

char str[128] = "Some Text";

и: "все строковые константы содержат символ нулевой остановки (\ 0) в качестве последнего символа" (gnu-manual). Таким образом, строка, фактически сохраненная в памяти, имеет \0 в конце: "Some Text\0".

В третьей инструкции p ++ указатель p переводится в следующий элемент массива str, поэтому на 9-й итерации * p становится 0 (или\0, NULL, NUL, см. ответ от @Joe), и петля завершается.

1.3 Посмотрите, чтобы верить

Изображение стоит тысячи слов, вот графическое представление цикла:

введите описание изображения здесь

1.4 Еще один пример: такое же использование * p в другом примере

В следующем фрагменте * p используется так же, но в цикле while:

#include <stdio.h>
int main() {
    char str[128] = "We all scream for ice cream!";
    char *p = str;
    // here we see again the loop exit condition *p == '\0'
    while(*p) {
        printf("%c", *p);
        p++;
    }
    printf("\n");
}

Пусть for (; * C;) e будет с вами!


Ссылки

(1) Vol. I, Фундаментальные алгоритмы, раздел 1.1 (1968)

(2) Язык программирования C Pg 94-99

Ответ 7

Он использует тот факт, что терминатор для строки (в конечном итоге найденный для этого цикла) будет ASCII NUL, который равен нулю, что также происходит для вычисления false, что завершает цикл for.

Стоит отметить разницу и сходство между 0, ложными, NULL и ASCII NUL. См. Этот вопрос: В чем разница между NULL, '\ 0' и 0

Ответ 8

Давным-давно, в PDP далеко, далеко не хватало ресурсов, имена были короткими: i для индекса, p для указателя были бы ранними программистами Jedi.

Неявные тесты сказали правду в пространстве условий for. Единственное * было все, что они набрали, доверяя p и нажав его до конца строк.

По сей день они используют for(e = s;*e;e++) самый знакомый и элегантный цикл, чтобы бросить вызов империи С++ и ее когортам ctors, dtors и мерзким итераторам. Голые биты и байты против шаблонов, исключений и неясных типов, только храбрый все еще дерзает за C, чтобы сражаться, и отключил void *.

Ответ 9

Я пытался удовлетворить пожелания лауреатов премии, которые упоминались в разное время. Чтобы это было просто, я ограничил свой ответ тремя разделами по три строки каждый, и потому что (как "Беллман" сказал в своем "Правиле из трех" ) То, что я вам говорю три раза, верно "(тема этого ответа).

Технический

Истина вашего цикла for завершает его, когда выражение *p оценивается как 0, и эта оценка выполняется перед каждой итерацией цикла, обратите внимание, что в C 0 ложно и что-то еще истинно - это очень экспансивное определение в других мирах!

Переменная указателя p инициализируется один раз, указывая на начало массива с p = str, а p увеличивается в конце каждой итерации, поэтому *p обращается к последовательным элементам массива на каждой итерации.

Таким образом, выражение *p будет оцениваться как 0 (false), когда элемент массива, который читается *p, является терминатором 0 или '\0', который сигнализирует конец строки C ", но вы не может видеть этот нуль в инициализации str, потому что он предоставляется компилятором автоматически.

Лирическая

Выражения истины

Не поняты молодежью

Прочитайте Ричи и Кнут

Причудливая

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

"Каждые пять лет я чувствую, что я совершенно другой человек".

"Все о вашем продукте и его действиях. Либо он работает, либо это не так."

Ответ 10

Хайку:

WHY   for (p=str; *p;        p++)
IS    for (p=str; p[0] != 0; p++)
THINK for (i=0;   str[i];    ++i)

EDITED

Вот несколько дополнительных деталей:

Вторая строка кода "хайку" эквивалентна первой строке. В исходном сообщении задается вопрос "что это значит" в комментарии к коду. Вторая строка демонстрирует ответ на эквивалентность. * p означает p [0]. Второе предложение в цикле для заботится о том, эквивалентно ли p [0] нулю.

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

Ответ 11

String in str

Как видно из рисунка, цикл for начинается с *p, где p указывает str. На этом этапе *p имеет S.

При непрерывном цикле for он, наконец, достигает str[9], который имеет '\0', что означает NULL.

В этот момент оператор условия *p в for (p = str; *p; p++) равен NULL, поэтому код будет прерываться из цикла for.

Ответ 12

Это условие является частью цикла.
Если это условие не выполняется, цикл больше не выполняется.
*p разделяет указатель p и возвращает символ, указанный в строке str.
Строка стиля C str заканчивается значением \0.
Цикл выполняет итерацию по каждому символу (используя p), пока условие не будет выполнено.

В C значение 0 или \0 похоже на значение false, то есть условие не выполняется.
Любое другое значение похоже на значение true, то есть условие выполнено.

Короче говоря, p выполняет итерацию по каждому символу в str и останавливается, как только он нажимает символ окончания строки \0.

Почему бы не использовать p вместо *p?
Потому что p является указателем и содержит адрес. Иногда бывает сложно или даже невозможно использовать адресную арифметику. Это не хорошая практика и делает код трудным для чтения.
*p - это разыменованный указатель и содержит значение, на которое указывает p. В этом случае легко использовать значения, на которые указывает p, потому что вы знаете, что строка завершается символом \0. В качестве условия (if, while и т.д.) *p эквивалентно *p != '\0'.

Ответ 13

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

    int var=0;
    int *p;
    int p=&var;

в этом коде p является указателем, а printf("%d",p); печатает адрес переменной var и printf("%d",*p); печатает значение переменной var, которое в этом примере равно 0.

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

        int array[3]={9,8,7};
        printf("%d",array[0]); //prints what is on 1st position,9
        printf("%d",array[1]); //prints what is on 2nd position,8
        printf("%d",array[2]); //prints what is on 3rd position,7

operator [] - это просто удобная работа с массивами. Последние три строки кода могут быть заменены следующими строками (и они будут делать то же самое):

        printf("%d",*(array+0)); //prints what is on 1st position,9 
        printf("%d",*(array+1)); //prints what is on 2nd position,8
        printf("%d",*(array+2)); //prints what is on 3rd position,7

array является указателем на первый элемент массива (содержит адрес первого элемента в массиве), поэтому разыменовывая его, мы получаем значение первого элемента, например. *array. Мы знаем, что массивы seqential, что означает, что array+1 указывает на второй элемент массива, поэтому разыменовывая это, вы получаете значение второго элемента, например. *(array+1) и т.д. сегмент памяти массива

То же самое также подходит для строк, потому что они представляют собой массив из char, за исключением того, что строка имеет "\ 0" (нулевой символ) в конце строк.

    char str[128] = "Some Text";
    char *p;

    for (p = str; *p; p++)
    {
        printf("%c",*p);
    }

Эта программа печатает строку str.

p = str//присваиваем адрес первого символа строки str p, мы не потеряем трек первого char в строке, поэтому используем p not str для итерации

*p//это выражение означает *p!=0, поэтому это верно, пока вы не дойдете до конца строки, помните, что "0" в ascii имеет целочисленное значение 48

p++//в конце для блока вы добавляете +1 к p, чтобы получить адрес следующего char

Ответ 14

Это можно объяснить следующим образом:

for( initialization ; Conditional Expression ; expression3)
{
    Code here will execute while 2nd Expression(Conditional Expression) is true
    true means non-zero value
    '\0' is equivelant to 0,so when *p equal '\0' : loop will terminate
}