Перебирать массив с помощью while

Я застрял в следующем цикле while. Обновлено: Цель программы — полностью перебрать массив от начала до конца, как указано в заголовке. Для этого я попытался использовать цикл while

while(*rtn)

считается ложным, пока первое значение равно нулю (см. пример 0 ниже), поэтому программа не входит в цикл while. Если я изменю первое значение на ненулевое (пример 1), программа войдет в цикл while. Если я изменю присвоение значения так, чтобы пятый элемент был равен нулю (пример 2), цикл while остановится на пятом элементе. Цикл for работает как положено.

    size_t sz = 100 ;                                                                                                                                                                                                                                                                             
    int *rtn = malloc(sz*sizeof(int));                                                                                                                             
    int cnt ;                                                                                                                                                      
                                                                                                                                                                   
    for(cnt = 0 ; cnt < sz ; cnt++){                                                                                                                               
 EXAMPLE 0 rtn[cnt] = cnt*2 ;        // First value of array is 0                                                                                                                                     
 EXAMPLE 1 rtn[cnt] = cnt*2 + 5 ;    // first value of array is non zero                                                                                                                                     
 EXAMPLE 2 rtn[cnt] = 5 - cnt ;      // fifth value will be 0                                                                                                                                     
    }                                                                                                                                                              
                                                                                                                                                                                                                                                                                                                          
    cnt=0 ;                                                                                                                                                        
    printf("\n---WHILE LOOP---\n") ;                                                                                                                               
    while(*rtn){                                                                                                                                                   
        printf("%3d - %4d\n",cnt, *rtn) ;                                                                                                                          
        cnt++ ;                                                                                                                                                    
        rtn++ ;                                                                                                                                                    
    }                                                                                                                                                              
                                                                                                                                                                   
    printf("\n---FOR LOOP---\n") ;                                                                                                                                 
    for(cnt = 0 ; cnt < 10 ; cnt ++){                                                                                                                              
        printf("%3d - %4d\n", cnt, rtn[cnt]) ;                                                                                                                     
    }                                               
   

Я думал, что цикл while проверяет NULL-указатель, а не 0, который, очевидно, может быть допустимым значением!? Также, если я изменю цикл while на

while(rtn){
...
}

он просто пройдет всю память (?) и завершится ошибкой. Итак, по сути, цикл while не является подходящим инструментом для перебора массива int?!

Ваш вопрос действительно не имеет смысла. Вы заинтересованы в переборе каждого элемента массива или в переборе до тех пор, пока не увидите «ложное» значение (включая 0 или NULL)? Имеет смысл сделать и то, и другое. Если вы хотите перебрать каждый элемент, либо используйте цикл for, либо создайте цикл while, который проверяет, достиг ли cnt конца.

asimes 14.06.2024 19:15

В C нулевое целое число считается ложным, поэтому цикл завершится, как только встретится первый ноль в массиве.

user24714692 14.06.2024 19:17

Почему бы тогда не проверить цикл while, если указатель равен нулю? Если только прямое сравнение с NULL не рекомендуется в C.

Frasher Gray 14.06.2024 19:19

Имейте в виду, что while(*rtn) эквивалентно while (rtn[0] != 0). И что если в массиве нет 0, ваш цикл выйдет за пределы, и у вас будет неопределенное поведение.

Some programmer dude 14.06.2024 19:19

@FrasherGray Технически, если rtn изначально не является нулевым указателем, он никогда не станет нулевым указателем. Если только код явно не выполняет назначение rnt = NULL.

Some programmer dude 14.06.2024 19:20

@sake Что еще хуже, когда вы доберетесь до цикла for, у вас больше не будет исходного значения rtn. Он теряется в цикле while. Так что там вы пойдете еще дальше за пределы поля.

Some programmer dude 14.06.2024 19:21

Ох, да это мб. Насколько я понимаю, ему просто нужен способ проверить, находится ли указатель в выделенной памяти?

Frasher Gray 14.06.2024 19:22

Что касается решения проблемы, цикл for типа for (a; b; c) { d; } эквивалентен { a; while (b) { d; c; } }. Так что вы все еще можете использовать такой цикл while. Или отслеживать конец выделенной памяти, например int *end = rtn + cnt;, а затем while (rtn < end)

Some programmer dude 14.06.2024 19:23

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

chrslg 14.06.2024 19:28
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
9
91
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

#include <stdio.h>
#include <stdlib.h>

int main() {
    int sz = 5; // I made this smaller for my example
    int* rtn = malloc(sz * sizeof(int));
    int cnt;

    // Populate some random data
    rtn[0] = 3;
    rtn[1] = 2;
    rtn[2] = 0;
    rtn[3] = 1;
    rtn[4] = 4;

    // If your goal is to iterate over every element this works
    printf("For every element\n");
    for (cnt = 0; cnt < sz; cnt++) {
        printf("%d\n", rtn[cnt]);
    }

    // If your goal is to iterate over every element this works too
    printf("While every element\n");
    cnt = 0;
    while (cnt < sz) {
        printf("%d\n", rtn[cnt]);
        cnt++;
    }

    // If your goal is to iterate until you find a "false" element this does
    // that BUT it will go out of bounds if there is no "false" element
    printf("For until \"false\" element\n");
    for (cnt = 0; rtn[cnt]; cnt++) {
        printf("%d\n", rtn[cnt]);
    }

    // If your goal is to iterate until you find a "false" element this does
    // that too. It also will go out of bounds if there is no "false" element
    printf("While until \"false\" element\n");
    cnt = 0;
    while (rtn[cnt]) {
        printf("%d\n", rtn[cnt]);
        cnt++;
    }

    // Yet another way to do the exact same as the previous while loop
    printf("Another while until \"false\" element\n");
    while (*rtn) {
        printf("%d\n", *rtn);
        rtn++;
    }

    return 0;
}

спасибо, искал решение с циклом while, чтобы лучше понять арифмерику указателей

sake 14.06.2024 22:29
Ответ принят как подходящий

Ваше понимание поведения цикла while (*rtn) в C правильное. Условие цикла while (*rtn) оценивает значение, на которое указывает rtn. Если значение равно нулю, оно считается false и цикл завершается. Вот почему в ваших примерах:

  • Пример 0: первое значение равно нулю, поэтому цикл никогда не запускается.
  • Пример 1. Первое значение не равно нулю, поэтому цикл запускается и продолжается до тех пор, пока не встретится ноль.
  • Пример 2: Цикл останавливается на пятом элементе, который равен нулю.

Цикл while (*rtn) действительно проверяет значение, на которое указывает rtn, а не NULL-указатель.

Условие while (*rtn) не подходит для перебора массива, если любой элемент массива может быть равен нулю. Он останавливается, как только встречает первый ноль, который может не быть концом массива.

Чтобы перебрать весь массив, вы можете использовать цикл for или цикл while с явным счетчиком. Вот как это можно сделать с помощью цикла for:

#include <stdio.h>
#include <stdlib.h>

int main() {
    size_t sz = 100;
    int *rtn = malloc(sz * sizeof(int));
    int cnt;

    // Example 0: First value of array is 0
    for (cnt = 0; cnt < sz; cnt++) {
        rtn[cnt] = cnt * 2;
    }

    // Example 1: First value of array is non-zero
    for (cnt = 0; cnt < sz; cnt++) {
        rtn[cnt] = cnt * 2 + 5;
    }

    // Example 2: Fifth value will be 0
    for (cnt = 0; cnt < sz; cnt++) {
        rtn[cnt] = 5 - cnt;
    }

    printf("\n---WHILE LOOP---\n");
    cnt = 0;
    while (cnt < sz) {
        printf("%3d - %4d\n", cnt, rtn[cnt]);
        cnt++;
    }

    printf("\n---FOR LOOP---\n");
    for (cnt = 0; cnt < 10; cnt++) {
        printf("%3d - %4d\n", cnt, rtn[cnt]);
    }

    free(rtn);
    return 0;
}

Объяснение:

  1. Выделите память для массива rtn.
  2. Заполните массив значениями на основе ваших примеров.
  3. Используйте цикл while с явным счетчиком для перебора всего массива.
  4. Используйте цикл for для печати первых 10 элементов массива.

Используя счетчик в цикле while (while (cnt < sz)), вы гарантируете, что выполните итерацию по всему массиву, независимо от содержащихся в нем значений.

В дополнение к другим ответам - вы можете остановить цикл, создав указатель на конец массива, а затем проверив цикл, когда он достигнет конца, например:

size_t sz = 100;
int *rtn = malloc(sz*sizeof(int));
int *end = rtn + sz;

...                                                                              

printf("\n---WHILE LOOP---\n");
while (rtn < end) {
    ...
    ++rtn;
}

Кстати, вы создаете утечку памяти, изменяя указатель, который возвращает malloc(). Вам нужно free() массив, когда вы закончите его использовать. Поэтому вам следует использовать другой указатель для перебора массива, например:

int *arr = malloc(...);
...

printf("\n---WHILE LOOP---\n");
int *rtn = arr;
while (...) {
    ...
    ++rtn;
}

...

free(arr);

rtn < end — это именно то, что я искал, спасибо!

sake 14.06.2024 22:25

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