【问题标题】:Pointer and regular variable with the same name同名指针和正则变量
【发布时间】:2020-02-07 10:06:40
【问题描述】:

在“嵌入式​​C编程”一书的“内存和指针”一章中,Mark Siegesmund给出了以下示例:

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);

如果我理解正确:

  • table 是一个 int 数组,
  • count 是数组的大小
  • 调用时,&lowest 和 &highest 是要存储结果的 int 变量的地址
  • 函数定义中的int * min和int * max指的是指针* min和* max,都是整数类型
  • 最后一行中定义的 &lowest 和 &highest 实际上是指向具有 int 类型变量的地址的指针

(不是 100% 确定最后一个。)

在 for 循环中,他每次将数组列表中的下一个 int 与指针 *min 和 *max 地址处的值进行比较,并在必要时更新这些地址处的值。

但是在循环的定义中,他定义了min = 255,max = 0。

在我看来,这似乎是两个尚未初始化的全新变量。 该行不应该是

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

这是书上的错误还是我误解了什么?

【问题讨论】:

  • 不是该代码中的唯一错误:: 而不是 ;,将 minmax 参数隐藏在循环内,尝试取消引用 int 变量,假设列表可以包含不超过 255 个项目,不包括空列表,...
  • 那个 : 而不是 ;是我的一个错字(现已更正)。取消引用发生在哪里?将最小/最大参数隐藏在循环中是什么意思?
  • for(int i=0, max=1,min=2; i &lt;count; i++){} :前两个逗号是 not 逗号运算符,而是声明列表的一部分。这也是一种糟糕的风格。
  • @DieterVansteenwegenON4DD :for 循环初始化语句定义了 3 个新的 int 变量(iminmax)。后两者与周围作用域中的变量具有相同的名称,因此它们将那些隐藏在for 循环的作用域中。 IE。在for循环内使用min时,它指的是在init语句中定义的int;当在for循环外使用min时,它指的是int*参数。
  • @SanderDeDycker 这很有道理,并消除了我的一些疑虑。我无法将其标记为答案,因为它在评论中...

标签: c pointers embedded


【解决方案1】:

这似乎是书中的一个错误——它确实在循环内声明了新变量。 (提示:在出版编程书籍之前,至少要先编译代码……)

但即使修复了这个令人尴尬的错误,代码还是很幼稚。这里还有更多错误:

  • 始终const 限定未修改的数组参数。
  • 在嵌入式系统中始终使用stdint.h
  • 切勿使用像255 这样的幻数。在这种情况下,请改用UINT8_MAX

以上为行业标准共识。 (MISRA-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];
    }
  }
}

PC 使用示例:(请注意,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 gcc -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}

仍然不是最有效的代码,分支密集。如果目标是库质量的代码,我认为还有很大的空间可以进一步优化。但它也是一种专门的算法,可以找到最小值和最大值,以及它们各自的索引。

对于小型数据集,首先对数据进行排序可能更明智,然后从排序的最小和最大索引中获取最小值和最大值。如果您打算在代码的其他地方搜索数据以用于其他目的,那么一定要先对其进行排序,以便您可以使用二分查找。

【讨论】:

    【解决方案2】:

    int i=0, min=255, max=0int i=0, *min=255, *max=0 都定义了三个新变量,它们已初始化,但在循环体中使用不正确。

    限制应该在循环之前初始化:

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

    或者可以在循环之前定义新变量i,但这不像第一个那样容易阅读:

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

    请注意,如果有小于0或大于255的值,则返回的最小值和最大值将不正确。

    【讨论】:

    • 请注意:避免此类问题的安全方法是将minmax 都初始化为第一个值,即list[0]
    【解决方案3】:

    这是代码中的一些错误。也许应该这样写:

    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];
        }
    

    【讨论】:

    • 这定义了一个新变量 (int *min = 255) 然后取消引用它,导致访问冲突。
    • @vll 所以更好的选择是在 for 循环之前初始化 *min 和 *max?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-23
    • 2013-07-12
    • 2020-07-27
    • 1970-01-01
    相关资源
    最近更新 更多