fscanf的不同输入类型

时间:2017-07-29 15:11:25

标签: c string scanf c-strings

我对fscanf的理解:
 从文件中抓取一行并根据格式将其存储到字符串中。

话虽如此,有三种(看似不同的)方式传递"字符串"周围(字符数组)。

一些假设:
1. fp是一个有效的FILE指针 2.文件中有1行,内容为" Something"

具有已分配内存的指针

char* temp = malloc(sizeof(char) * 1); // points to some small part in mem.
int resp = fscanf(fp,"%s", temp); 
printf("Trying to print: %s\n",temp); // prints "Something" (that's what's in the file)

具有预定义长度的数组(它与指针不同!)

char temp[100]; // this buffer MUST be big enough, or we get segmentation fault
int resp = fscanf(fp,"%s", temp); 
printf("Trying to print: %s\n",temp); // prints "Something" (that's what's in the file)

空指针

char* temp; // null pointer
int resp = fscanf(fp,"%s", temp); 
printf("Trying to print: %s\n",temp); // Crashes, segmentation fault

因此出现了一些问题!

  1. malloc为1的指针如何包含更长的文本?
  2. 由于指针的内容似乎并不重要,为什么空指针会崩溃?我希望分配的指针也会崩溃,因为它指向一小段内存。
  3. 为什么指针有效,但数组(char temp[1];)崩溃?
  4. 编辑:

    我很清楚你需要传递一个足够大的缓冲区来包含来自该行的数据,我想知道为什么它仍在工作并且在其他情况下不会崩溃

4 个答案:

答案 0 :(得分:2)

  

我对fscanf的理解:

     

从文件中获取一行并基于   格式,将其存储到字符串。

不,这包含一些严重而重要的错误观念。 fscanf()按照指定的格式从文件中读取,以便为其第三个和后续参数指向的部分或全部对象赋值。它不一定读取整行,但另一方面,它可能读取不止一行。

在您的特定用法中,

int resp = fscanf(fp,"%s", temp);

,它试图跳过任何前导空格,包括但不限于空行和空行,然后将字符读入指向字符数组,直到第一个空格字符或文件末尾。在任何情况下它都不会消耗它填充数组内容的行的行终止符,但如果在至少一个非空白字符后面的行上有其他空格,它甚至都不会那么远(尽管不是您描述的特定样本输入中的案例。)

  

话虽如此,有三种(看似不同的)方式传递"字符串"周围(字符数组)。

字符串不是C中的实际数据类型。字符数组是,但这样的数组不是"字符串"在C意义上,除非它们包含至少一个空字符。此外,在这种情况下,大多数情况下C字符串函数仅对这些数组的部分进行操作,直到并包括第一个空值,因此最好将这些部分表示为"字符串"。

有多种方法可以获得可以被视为字符串的字符序列的存储空间,但只有一种方法可以传递它们:通过指向第一个字符的指针。无论是通过声明字符数组,字符串文字还是通过为其分配内存来获取存储,都只能通过指针访问 。即使通过将索引运算符[]应用于数组变量的名称来声明char数组和访问元素,实际上仍然使用指针来访问内容。

  
      
  1. 为什么malloc为1的指针可以包含更长的文本?
  2.   

指针除了自身之外不包含任何内容。它指向的空间包含其他任何内容,例如文本。如果只分配一个字节,则分配的空间只能包含一个字节。如果通过尝试写入指针所指向的较长字符序列来超出该一个字节,则调用未定义行为。特别是,C不保证会产生错误,或者程序将无法按预期运行,但是所有方式的 都可以无限制地发生。

  
      
  1. 由于指针内容似乎不重要,为什么空指针崩溃,我希望分配的指针崩溃为   好吧,因为它指的是一小段记忆。
  2.   

尝试取消引用无效指针(包括但不限于空指针)也会产生未定义的行为。崩溃完全在可能的行为范围内。在这种情况下,C不保证崩溃,但是某些实现可靠地提供了这种情况。

  
      
  1. 为什么指针有效,但数组(char temp [1];)崩溃?
  2.   

你没有演示你的单字符数组替代,但是再次,超越对象的边界 - 在这种情况下是一个数组 - 产生未定义的行为。它是 undefined 所以没有理由认为行为与超出已分配对象的边界相同,或者甚至其中任何一个行为都是一致的。

答案 1 :(得分:1)

  

话虽如此,有三种(看似不同的)方式传递"字符串"周围(字符数组)。

传递C - "字符串"至scanf()&朋友只有一种方式:传递足够有效记忆的地址。

如果你没有代码会调用infamouse Undefined Behavior,这意味着任何事情都可能发生,从崩溃到看似正常运行。

答案 2 :(得分:0)

  

为什么malloc为1的指针可以包含更长的文本?

理论上讲,它不能不导致未定义的行为。但实际上,当你分配一个字节时,分配器会给你一小块它支持的最小大小的内存,这通常足够8..10个字符而不会导致崩溃。附加内存用作防止崩溃的“填充”(但它仍然是未定义的行为)。

  

由于指针内容似乎不重要,为什么空指针崩溃,我希望分配的指针也会崩溃,因为它指向一小段内存。

另一方面,

空指针即使对于空字符串也是不够的,因为您需要空终止符。因此,它是一个有保证的UB,它在大多数平台上表现为崩溃。

  

为什么指针有效,但数组(char temp[1])崩溃?

因为数组的分配没有任何额外的“填充”内存。请注意,无法保证崩溃,因为数组后面可能没有未使用的内存字节,您的字符串可能会损坏而不会产生任何后果。

答案 3 :(得分:0)

因为空指针没有分配内存

当你请求一小段内存时,它是从名为" heap"的内存块中分配的。堆总是以块或页为单位进行分配和释放,总是比几个字节大一些,通常是几KB。

因此,当您使用new或通过定义数组(小)分配内存时,您将在堆中获得一块内存。实际可用空间较大,可以(经常)超过您请求的数量,因此写入(和读取)比请求的数据更安全。但理论上,它是一个UB而使程序崩溃

创建空指针时,它指向0,无效地址,无法读取或写入。因此,它保证程序会崩溃,通常是由于分段错误。

小型阵列可能比newmalloc更频繁地崩溃,因为它们并不总是从堆中分配,并且可能会在之后没有任何额外空间,所以写超过限制更危险。但是,它们经常在未使用的(未分配的)内存区域之前,因此有时您的程序可能不会崩溃,而是会损坏数据。