问题描述
#include< stdio.h>
int main(){
char * mystring = calloc(2,sizeof(char));
scanf(%10 [^ \n] s,mystring);
printf( \nValue:%s\n数组大小:%d\n已分配空间:%d\n,
mystring,2 * sizeof(char), sizeof(char)* strlen(mystring));
free(mystring);
}
输出:
$ ./\"dyn_mem
laaaaaaaaaaa
值:laaaaaaaaa
数组大小:2
分配空间:10
如果我输入 scanf 输入大于数组大小的字符串。我该如何处理这个?
解决方案
您的代码中存在多个问题:
-
mystring
初始化为指向分配的2个字节的块。从技术上讲,您应该测试内存分配失败。
-
转换格式%10 [^ \n] s 不正确:应删除结尾的 s
,字符类的语法以]
结尾
数字 10
表示最多可存储10个字符,并且在<$ c $中存储一个空终止符c> mystring
。如果需要存储多个字符,则该代码具有未定义的行为。
-
printf
转换 size_t
的说明符是%zu
,而不是%d
。如果您的C库符合C99,请使用%zu
,否则将后两个参数设置为(int)
。 / p>
-
输出的大小与标签不符:第一个是分配的大小,第二个是字符串的长度。
-
scanf()
将失败。您应测试 scanf()
的返回值,该返回值必须为 1
,以避免在无效情况下发生未定义的行为输入。
-
sizeof(char)
是 1
按照定义。
有很多方法可以实现您的目标:
在支持它的系统上,例如带有GNU lib C的linux,可以在% m
前缀。 / code>和 [
以 scanf()
转换格式,并传递 char *
作为参数。 scanf()
将分配一个数组,该数组的 malloc()
大小足以接收转换后的输入。
这是Linux的修改版本:
#include< stdio.h>
#include< stdlib.h>
int main(){
char * mystring = NULL;
if(scanf(%m [^ \n],& mystring)== 1){
printf( Value:%s\n
长度字符串:%zu\n
分配的空间:%zu\n,
mystring,strlen(mystring),malloc_usable_size(mystring));
free(mystring);
}
返回0;
}
在POSIX系统上,可以使用 getline()
会将一行读取到分配的数组中。
在其他系统上,您需要编写一个函数来读取输入流并重新分配只要没有换行符或文件末尾,就可以使用目标数组。
一个常见的折衷办法是假设输入的最大长度:
#include< stdio.h>
#include< stdlib.h>
int main(){
char buf [1024];
if(scanf(%1023 [^ \n],buf)== 1){
char * mystring = strdup(buf);
if(mystring){
printf( Value:%s\n
字符串长度:%d\n,
最小分配大小:% d\n,
mystring,(int)strlen(mystring),(int)strlen(mystring)+1);
free(mystring);
}
}
返回0;
}
您也可以使用 fgets()
从输入流中读取一行并剥离换行符(如果有)。这种方法的优点是不会在空行上失败。
这里是 getline()
的简单实现,应该满足您的需求:
#include< stdio.h>
#include< stdlib.h>
int my_getline(char ** lineptr,size_t * n,FILE * stream){
char * ptr = * lineptr;
size_t size = * n;
size_t pos = 0;
int c;
而(((c = getc(stream)&& c!='\n')){
if(pos + 1> = size){
/ *重新分配数组按黄金比例增加大小* /
size = size +(size / 2)+(size / 8)+ 16;
ptr = realloc(ptr);
if(ptr = = NULL){
ungetc(c,流);
返回EOF;
}
* n =大小;
* lineptr = ptr;
}
ptr [pos ++] = c;
ptr [pos] ='\0';
}
return(int)pos;
}
int main(){
char * mystring = NULL; //必须初始化
size_t size = 0; //必须初始化
int res;
while(((res = my_getline(& mystring,& size,stdin))> = 0){
printf( Value:%s\n
字符串的长度:%d\n,
分配的大小:%d\n,
mystring,res,(int)size);
}
free(mystring);
返回0;
}
#include <stdio.h>
int main() {
char *mystring = calloc(2, sizeof(char));
scanf("%10[^\n]s", mystring);
printf("\nValue: %s\nSize of array: %d\nAllocated space: %d\n",
mystring, 2 * sizeof(char), sizeof(char) * strlen(mystring));
free(mystring);
}
Output:
$ ./"dyn_mem"
laaaaaaaaaaa
Value: laaaaaaaaa
Size of array: 2
Allocated space: 10
This code can produce an undefined behavior if I enter in the scanf
input a string bigger than array size. How can I handle this ?
解决方案 There are multiple problems in your code:
mystring
is initialized to point to an allocated block of 2 bytes. Technically, you should test for memory allocation failure.
the conversion format "%10[^\n]s"
is incorrect: the trailing s
should be removed, the syntax for character classes ends with the ]
.
the number 10
means store at most 10 characters and a null terminator into mystring
. If more than 1 character needs to be stored, the code has undefined behavior.
the printf
conversion specifier for size_t
is %zu
, not %d
. If your C library is C99 compliant, use %zu
, otherwise case the last 2 arguments as (int)
.
the sizes output do not correspond to the labels: the first is the allocated size, and the second is the length of the string.
the scanf()
will fail if the file is empty or starts with a newline. You should test the return value of scanf()
, which must be 1
, to avoid undefined behavior in case of invalid input.
sizeof(char)
is 1
by definition.
There are many ways to achieve your goal:
On systems that support it, such as linux with the GNU lib C, you could use an m
prefix between the %
and the [
in the scanf()
conversion format and pass the address of a char *
as an argument. scanf()
will allocate an array with malloc()
large enough to receive the converted input.
Here is a modified version for linux:
#include <stdio.h>
#include <stdlib.h>
int main() {
char *mystring = NULL;
if (scanf("%m[^\n]", &mystring) == 1) {
printf("Value: %s\n"
"Length of string: %zu\n"
"Allocated space: %zu\n",
mystring, strlen(mystring), malloc_usable_size(mystring));
free(mystring);
}
return 0;
}
On POSIX systems, you could use getline()
that reads a line into an allocated array.
On other systems, you would need to write a function that reads the input stream and reallocates the destination array as long as you don't get a newline or the end of file.
A common compromise is to make an assumption about the maximum length of the input:
#include <stdio.h>
#include <stdlib.h>
int main() {
char buf[1024];
if (scanf("%1023[^\n]", buf) == 1) {
char *mystring = strdup(buf);
if (mystring) {
printf("Value: %s\n"
"Length of string: %d\n",
"Minimum allocated size: %d\n",
mystring, (int)strlen(mystring), (int)strlen(mystring) + 1);
free(mystring);
}
}
return 0;
}
You could also use fgets()
to read a line from the input stream and strip the newline (if any). This approach has the advantage of not failing on empty lines.
Here is a simple implementation of getline()
that should fit your needs:
#include <stdio.h>
#include <stdlib.h>
int my_getline(char **lineptr, size_t *n, FILE *stream) {
char *ptr = *lineptr;
size_t size = *n;
size_t pos = 0;
int c;
while ((c = getc(stream) && c != '\n') {
if (pos + 1 >= size) {
/* reallocate the array increasing size by the golden ratio */
size = size + (size / 2) + (size / 8) + 16;
ptr = realloc(ptr);
if (ptr == NULL) {
ungetc(c, stream);
return EOF;
}
*n = size;
*lineptr = ptr;
}
ptr[pos++] = c;
ptr[pos] = '\0';
}
return (int)pos;
}
int main() {
char *mystring = NULL; // must be initialized
size_t size = 0; // must be initialized
int res;
while ((res = my_getline(&mystring, &size, stdin)) >= 0) {
printf("Value: %s\n"
"Length of string: %d\n",
"Allocated size: %d\n",
mystring, res, (int)size);
}
free(mystring);
return 0;
}
这篇关于仅在阵列中有足够的空间时才如何保存scanf输入?如何重新分配数组以使scanf输入适合?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!