Указатель и обычная переменная с тем же именем - PullRequest
3 голосов
/ 07 февраля 2020

В главе «Память и указатели» книги «Встроенное C программирование» Марк Сигесмунд приводит следующий пример:

void Find_Min_Max( int list[], int count, int * min, int * max){
    for( int i=0, min=255, max=0; i<count; i++){
        if( *min>list[i])
            *min=list[i];
        if( *max<list[i] )
            *max=list[i];
    }
}
// call like this: Find_Min_Max( table, sizeof(table), &lowest, &highest);

Если я правильно понимаю:

  • таблица - это массив int,
  • count - это размер массива
  • при вызове, а наименьший и & наибольший адреса - это переменные типа int, в которых нужно сохранить результаты
  • int * min и int * max в определении функции относятся к указателям * min и * max, оба к целочисленным типам
  • , причем наименьший и самый высокий, как определено в его последней строке, на самом деле являются указателями на адрес с переменная типа int

(Не уверен на 100% в этом последнем.)

Внутри для l oop он каждый раз сравнивает следующее int в списке массивов с указателями * min и * max адресов и при необходимости обновляет значения по этим адресам.

Но в определении l oop он определяет min = 255, max = 0.

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

for( int i=0, *min=255, *max=0: i<count; i++){

Это ошибка в книге или что-то, что я неправильно понимаю?

Ответы [ 3 ]

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

Это похоже на ошибку в книге - она ​​действительно объявляет новые переменные внутри l oop. (Подсказка: прежде чем публиковать книги по программированию, хотя бы сначала скомпилируйте код ...)

Но даже с исправленной ошибкой код написан наивно. Вот еще ошибки:

  • Всегда const квалифицировать параметры массива, которые не изменены.
  • Всегда использовать stdint.h во встроенных системах.
  • Никогда не использовать маги c цифры, такие как 255. В этом случае используйте UINT8_MAX.

Выше приведен консенсус отраслевого стандарта. (Также требуется MISRA- C et c.)

Кроме того, наиболее правильным является использование size_t вместо int для размера массивов, но это больше проблема стиля примечание.

Кроме того, лучший алгоритм состоит в том, чтобы указатели указывали на минимальное и максимальное значения, найденные в массиве, а это означает, что мы получаем не только значения, но и их расположение в контейнере данных. Поиск местоположения является очень распространенным вариантом использования. Это примерно та же скорость выполнения, но мы получаем больше информации.


Так что, если мы попытаемся переписать это в какой-нибудь код, достойный книги, это будет выглядеть примерно так:

void find_min_max (const uint8_t* data, size_t size, const uint8_t** min, const uint8_t** max);

Немного сложнее для чтения и использования с указателями на указатели, но более мощный.

(Обычно мы бы микрооптимизировали указатели одного типа с restrict, но в этом случае все указатели могут в конечном итоге указывать на один и тот же объект, поэтому это невозможно.)

Полный пример:

#include <stddef.h>
#include <stdint.h>

void find_min_max (const uint8_t* data, size_t size, const uint8_t** min, const uint8_t** max)
{
  *min = data;
  *max = data;

  for(size_t i=0; i<size; i++)
  {
    if(**min > data[i])
    {
      *min = &data[i];
    }
    if(**max < data[i])
    {
      *max = &data[i];
    }
  }
}

Пример использования для P C: (обратите внимание, что int main (void) и stdio.h не должны использоваться во встроенных системах.)

#include <stdio.h>
#include <inttypes.h>

int main (void)
{
  const uint8_t data[] = { 1, 2, 3, 4, 5, 4, 3, 2, 1, 0};
  const uint8_t* min;
  const uint8_t* max;

  find_min_max(data, sizeof data, &min, &max);

  printf("Min: %"PRIu8 ", index: %d\n", *min, (int)(min-data));
  printf("Max: %"PRIu8 ", index: %d\n", *max, (int)(max-data));

  return 0;
}

Разборка этого алгоритма поиска для ARM g cc -O3:

find_min_max:
        cmp     r1, #0
        str     r0, [r2]
        str     r0, [r3]
        bxeq    lr
        push    {r4, lr}
        add     r1, r0, r1
.L5:
        mov     lr, r0
        ldr     ip, [r2]
        ldrb    r4, [ip]        @ zero_extendqisi2
        ldrb    ip, [r0], #1    @ zero_extendqisi2
        cmp     r4, ip
        strhi   lr, [r2]
        ldr     r4, [r3]
        ldrbhi  ip, [r0, #-1]       @ zero_extendqisi2
        ldrb    r4, [r4]        @ zero_extendqisi2
        cmp     r4, ip
        strcc   lr, [r3]
        cmp     r1, r0
        bne     .L5
        pop     {r4, pc}

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

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

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

int i=0, min=255, max=0 и int i=0, *min=255, *max=0 оба определяют три новые переменные, которые инициализируются, но неправильно используются в теле l oop.

Ограничения должны быть инициализированы до l oop:

*min=255;
*max=0;
for(int i=0; i<count; i++)

В качестве альтернативы новая переменная i может быть определена перед l oop, но это не так легко прочитать, как первое:

int i;
for(i=0, *min=255, *max=0; i<count; i++)

Обратите внимание, что если есть значения меньше 0 или больше 255, возвращенные минимальное и максимальное значения будут неправильными.

0 голосов
/ 07 февраля 2020

Это некоторая ошибка в коде. Может быть, это следует читать:

for( int i=0, *min=255, *max=0; i<count; i++){
        if( *min>list[i])
            *min=list[i];
        if( *max<list[i] )
            *max=list[i];
    }
...