[英]Pointer and regular variable with the same name
在《嵌入式 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);
如果我理解正確:
(對最后一個不是 100% 確定。)
在 for 循環中,他每次都將數組列表中的下一個 int 與指針 *min 和 *max 地址處的內容進行比較,並在必要時更新這些地址處的值。
但是在循環的定義中,他定義了min = 255,max = 0。
在我看來,這似乎是兩個尚未初始化的全新變量。 那條線應該不是
for( int i=0, *min=255, *max=0: i<count; i++){
這是書上的錯誤還是我誤解了?
這似乎是書中的一個錯誤——它確實在循環內聲明了新變量。 (提示:在出版編程書籍之前,至少要先編譯代碼……)
但即使修復了那個令人尷尬的錯誤,代碼還是很天真。 這里有更多錯誤:
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}
仍然不是最有效的代碼,非常密集的分支。 如果目標是庫質量的代碼,我認為還有很多空間可以進一步優化。 但它也是一種專門的算法,可以找到最小值和最大值,以及它們各自的索引。
對於小數據集,最好先對數據進行排序,然后從排序的最小和最大索引中獲取最小值和最大值。 如果您打算在代碼中的其他地方出於其他目的搜索數據,那么一定要先對其進行排序,以便您可以使用二分搜索。
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
的值,則返回的最小值和最大值將不正確。
這是代碼中的一些錯誤。 也許它應該寫成:
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];
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.