C语言中的字符串输入和输出
我有一段代码:C语言中的字符串输入和输出,c,string,C,String,我有一段代码: char* receiveInput(){ char *s; scanf("%s",s); return s; } int main() { char *str = receiveInput(); int length = strlen(str); printf("Your string is %s, length is %d\n", str, length); return 0; } 我收到以下输出: Your
char* receiveInput(){
char *s;
scanf("%s",s);
return s;
}
int main()
{
char *str = receiveInput();
int length = strlen(str);
printf("Your string is %s, length is %d\n", str, length);
return 0;
}
我收到以下输出:
Your string is hellàÿ", length is 11
我的意见是:
helloworld!
有人能解释一下为什么,以及为什么这种编码方式不好,提前感谢scanf并没有为您分配内存 您需要为传递给
scanf
的变量分配内存
你可以这样做:
char* receiveInput(){
char *s = (char*) malloc( 100 );
scanf("%s",s);
return s;
}
// s must be of at least 100 chars!!!
char* receiveInput( char *s ){
scanf("%99s",s);
return s;
}
int main()
{
char str[100];
receiveInput( str );
int length = strlen(str);
printf("Your string is %s, length is %d\n", str, length);
return 0;
}
但警告:
receiveInput
的函数将获得返回内存的所有权:在main
中打印后,必须free(str)
。(以这种方式放弃所有权通常不被视为良好做法)
一个简单的解决方法是将分配的内存作为参数
99
(在我的例子中),您的程序将遭受缓冲区溢出(这就是它已经发生的情况)
一个简单的解决方法是将缓冲区的长度传递给scanf
:
scanf("%99s",s);
char* receiveInput(){
char *s = (char*) malloc( 100 );
scanf("%s",s);
return s;
}
// s must be of at least 100 chars!!!
char* receiveInput( char *s ){
scanf("%99s",s);
return s;
}
int main()
{
char str[100];
receiveInput( str );
int length = strlen(str);
printf("Your string is %s, length is %d\n", str, length);
return 0;
}
必须首先在receiveInput()方法中将内存分配给s对象。例如:
s = (char *)calloc(50, sizeof(char));
有几个问题已经解决了你做错了什么以及如何修复它,但你也说了(我的重点): 有人能解释一下为什么,以及为什么这种编码方式不好吗 我认为
scanf
是一种读取输入的糟糕方式。它与printf
不一致,很容易忘记检查错误,很难从错误中恢复,并且与普通(更容易正确执行)读取操作(如fgets
和company)不兼容
首先,请注意,“%s”
格式将是只读的,直到它看到空白为止。为什么是空白?为什么“%s”
打印出整个字符串,但以如此有限的容量读入字符串
如果您想阅读整行内容,就像您经常做的那样,scanf
提供。。。使用“%[^\n]”
。什么?那是什么?这是什么时候变成Perl的
但真正的问题是这两个都不安全。它们都可以自由溢出,没有边界检查。要检查边界吗?好的,你明白了:%10s”
(而且%10[^\n]“
看起来更糟了)。这将只读取9个字符,并自动添加一个终止nul字符。所以这很好。。。当我们的阵列大小永远不需要更改时
如果要将数组的大小作为参数传递给scanf,该怎么办printf
可以执行以下操作:
char string[] = "Hello, world!";
printf("%.*s\n", sizeof string, string); // prints whole message;
printf("%.*s\n", 6, string); // prints just "Hello,"
想用scanf做同样的事情吗?以下是方法:
static char tmp[/*bit twiddling to get the log10 of SIZE_MAX plus a few*/];
// if we did the math right we shouldn't need to use snprintf
snprintf(tmp, sizeof tmp, "%%%us", bufsize);
scanf(tmp, buffer);
没错-scanf
不支持“%.*s”
变量精度printf
支持,因此要使用scanf
进行动态边界检查,我们必须在临时缓冲区中构造自己的格式字符串。这是各种各样的坏主意,即使这里实际上是安全的,但对任何一个来访的人来说,这都是一个非常糟糕的主意
同时,让我们看看另一个世界。让我们看看fgets
的世界。下面是我们如何使用fgets
读取一行数据:
fgets(buffer, bufsize, stdin);
无限少的头痛,没有浪费处理器时间将整数精度转换为字符串,该字符串只能由库重新解析为整数,所有相关元素都在一行上,让我们看看它们是如何协同工作的
当然,这可能不会读取整行。如果行短于bufsize-1
个字符,它将只读取整行。下面是我们如何阅读整行文字:
char *readline(FILE *file)
{
size_t size = 80; // start off small
size_t curr = 0;
char *buffer = malloc(size);
while(fgets(buffer + curr, size - curr, file))
{
if(strchr(buffer + curr, '\n')) return buffer; // success
curr = size - 1;
size *= 2;
char *tmp = realloc(buffer, size);
if(tmp == NULL) /* handle error */;
buffer = tmp;
}
/* handle error */;
}
curr
变量是一种优化,它可以防止我们重新检查已经读取的数据,并且是不必要的(尽管在读取更多数据时很有用)。如果您愿意,我们甚至可以使用strchr
的返回值来去掉结尾的“\n”
字符
还要注意size\u t size=80代码>作为起始位置完全是任意的。我们可以使用81、79或100,或者将其作为用户提供的参数添加到函数中。我们甚至可以添加一个int(*inc)(int)
参数,并更改size*=2代码>至size=inc(大小)代码>,允许用户控制阵列的增长速度。当重新分配成本高昂且需要读取和处理大量数据行时,这些方法有助于提高效率
我们可以使用scanfscanf
编写相同的代码,但是想想我们需要重写多少次格式字符串。我们可以将其限制为常量增量,而不是上面实现的加倍(很容易),并且不必调整格式字符串;我们可以让步,只存储数字,用上面的方法进行计算,每次重新分配时使用snprintf
将其转换为格式字符串,以便scanf
可以将其转换回相同的数字;我们可以通过手动调整格式字符串的方式限制增长和起始位置(比如,只增加数字),但这可能会在一段时间后变得棘手,可能需要递归(!)才能干净地工作
此外,很难将读取与scanf
和读取与其他功能混合在一起。为什么?假设您想从一行中读取一个整数,然后从下一行中读取一个字符串。你可以试试这个:
int i;
char buf[BUSIZE];
scanf("%i", &i);
fgets(buf, BUFSIZE, stdin);
这将读取“2”,但是fgets
将读取空行,因为scanf
没有读取换行符!好的,拿两个:
...
scanf("%i\n", &i);
...
你认为这会消耗换行符,确实如此——但它也会消耗下一行的前导空格,因为scanf
无法区分换行符和其他形式的空格。(另外,事实证明您正在编写Python解析器,在行中引入空格非常重要。)要实现这一点,您必须调用getchar
或在换行符中读取的内容并将其丢弃:
...
scanf("%i", &i);
getchar();
...
这不是很傻吗?如果在函数中使用scanf
,但是