字符串输入和输出用C
我有这样的片断代码:字符串输入和输出用C
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;
}
但警告:
-
调用
receiveInput
将返回的内存的所有权的功能:你会在打印后必须free(str)
它在main
。 (以这种方式剥夺所有权通常不被认为是一种好的做法)。一个简单的解决方法是获取分配的内存作为参数。
-
如果输入字符串长于
99
(在我的情况下),您的程序将遭受缓冲区溢出(这是它已经发生的情况)。一个简单的办法是通过对
scanf
您的缓冲区长度:scanf("%99s",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 = (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_t size = 80;
作为起始的地方完全是任意的。我们可以使用81或79或100,或者将它作为用户提供的参数添加到函数中。我们甚至可以添加int (*inc)(int)
参数,并将size *= 2;
更改为size = inc(size);
,从而允许用户控制阵列的增长速度。当重新分配成本高昂并需要读取和处理大量的数据时,这些对于效率很有用。
我们可以写同样与scanf
,但想到我们就会有多少次重写格式字符串。我们可以将它限制为一个常量增量,而不是上面实现的加倍(容易),而不必调整格式字符串;我们可以让步,只储存数量,做数学题与上面,并用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
,但不叫getchar
,因为你不知道下一个读是否将是scanf
什么理智的,会发生什么(或者判断下一个字符,甚至将是一个换行符) ?突然间,处理这种情况的最好方法似乎是挑选其中一种:我们是否专门使用scanf
,并且永远不能访问fgets
风格的全控制输入,或者我们专门使用fgets
,并且难以执行复杂的解析?
其实,答案是我们不。我们使用fgets
(或非scanf
功能)完全,而当我们需要scanf
样的功能,我们只是呼吁串sscanf
!我们不需要有scanf
不必要地浪费我们的文件流!我们可以对我们想要的输入进行精确控制,并且仍然获得scanf
格式的所有功能。即使我们不能,很多scanf
格式选项在标准库中都有近乎直接的对应功能,如无限灵活的strtol
和strtod
功能(和朋友)。此外,i = strtoumax(str, NULL)
为C99大小的整数类型是有很多清洁看上去比scanf("%" SCNuMAX, &i);
,和安全很多(我们可以使用strtoumax
线不变较小的类型,让隐式转换手柄的额外位,但与scanf
我们必须做出一个临时uintmax_t
读入)。
这个故事的寓意:避免scanf
。如果您需要它提供的格式,并且不希望(或不能)(更高效地)执行此操作,请使用fgets
/sscanf
。
还需要stdlib include for malloc! – 2011-02-02 10:01:54
@fehergeri:是的,stdio使用`scanf`。我猜标题已被删除的目的? – peoro 2011-02-02 10:02:45
我宁愿使它成为'receiveInput(char * s,size_t len)`,但是你需要跳过构建格式字符串的一些箍。 – 2011-02-02 10:05:29