簡體   English   中英

如何傳遞對字符串的引用?

[英]How to pass a reference to a string?

我所讀到的有關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. 我在main方法中使用的char*指針未更改。 調用我的輸入函數后,此指針的取消引用保持與其初始值相同。

我的印象是,因為我傳遞了一個指向函數的指針,所以它將按引用對待參數。 實際上,這就是我在課堂上所教的。 任何見識都可以提供幫助。

如果獲得獎勵積分,則:

您可以告訴我為什么它不能讓我釋放main中的char*指針。 (也許是因為尚未通過同一問題分配它?)

我還做錯了什么,例如多次調用realloc?

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

我的印象是,因為我傳遞了一個指向函數的指針,所以它將按引用對待參數。 實際上,這就是我在課堂上所教的。 任何見識都可以提供幫助。

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?

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;

內存泄漏,因為...

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

[資源]


這是我在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;
}

我在main方法中使用的char *指針未更改。 調用我的輸入函數后,此指針的取消引用保持與其初始值相同。

這是很多人在剛開始編寫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 1開始 ,不需要強制轉換,並且在C89下實際上可以抑制有用的診斷。 由於C99取消了隱式int聲明,因此不再是一個大問題,但是最好還是不要使用它。 還要注意sizeof的操作數; 而不是像(char)這樣的類型表達式,我們使用表達式*p 由於表達式*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運算符。

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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM