2014-01-29 26 views
2

出於某種原因,這是行不通的:C:製作,傳遞和獲取指針的常量數組常量字符串

const char * str_reset_command = "\r\nReset"; 
const char * str_config_command = "\r\nConfig"; 

const char *commands[2] = { 
    &str_reset_command, 
    &str_config_command 
}; 

這是正確的嗎? (難道是代碼的其他地方,必須引起問題)

(我運行它在基線的8位微控制器,並具有非常有限的調試功能)

澄清:

這是什麼我想達到:

const char * str_reset_command = "\r\nReset"; 
const char * str_config_command = "\r\nConfig"; 
const char * str_start_command = "\r\nStart"; 
const char * str_end_command = "\r\nEnd"; 

const char * const group0[1] = { 
    str_reset_command 
} 
const char * const group1[2] = { 
    str_reset_command, 
    str_start_command 
} 
const char * const group2[3] = { 
    str_reset_command, 
    str_start_command, 
    str_end_command 
} 
void doStuffToCharacter(unsigned char the_char){ 
    // .... 
} 
void doStuffToGroup(char ** group, unsigned char num_strings){ 
    for (unsigned char s=0; s < num_strings; s++){ 
     unsigned char idx = 0; 
     while (group[s][idx]){ 
      doStuffToCharacter(group[s][idx]); 
     } 
    } 
} 
void main (void){ 
    doStuff(group0, 1); 
    doStuff(group1, 2); 
    doStuff(group2, 3); 
} 

以及更正,任何更乾淨的建議做上述將是值得歡迎的。

命令字符串的各種組合需要發送到一個函數進行處理。所有的字符串都將放在ROM中,指向它們的指針組也一樣。

+0

澄清更新問題 – Jodes

+0

請參閱我的編輯。 – StoryTeller

+0

昨天我修改了我的答案,標題爲「編輯」(現在我寫了「第二次嘗試」的標題)。在那裏,我解釋了一個基於位掩碼的新代碼。我認爲是一種更好的方法,因爲它可以節省更多的內存,並且不會使用任何數組。另外,您還說過「需要發送各種命令字符串的組合」。我認爲我的方法爲此目的靈活。在這第二個代碼中有幾個#define行,但是這不會在編譯的程序中花費內存。 – pablo1977

回答

3

並不直接回答你的問題,但爲什麼不試試這個:

typedef enum { 
    RESET_CMD, 
    CONFIG_CND 
} cmd_id; 

const char *commands[2] = { 
    "\r\nReset", 
    "\r\nConfig" 
}; 

而不是像這樣使用:

commands[RESET_CMD] 

編輯

我我會重寫您的說明以符合此方法:

typedef enum { 
    RESET_CMD, 
    CONFIG_CND, 
    START_CMD, 
    END_CMD 
} cmd_id; 

const char *commands[4] = { 
    "\r\nReset", 
    "\r\nConfig", 
    "\r\nStart", 
    "\r\nEnd" 
}; 

const cmd_id group0[1] = { 
    RESET_CMD 
}; 

const cmd_id group1[2] = { 
    RESET_CMD, 
    START_CMD 
}; 

const cmd_id group2[3] = { 
    RESET_CMD, 
    START_CMD, 
    END_CMD 
}; 

void doStuffToCharacter(unsigned char the_char){ 
    // .... 
} 

void doStuffToGroup(const cmd_id group[], unsigned char num_cmds){ 
    for (unsigned char s=0; s < num_cmds; s++) { 
     unsigned char idx = 0; 
     while (commands[group[s]][idx]) { 
      doStuffToCharacter(commands[group[s]][idx++]); 
     } 
    } 
} 

void main (void){ 
    doStuff(group0, 1); 
    doStuff(group1, 2); 
    doStuff(group2, 3); 
} 
+0

在OP澄清之後,這確實看起來是一個非常好的方法。定義組也很簡單:'cmd_id group1 [] = {RESET_CMD,CONFIG_CND};'。 – user694733

+1

太棒了,謝謝!現在我可以專注於重新生長丟失的頭髮! – Jodes

2

你有一個const char指針的數組,你試着在它裏面存儲指向const char指針的指針。刪除&運營商,它應該工作。

+0

謝謝,但我得到錯誤「需要的常量表達式」 – Jodes

4

您創建了一個指針數組,但您傳遞指針的地址(意思是指向指針的指針)。你應該做的是這樣的 -

const char* commands[2]= {str_reset_command, str_config_command}; 
+0

謝謝,但我得到的錯誤「需要的常量表達式」 – Jodes

+1

@Jodes你沒有告訴你在文件範圍內使這個變量。 – this

+0

他們都在同一個文件,所以從我讀過的,它不應該有所作爲? – Jodes

1

如果你有變量聲明在函數之外,你的初始化必須是常量。在這種情況下,Const不算。您將創建指向指針的指針數組,然後使用char *變量的地址。

const char * str_reset_command = "\r\nReset"; 
const char * str_config_command = "\r\nConfig"; 

const char **commands[2] = { 
    &str_reset_command, 
    &str_config_command 
}; 

不要忘記在使用數組時取消引用指針。

const char* string = *commands[1] ; //actually it is *(commands[1]) but the [] operator has higher precedence anyway 
+0

這幾乎肯定不是OP想要的 – user102008

1

試試這個。這不完全是你想要的,但它完成了這項工作。

#define RESET_COMMAND    "Reset" 
#define CONFIG_COMMAND   "Config" 

const char *commands[2] = { 
    RESET_COMMAND, 
    CONFIG_COMMAND, 
}; 

int 
main() { 
    ... 
} 
+0

謝謝,但我更喜歡編譯器肯定會優化的東西,以便當它們包含在多個組中時字符串不會被複制到ROM中 – Jodes

+0

@Jodes實際上,如果您使用相同的文字在同一個編譯單元多次?編譯器很容易檢測到這一點,迄今爲止我所使用的所有編譯器都對其進行了優化。 – user694733

1

您正在使用全局變量。你確定你需要這種方法嗎?

通過按照您的代碼原樣,您將str_reset_command定義爲const char *。這意味着這是一個指向char的指針,它也具有限定符const。您不得將「const」與const混淆。 :)
編譯器從涉及文字的表達式構建的每個表達式中考慮「const」。
另一方面,const限定符意味着您正在定義一個「變量」,其值無法修改,除非在其定義的那一刻。

表達式"\r\nReset"是一個「const」,因爲它是一個字符串文字。
然而const char * str_reset_command不是「const」,而是一個不能被修改的變量。

區別在於文字是編譯器的「常量」,因爲它可以在編譯時計算它們的值。但const對象作爲變量保存,因爲它的值可能會在執行時間內確定。例如,請考慮<string.h>中幾個函數的const參數。

您的變量group0被定義爲指向char數組的指針。
由於它被定義爲一個全局變量,它的初始值必須是一個「const」(在編譯時間意義上)值。但是你提供了str_reset_command,這不是一個文字(或只涉及文字的表達式)。因此,它的值不能在編譯時確定,並且不能在那裏用作初始化。

也許你可以試着寫一個初始化函數:

const char * str_reset_command = "\r\nReset"; 
const char * str_config_command = "\r\nConfig"; 
const char * str_start_command = "\r\nStart"; 
const char * str_end_command = "\r\nEnd"; 

char * group0[1]; 
char * group1[2]; 
char * group2[3]; 

void initGroups(void) { 
     group2[0] = group1[0] = group0[0] = str_reset_command; 
     group2[1] = group1[1] = str_start_command; 
     group2[2] = str_end_command; 
} 

int main(void) { 
    initGroups(); 

    // Do stuff... 
} 

我放棄了const預選賽,因爲現在我們需要修改指針。

無論如何,使用全局變量有一些副作用。
嘗試修改此方面,如果可能的話。


編輯

第二TRY

我想在你的程序,我已經完全改變你的做法。
首先,我不明白你爲什麼使用所有「group []」數組。
既然你想優化內存資源,這不是最優的。
另一方面,你的「常量字符串」似乎只是少數。
通過假設它們不超過8,給定組中涉及的字符串的信息可以保存在1個字節中。
我的意思是,你可以通過一個字節的位來定義一個「組」或「集合」,因此如果一個給定的成員屬於該集合,則將該位置1(開),否則爲0。

此外,由於您接受使用指向恆定字符的指針數組,我認爲您所有的字符串都可以在代碼開始時保存在一個常量字符數組中。這可以通過一個const聲明在ROM中保存。

您已將名稱用作str_reset,str_start等。
看來你需要這些信息才能清楚自己。
這些信息可以通過編譯器常量和/或枚舉的方式保存在代碼中,這些代碼在編譯後的程序中沒有任何代價。

我設計了一個使用位掩碼的代碼。
這可讓您使用最多8個字符串。
由於位掩碼是2的冪,因此不能用作數組索引。然而,人們可以以一種非常隨後的方式定義一個名稱與位掩碼常量「並行」的激勵常量列表。
特別是,你將能夠或改變枚舉的順序,但你的代碼將繼續工作。

爲了完成該圖片,我使用了C99/C11標準的一個特性(您似乎使用了這個特性,因爲您聲明for語句的方式),允許我們通過寫入數組來初始化數組的不可數成員期望的指數。
由於索引現在將具有枚舉給定的名稱,所以您可以信任此技術,而不必關注已使用的實際索引。

我已經使用<stdio.h>printf()來測試程序。你可以擦除這些行。

#include <stdio.h> 

#define bit0 0x01u /* Binary 0000 0001 */ 
#define bit1 0x02u /* Binary 0000 0010 */ 
#define bit2 0x04u /* Binary 0000 0100 */ 
#define bit3 0x08u /* Binary 0000 1000 */ 
#define bit4 0x10u /* Binary 0001 0000 */ 
#define bit5 0x20u /* Binary 0010 0000 */ 
#define bit6 0x40u /* Binary 0100 0000 */ 
#define bit7 0x80u /* Binary 1000 0000 */ 

enum {reset_command = 0, config_command, start_command, end_command}; 

#define bit_reset (1u << reset_command) /* Equal to bit0 */ 
#define bit_config (1u << config_command) /* Equal to bit1 */ 
#define bit_start (1u << start_command) /* Equal to bit2 */ 
#define bit_end (1u << end_command)  /* Equal to bit3 */ 

const char * const str[] = { 
     [reset_command] = "\r\nReset", 
     [config_command] = "\r\nConfig", 
     [start_command] = "\r\nStart", 
     [end_command] = "\r\nEnd" 
    }; 

const unsigned char bitgroup0 = bit_reset; 
const unsigned char bitgroup1 = bit_reset | bit_start; 
const unsigned char bitgroup2 = bit_reset | bit_start | bit_end; 

void doStuffToCharacter(unsigned char the_char){ 
    printf("%c", the_char); 
} 

void doStuff(const char * const * str, unsigned char bitgroup){ 
    printf("\n\nGroup: %hu\n", bitgroup); 

    for (unsigned char b=bitgroup, j=0; b; b >>= 1u, j++){ 
     if (b & 1u) { 
      for(unsigned char idx = 0; str[j][idx]; idx++) { 
       doStuffToCharacter(str[j][idx]); 
      } 
     } 
    } 
} 
int main (void){ 
    doStuff(str, bitgroup0); 
    doStuff(str, bitgroup1); 
    doStuff(str, bitgroup2); 
} 

正如您所看到的,每個「組」現在只需要1個字節。 (你的方法至少使用了1,2和3個字節)。
for()語句中的迭代次數未超過groupbit參數中的「on」的最大位數。

指針的const char對象被要求保存在const指針中也是滿載的。

數組的大小由編譯器自動確定以保存最大索引。

for()循環在初始化爲您作爲參數傳遞的「組」的「字節」上迭代。
最後一位針對1進行測試。
如果該位爲1,則執行一些操作。
否則,這會跳到下一個迭代中。
最後一位被賦值爲b >>= 1u

每次迭代,我們需要走開數組str的索引j
因此,當且僅當處理j字符串時,第j位是1。
接下來,每個重複,直到b沒有更多位1

如果您使用其位1全部連續的「組」,那麼該程序的工作方式與您在示例中所期望的相同。

現在您可以選擇所需的操作,只需切換相應的位即可。

+0

謝謝您的詳細解答!全局變量會產生哪些附帶影響?在使用全局變量時,我相信編譯器可以通過不在函數間不必要地傳遞變量來實現理想的優化,從而在微控制器上節省一些珍貴的RAM和執行週期。 – Jodes

+1

「並行效應」意味着「副作用」(我曾遇到過翻譯成英文的麻煩)。它被稱爲編程中的常見邏輯錯誤。在C語言的模塊化語言中,每個函數都隱藏自己的變量並通過參數或返回值將信息傳遞給程序的其餘部分是一種好的做法。在你的特定情況下,如果你確實需要使用全局變量,那麼你必須非常小心才能不會把你的程序亂成一團。 – pablo1977

+0

@Jodes:我添加了一個代碼,避免了多個數組的方法,將字符串保存在ROM內存中,並節省了內存。看看並告訴我,如果你喜歡它。 – pablo1977

1

你的數組聲明是與所述功能的使用它的原型不一致:

const char *commands[2] = { // <- declared as array of 2 pointers to char 
doStuffToGroup(char ** group // <- used as pointer to pointer to char 

這是一種常見的C/C++陷阱(儘管用作用於香草陣列一個remplacement載體使在遠不如普通C++)。

有關更詳細的解釋,請參閱this answer