2016-02-11 48 views
2

我讀過的關於scanfgetsfgets的一切都是有問題的;無論是空白,溢出還是複雜性。我正在介紹C語言課程,並且因爲我有足夠的Java和其他語言編程經驗,所以我決定創建自己的函數,以使用getchar()函數從用戶獲取字符串輸入。我的代碼的相關作品是下面:如何將引用傳遞給字符串?

bool get_string (char prompt[], char* string) 
{ 
    printf(prompt); // Prompt the user 

    // Ensure string is allocated to store the null character and reset it if it has been initialized 
    do { string = (char*)malloc(sizeof(char)); } while (string == NULL); 

    int index = 0; 
    char place = getchar(); 
    while (place != '\n') // Obtain all characters before endl 
    { 
     string = (char*)realloc(string, sizeof(string) + sizeof(char)); // Create room in the array 
     if (string == NULL) return false; // Ensure realloc worked correctly 
     string[index++] = place; // Place the new string in the second to last index 
     place = getchar(); 
    } 
    string[index] = '\0'; // Append the null character at the end 

    return true; // Operation succeeded 
} 

通過測試和調試,我設法弄清楚的是:

  1. 我的功能滿足當地規範,參數string持有輸入的字符串。
  2. 我在主要方法中使用的指針char*未被更改。在調用我的輸入函數之後,這個指針的解引用和它的初始值保持一樣。

我的印象是,因爲我傳遞一個指向函數的指針,它將通過引用來處理參數。事實上,這是我在課堂上教過的。任何見解都可以幫助。

獎勵積分獎勵,如果:

你能告訴我爲什麼它不會讓我自由我char*指針爲主。 (也許是因爲它沒有通過相同的問題分配?)

我還在做什麼錯了,如調用realloc太多次了?

注意:我使用MSVC C89編譯器並定義了bool,true和false預編譯。

+0

嗯'do {string =(char *)malloc(sizeof(char)); } while(string == NULL);'看起來像一個潛在的不定式循環。更好地返回錯誤指示。 – chux

+0

'bool get_string(char prompt [],char * string)'的調用者永遠不會看到分配的'string'的值。 ('string'按值傳遞,而不是引用) – chux

+2

'sizeof(string)'不會做你認爲它的作用。 – aschepler

回答

2

我用我的主要方法的字符*指針沒有被改變。在調用我的輸入函數之後,這個指針的解引用和它的初始值保持一樣。

這是什麼,班次很多人,當他們第一次開始如果你想有一個函數來更新一個參數是指針值寫入C.,你必須通過一個指針的指針。

假設如下:

void foo(T *p) 
{ 
    *p = new_value(); // update the thing p is pointing to 
} 

void bar(void) 
{ 
    T val; 
    foo(&val); // update val 
} 

很簡單 - 我們希望foo功能到一個新的值寫入val,所以我們傳遞一個指針val。現在用型R *更換型T

void foo(R **p) 
{ 
    *p = new_value(); // update the thing p is pointing to 
} 

void bar(void) 
{ 
    R *val; 
    foo(&val); // update val 
} 

語義是完全一樣的 - 我們正在編寫一個新的價值val。所有改變的是valp的類型。

所以,你的函數原型必須

bool get_string (char prompt[], char **string) 

,因爲你要修改的指針string點。這也意味着,在你的函數體,你寫*string,不string

用於寫入malloc呼叫的優選方法是

T *p = malloc(sizeof *p * number_of_elements); 

T *p; 
... 
p = malloc(sizeof *p * number of elements); 

鑄造是不必要的,因爲C89 的,和C89下實際上能夠抑制有用的診斷。由於C99廢除了隱int聲明這不是大問題了,但它仍然是更好地把它關閉。另請注意sizeof的操作數;我們使用表達式*p而不是像(char)這樣的類型表達式。由於表達*p的類型是T,然後sizeof *p給出相同的結果sizeof (T)。它不僅看起來更乾淨,而且如果您決定更改p的類型,它會減少維護。

在你的情況,p*string,給我們

*string = malloc(sizeof **string); 

由於realloc是一個潛在的昂貴的操作,你真的不想叫它爲每一個新角色。一個更好的策略是最初分配一個應該處理大多數情況的緩衝區,然後根據需要將其擴展一些當前大小的因子(例如加倍)。在這種情況下,我會做類似如下:

size_t stringSize = INITIAL_SIZE; // keeps track of the physical buffer size 

*string = malloc(sizeof *string * stringSize); 
if (! *string) 
    // initial memory allocation failed, panic 

while ((place = getchar()) != '\n' && place != EOF) 
{ 
    if (index == stringSize) 
    { 
    // double the buffer size 
    char *tmp = realloc(*string, sizeof **string * (stringSize * 2)); 
    if (tmp) 
    { 
     *string = tmp; 
     stringSize *= 2; 
    } 
    } 
    (*string)[index++] = place; 
} 

這降低了呼叫realloc,應該最大限度地提高你的表現的總數。

此外,如果realloc失敗,它將返回NULL並保留當前分配的緩衝區;但是,如果出現這種情況,你真的不想這個結果指派回到*string,否則你會失去你該內存僅供參考。你應該總是realloc結果分配給一個臨時變量和前檢查*string分配回來。

還要注意我們如何下標*string;由於下標[]運算符具有比一元運算符*更高的優先級,所以*string[index++]將被解析爲*(string[index++]),這不是我們想要的 - 我們想要索引到*string而不是string。所以,我們必須明確地組用括號內*運營商,給我們

(*string)[index++] = place; 


1.這是必要的,C++,但是,但是,如果你正在寫C++,你應該使用new操盤手。

+0

這是一個非常棒的答案,謝謝!我已經讓我的代碼工作並清理它以符合C標準。我的教授解釋了指針的指針,但直到現在,我認爲這是合法的浪費記憶。現在我明白爲什麼它不是在向數據結構中添加維度。 –

+0

@LukeSamuel:多重間接可能會很匆忙,並且在實際代碼中很少會看到三個以上的間接級別('T *** p');然而,有時你無法避免它。您可以在SO和其他地方找到關於「三星程序員」的大量嘲諷評論;我個人認爲,一旦你理解了規則,你就可以在任何層面上進行間接工作,而對我而言,這並不是那種恐嚇。然而,我是一個瘋狂的人。 –

3

我的印象是,因爲我傳遞了一個指向該函數的指針,它將通過引用處理該參數。事實上,這是我在課堂上教過的。任何見解都可以幫助。

沒有由C.引用你們經過的每個參數是通過價值

這樣:

void foo(int a) { 
    a = 21; 
    // a == 21 for the rest of THIS function 
} 
void bar(void) { 
    int x = 42; 
    foo(x); 
    // x == 42 
} 

也是一樣的:

static int john = 21; 
static int harry = 42; 
void foo(int * a) { 
    a = &john; 
    // a points to john for the rest of THIS function 
} 
void bar(void) { 
    int * x = &harry; 
    foo(x); 
    // x still points to harry 
} 

如果你想通過參數來改變一個指針,那麼你需要一個指針傳遞到指針:

static int john = 21; 
static int harry = 42; 
void foo(int ** m) { 
    *m = &john; 
} 
void bar(void) { 
    int * x = &harry; 
    foo(&x); // passing the address of x by value 
    // x now points to john 
} 

我還在做什麼錯了,比如調用realloc t很多次?

printf(prompt); 

安全問題:嘗試之類的東西"%s"prompt值。更好地使用putsprintf("%s", prompt)

do { string = (char*)malloc(sizeof(char)); } while (string == NULL); 

這是一個可能的無限循環。如果malloc失敗,再次立即調用它不會改變任何東西。另外:不要投的malloc返回值。此外被sizeof(char)定義爲等於1

int index = 0; 

對於指數使用size_t

char place = getchar(); 

還有一個原因getchar返回int,即能檢查EOF,你...

while (place != '\n') 

...不這樣做,而是應該!

string = (char*)realloc(string, sizeof(string) + sizeof(char)); 

不要將返回值,sizeof(string)是不是做你認爲是這樣,它是一個編譯時間常數(可能是64位系統上8)。

if (string == NULL) return false; 

內存泄漏,因爲......

如果沒有足夠的內存,舊內存塊不被釋放,並返回空指針。

[Source]


以下是我讀到在C線:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <assert.h> 

char * readline(char const * const prompt) { 
    char buffer[10]; 
    char * string = malloc(1); 
    if (string == NULL) { 
    return NULL; 
    } 
    // The accumulated length of the already read input string. 
    // This could be computed using strlen, but remembering it 
    // in a separate variable is better, performancewise. 
    size_t accumLength = 0; 
    string[0] = '\0'; 
    printf("%s", prompt); 
    while (fgets(buffer, 10, stdin) != NULL) { 
    // To see what has been read in this iteration: 
    // printf("READ: |%s|\n", buffer); 
    // Compute the length of the new chunk that has been read: 
    size_t const newChunkLength = strlen(buffer); 
    // Try to enlarge the string so that the new chunk can be appended: 
    char * const newString = realloc(string, accumLength + newChunkLength + 1); 
    if (newString == NULL) { 
     free(string); 
     return NULL; 
    } 
    string = newString; 
    // Append the new chunk: 
    strcpy(string + accumLength, buffer); 
    accumLength += newChunkLength; 
    // Done if the last character was a newline character 
    assert(accumLength > 0); 
    if (string[accumLength - 1] == '\n') { 
     // NOTE: Wasting 1 char, possible solution: realloc. 
     string[accumLength - 1] = '\0'; 
     return string; 
    } 
    } 
    // EOF is not an error! 
    if (feof(stdin)) { 
    return string; 
    } 
    free(string); 
    return NULL; 
} 


int main(int argc, char ** argv) { 
    char const * const input = readline(">"); 
    printf("---\n%s\n---\n", input); 
    return 0; 
} 
0

一個OP要 「傳遞給一個字符串的引用」,然後傳遞給函數的在何處存儲所述串的所述第一元素的地址的地址。

// The function signature needs some changes 
// 1: string read. 0: out-of-memory EOF:end-of-file 
//    const      * 
int get_string(const char prompt[], char** string) { 

    // Never do this. If prompt contain `'%'`, code becomes a hackers target 
    // printf(prompt); // Prompt the user 

    fputs(prompt, stdout); // Prompt the user 
    fflush(stdout); 

    // use size_t, not int 
    // int index 
    size_t size = 0; // Keep track of size 
    for (;;) { 
    // Use int to distinguish EOF from all other char 
    // char place = getchar(); 
    int place = getchar(); 
    // Note: reallocating every loop is generally inefficient 
    void *former = *string; 

    // sizeof(string) + sizeof(char) 
    // sizeof(string) is the size of the pointer, not the size of memory it points to. 
    // sizeof(char) is always 1 
    *string = realloc(*string, size + 1); 
    if (*string == NULL) { 
     free(former); // free old buffer 
     return 0; // fail 
    } 
    // add termination on \n or EOF 
    if (place == '\n' || place == EOF) { 
     // Add detection and housekeeping for EOF 
     if (place == EOF && size == 0) { 
     free(*string); 
     *string = NULL; 
     return EOF; 
     } 
     break; 
    } 
    (*string)[size++] = place; 
    } 
    (*string)[size] = `\0`; 
    return 1; // Operation succeeded 
} 

使用

char *s = NULL; 
while (get_string("Hello ", &s) > 0) { 
    puts(s); 
} 
free(s); 
s = NULL;