2010-07-21 45 views
4

我在一些脚本语言中相当能干,但我终于强迫自己学习C语言。我只是在玩一些基本的东西(现在是I/O)。我怎样才能分配堆内存,在分配的内存中存储一​​个字符串,然后吐出来呢?这就是我现在所拥有的,我怎样才能使它正常工作?Malloc和scanf

​​

目前我收到奇怪的输出像'8'\'。

+0

你并不需要从'的malloc和''包括投的返回类型'在ISO C(3)。 – sarnold 2010-07-21 02:34:34

+0

值得指出的是,您应该在这里使用堆栈。 – dicroce 2010-07-21 02:36:27

回答

7
char *toParseStr = (char*)malloc(10); 
    printf("Enter string here: "); 
    scanf("%s",toParseStr); 
    printf("%s",toParseStr); 
    free(toParseStr); 

首先,scanf中的字符串指定了它将要接收的输入。为了在接受键盘输入之前显示一个字符串,使用printf,如图所示。

其次,您不需要取消toParseStr,因为它指向的大小为10的字符数组与您分配的malloc如果您正在使用的功能,它会指向另一个内存位置,然后&toParseStr是必需的。

例如,假设您想编写一个函数来分配内存。然后,您需要&toParseStr,因为您正在更改指针变量的内容(这是内存中的一个地址---您可以通过打印其内容来查看自己的内容)。

void AllocateString(char ** ptr_string, const int n) 
{ 
    *ptr_string = (char*)malloc(n) 
} 

正如你可以看到,它接受char ** ptr_string其内容,其存储一个指示字的存储器位置的指针,其存储所分配的块的第一个字节的存储器地址(malloc操作之后)的n字节(现在它有一些垃圾内存地址,因为它是未初始化的)。

int main(int argc, char *argv[]) 
{ 
    char *toParseStr; 
    const int n = 10; 
    printf("Garbage: %p\n",toParseStr); 
    AllocateString(&toParseStr,n); 
    printf("Address of the first element of a contiguous array of %d bytes: %p\n",n,toParseStr); 

    printf("Enter string here: "); 
    scanf("%s",toParseStr); 
    printf("%s",toParseStr); 
    free(toParseStr); 

    return 0; 
} 

第三,建议释放你分配的内存。即使这是你的整个程序,并且这个内存将在程序退出时被释放,但这仍然是一个好习惯。

+2

即使在一个小程序中也可以释放+1。让我想起“小滴滴变成海洋”。 ;-) – 2010-07-21 03:54:01

+0

您应该在打印提示符并调用scanf之间调用'fflush(stdout);'。大多数实现会做到这一点,让你彬彬有礼,但它不是强制性的。 – 2010-07-21 11:07:47

3

,因为它已经是一个指针

也呼吁free(toParseStr)你不需要toParseStr之前&scanf事后

+1

根据bball的系统,可能需要在该printf中放置一个“\ n”,以便正确显示事物。另外,10个字符是一个非常短的字符串。 – George 2010-07-21 02:35:56

+1

虽然这是事实,但这并不是问题的根源(在这种情况下'&'是不必要的,但是无害的)。 – 2010-07-21 02:36:14

+2

@Jerry它是无害的,因为格式说明符没有指定任何参数,但是一旦他修复它在你的答案中有一个%s就会导致段错误 – 2010-07-21 02:37:42

9

你需要给scanf转换格式,因此它知道你想读一个字符串 - 现在,你只是显示在你分配的内存中发生的任何垃圾。而不是试图描述所有的问题,这里的一些代码,至少应该接近工作:

char *toParseStr = malloc(10); 
printf("Enter a string: "); 
scanf("%9s", toParseStr); 
printf("\n%s\n", toParsestr); 
/* Edit, added: */ 
free(toParseStr); 
return 0; 

编辑:在这种情况下,free荷兰国际集团字符串不作任何真正的区别,但像其他人那样指出,它是仍然是培养的好习惯。

+1

+1正确使用'scanf'中的'%9s'。 – sarnold 2010-07-21 02:42:47

+1

与某些人的看法相反,在执行读取操作之前,刷新'stdout'对于确保提示出现是不必要的,除非实现被正确地破坏了。对于那些真正关心的人,请参阅§7.19.3。如果可以确定*不*指向交互设备,'stdout'只能被完全缓冲。 – 2010-07-21 03:02:06

+0

你错了。 'stdout'仍然可以**行缓冲**,这意味着直到打印换行符才会显示任何内容。 POSIX建议实现在读取时刷新stdout和其他这样的行缓冲流,但是扫描行缓冲流的打开文件列表(特​​别是使用线程和锁定)并且实现可能选择不执行所以出于很好的理由。据我所知,ISO C对缓冲语义几乎没有要求。所以你**应该**冲洗! – 2010-07-21 11:10:43

0

首先,使程序不能正常工作的错误:scanf(3)采用格式字符串,就像printf(3)一样,不是为用户打印的字符串。其次,您传递的是指针toParseStr的地址,而不是指针toParseStr

我也从您的电话malloc(3)删除不必要的演员。

你的程序仍然需要改进的地方是使用scanf(3)a选项为你分配内存 - 这样一些小丑将十个字符放入你的字符串中并不会开始st on无关的内存。 (是的,C就会让别人几乎覆盖整个地址空间有了这个程序,书面巨安全漏洞。:)

#include <stdio.h> 
#include <stdlib.h> 

int main(int argc, char *argv[]) 
{ 
    char *toParseStr = malloc(10); 
    printf("Enter a short string: "); 
    scanf("%s",toParseStr); 
    printf("%s\n",toParseStr); 
    return 0; 
} 
+1

'scanf'没有'a'选项。这是一个GNU扩展,它不仅是非标准的,而且还有** CONFLICTS **和ISO C('%a'是读取浮点数的说明符之一!)。应该绝对避免。 – 2010-07-21 11:15:29

+0

谢谢;我不知道这个扩展与ISO C. – sarnold 2010-07-22 09:19:51

4

数据使用scanf()(或fscanf()你不控制)与标准“ %s“说明符是让你自己陷入缓冲区溢出困境的一种近乎确定的方式。

经典的例子是,我在你的程序中输入字符串“这个字符串超过10个字符”,混沌将随之而来,猫和狗将开始一起睡觉,裸露的奇点可能会出现并消耗地球(大多数人只是陈述“未定义的行为”,但我认为我的描述更好)。

我积极劝阻使用无法提供保护的功能。我会敦促你(特别是作为C的新手)使用fgets()来读取你的输入,因为你可以更容易地控制缓冲区溢出,它比scanf()更适合于简单的线路输入。

一旦你有了一条线,你就可以在其上调用sscanf()到你心中的内容,顺便说一句,你不需要在这个特定的情况下做,因为你只是得到一个原始字符串。

我会用:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#define BUFFSZ 10 

int main(int argc, char *argv[]) { 
    char *toParseStr = malloc(BUFFSZ+2); 
    if (toParseStr == NULL) { 
     printf ("Could not allocate memory!\n"); 
     return 1; 
    } 
    printf ("Enter a string: "); 
    if (fgets (toParseStr, BUFFSZ+2, stdin) == NULL) { 
     printf ("\nGot end of file!\n"); 
     return 1; 
    } 
    printf("Your string was: %s",toParseStr); 
    if (toParseStr[strlen (toParseStr) - 1] != '\n') { 
     printf ("\nIn addition, your string was too long!\n"); 
    } 
    free (toParseStr); 
    return 0; 
} 
+1

+1有冲突,但我补充说,虽然'fgets'确实有优势,scanf和fscanf都有防止缓冲区溢出的措施。 – 2010-07-21 02:46:06

+0

@Jerry虽然我很少看到人们用“%s”使用宽度说明符,但这是一个很好的观点:-)因为我的大多数控制台I/O代码都倾向于具有基于行的输入,所以%s不适合获得空白空间。但是,由于您的答案在这种情况下确实是正确的,所以您+1。 – paxdiablo 2010-07-21 03:02:12

+2

另一个有趣的可能性是'scanf(“%9 [^ \ n]”,your_string);'''scanf'的面向行的字符串输入,无论值多少钱。 – 2010-07-21 03:10:23