我读到的关于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预编译。
答案 0 :(得分: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?
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;
}
答案 1 :(得分:2)
我在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
。现在将T
类型替换为类型R *
:
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;
<小时/> 1。但是,在C ++中这是必要的,但是如果你正在编写C ++,那么你应该使用
new
运算符。
答案 2 :(得分:0)
OP希望&#34;传递对字符串&#34;的引用,然后将函数传递给存储字符串第一个元素地址的地址。
char *s = NULL;
while (get_string("Hello ", &s) > 0) {
puts(s);
}
free(s);
s = NULL;
使用
git config core.pager `fold -w 80 | less`