fgets之间的区别 - 数组与指针

时间:2017-01-03 18:26:22

标签: c arrays pointers

我已经阅读过有关数组和指针差异的文章,但如果我想使用scanf / fgets,我仍然不确定它是否会影响。

假设我有两个读单词的函数。

void read1() {
    char *text;
    text = malloc(sizeof(char) * 50);
    fgets(text, 50, stdin);
}

void read2() {
    char text[50];
    fgets(text, 50, stdin);
}

这些代码之间有什么区别吗?

5 个答案:

答案 0 :(得分:4)

编写带指针的函数带来的大部分便利是它们不关心内存位置,只要指针指向的内存满足函数的大小要求。

特别是,fgets在两个调用之间没有区别:它只关心传递给它的指针ptr对写length个字符有效。

即使与fgets没有区别,您的代码也可能非常关心指针在内存中的位置:例如,您的第一个代码段必须free(text)以避免内存泄漏。相比之下,您的第二个代码段不需要释放test。但是,即使函数的作用域结束,第一个函数中的text仍然有效,如果函数的返回类型被适当更改,则可以返回给调用者。如果没有复制,第二个函数就无法做到这一点,因为text在函数结束时就会超出范围。

答案 1 :(得分:1)

从技术上讲,第一个代码段没有对malloc()进行成功检查,因此可以通过取消引用NULL指针malloc()失败案例来调用UB。此外,这会面临memory leak的风险,因为您既没有free()指针,也没有将其返回给调用者,以便稍后调用free()

否则,在 此特定 的情况下,与“数组和指针”视点没有太大区别,无论如何因为它们(传递给fgets())都指向指定大小的有效内存块。

详细说明,引用C11,章节§6.3.2.1

  

除非它是sizeof运算符,_Alignof运算符或者&运算符的操作数。   一元char text[50];运算符,或者是用于初始化数组的字符串文字,一个包含的表达式   type ''类型''的数组被转换为类型''指向类型''指针的表达式   到数组对象的初始元素,而不是左值。 [....]

因此,对于使用fgets(text, 50, stdin); 的代码段,调用

char

使第一个参数成为指向50 char s数组的第一个元素的指针,其行为类似于将指针传递给大小为50 {{1}} s的内存块。

答案 2 :(得分:0)

我认为你在询问将数组和指针作为函数参数传递和声明函数参数(如数组和指针)之间的区别。

根据C标准(6.3.2.1 Lvalues,数组和函数指示符)

  

3除非它是sizeof运算符或一元&的操作数。   operator,或者是用于初始化数组的字符串文字, an   具有类型''数组类型''的表达式将转换为   带有''指向类型'的指针的表达式,指向初始值   数组对象的元素并且不是左值。如果是数组   对象具有寄存器存储类,行为未定义。

另一方面(6.7.6.3函数声明符(包括原型))

  

7应调整参数声明为''类型数组''   ''限定类型''的指针,类型限定符(如果有的话)   那些在数组类型推导的[和]内指定的......

因此在这个函数示例中

void read2() {
    char text[50];
    fgets(text, 50, stdin);
}

数组text被隐式转换为类型为char *的指针,指向数组的第一个元素。这是函数处理指针。指针是指向具有自动存储持续时间的数组的第一个元素,还是指向具有动态存储持续时间的数组的第一个元素,这一点并不重要。

此外,例如,这些函数声明了相同的一个函数

void f( char s[50] );
void f( char s[1] );
void f( char s[] );
void f( char *s );

因为参数调整为指针。

答案 3 :(得分:0)

简短回答:在这些例子中,它不会有所作为,但你应该在返回之前释放(https://linux.die.net/man/3/free)你通过malloc创建的那个。

长(并且有点简化)回答:

简化的解释是,对于fgets,名为text的两个变量都是指针。指针指向某些东西的指针,在C的情况下,它们指向一个记忆区域。

在您的第一个示例中,您指向的内存区域位于堆上,它是动态分配的,它具有全局范围(您可以返回此变量,您可以将其设置为全局指针并在任何点使用它您的代码可以将其传递给另一个函数)并且您有责任将其释放(在您的代码中,您没有导致内存泄漏(https://en.wikipedia.org/wiki/Memory_leak)。

在第二个示例中,您指向的内存区域位于堆栈(https://en.wikipedia.org/wiki/Stack-based_memory_allocation)上,该堆栈是程序启动时创建的内存区域,用于保存变量(以及其他一些值)对于函数调用和静态变量,我在这个答案中没有讨论过)。此变量的范围是当前函数以及从中调用的任何函数,但是当您从read2返回时,内容将被及时覆盖,因此您不能返回此值或将其设置为全局指针,否则您将运行后来陷入困境。

答案 4 :(得分:-2)

  • fgets()char*作为输入参数,传递给char text[50]fegts转换为char*到数组的第一个元素。 text中的read1()已经是char*。所以fgets()调用是一样的。

  • 即使控件到达函数text的{​​{1}},动态分配的read1()中的差异}仍然存在。要在程序执行期间释放,您必须明确致电read1()

  • free()中的text,只要控件达到main()的{​​{1}}或} 结束它变得自由,你无法进一步访问它。 (如果您尝试这样做,那么您有Undefined Behavior)。