指针和正则变量同名
Pointer and regular variable with the same name
在 "Embedded C programming" 一书的 "Memory and pointers" 章节中,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++){
这是书上的错误还是我理解有误?
这是代码中的一些错误。也许它应该是:
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 i=0, min=255, max=0
和int 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
的值,返回的最小值和最大值将不正确。
这似乎是书中的错误 - 它确实在循环内声明了新变量。 (提示:在出版编程书籍之前,至少先编译代码……)
但即使修复了那个令人尴尬的错误,代码还是天真地编写的。这里还有更多的错误:
- 始终
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}
仍然不是最有效的代码,非常分支密集。如果目标是库质量代码,我认为还有很大的空间可以进一步优化。但它也是一个专门的算法,找到最小值和最大值,以及它们各自的索引。
对于小数据集,先对数据进行排序可能更明智,然后从排序后的最小和最大索引中获取最小值和最大值。如果你打算在代码的其他地方搜索数据用于其他目的,那么一定要先排序,这样你就可以使用二分查找了。
在 "Embedded C programming" 一书的 "Memory and pointers" 章节中,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++){
这是书上的错误还是我理解有误?
这是代码中的一些错误。也许它应该是:
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 i=0, min=255, max=0
和int 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
的值,返回的最小值和最大值将不正确。
这似乎是书中的错误 - 它确实在循环内声明了新变量。 (提示:在出版编程书籍之前,至少先编译代码……)
但即使修复了那个令人尴尬的错误,代码还是天真地编写的。这里还有更多的错误:
- 始终
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}
仍然不是最有效的代码,非常分支密集。如果目标是库质量代码,我认为还有很大的空间可以进一步优化。但它也是一个专门的算法,找到最小值和最大值,以及它们各自的索引。
对于小数据集,先对数据进行排序可能更明智,然后从排序后的最小和最大索引中获取最小值和最大值。如果你打算在代码的其他地方搜索数据用于其他目的,那么一定要先排序,这样你就可以使用二分查找了。