[英]How to pass a reference to a string?
我所讀到的有關scanf
, gets
和fgets
內容都是有問題的。 具有空白,溢出或復雜性。 我正在學習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
}
通過測試和調試,我設法弄清楚了:
string
保存輸入的字符串。 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
值。 更好地使用puts
或printf("%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
編寫一個新值。 更改的只是val
和p
的類型。
因此,您的功能原型需要
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;
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.