fscanf 的不同输入类型

Different input types for fscanf

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

也就是说,有三种(看似不同的)方式来传递"strings"(字符数组)。

一些假设:
1. fp 是一个有效的文件指针。
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];)会崩溃?

编辑:

我很清楚您需要传递一个足够大的缓冲区来包含行中的数据,我想知道为什么它仍在工作并且没有崩溃情况。

That being said, there are three (seemingly different) ways to pass "strings" around(array of chars).

要将 C-"string" 传递给 scanf() & 朋友,只有一种方法:传递足够有效内存的地址。

如果您不这样做,代码将调用臭名昭著的未定义行为,这意味着任何事情都可能发生,从崩溃到看似 运行 正常。

Why does a pointer with malloc of 1 can contain longer texts?

理论上,它不能不引起未定义的行为。然而,实际上,当您分配一个字节时,分配器会为您提供它支持的最小大小的一小块内存,通常足以容纳 8..10 个字符而不会导致崩溃。额外的内存用作防止崩溃的 "padding"(但它仍然是未定义的行为)。

Since the pointer content doesn't seem to matter, why does a null pointer crash, I would expect the allocated pointer to crash as well, since it points to a small piece of memory.

另一方面,

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

Why does the pointer work, but an array(char temp[1]) crashes?

因为分配数组后没有任何额外的 "padding" 内存。请注意,不保证会发生崩溃,因为数组后面可能跟有未使用的内存字节,您的字符串可能会损坏这些内存而不会产生任何后果。

因为空指针没有分配内存

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

所以当你用new或者定义一个数组(小)分配内存时,你会在堆中得到一块内存。实际可用的 space 更大并且可以(经常)超过您请求的数量,因此写(和读)多于请求实际上是安全的。但理论上,它是一个 UB,应该 使程序崩溃

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

小数组可能比 newmalloc 更频繁地崩溃,因为它们并不总是从堆中分配,并且可能 没有任何额外的 space 在他们之后,所以越写越危险。然而,它们通常位于未使用(未分配)的内存区域之前,因此有时您的程序可能不会崩溃,但会获取损坏的数据。

My understanding of fscanf:

grabs a line from a file and based on format, stores it to a string.

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

在您的特定用法中,

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

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

That being said, there are three (seemingly different) ways to pass "strings" around(array of chars).

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

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

  1. Why does a pointer with malloc of 1 can contain longer texts?

指针只包含它本身。它指向的 space 包含任何其他内容,例如文本。如果只分配一个字节,那么分配的 space 只能包含一个字节。如果您通过尝试在指针指向的位置写入更长的字符序列来超出该一个字节,那么您将调用 未定义的行为。特别是,C 不保证会产生错误,或者程序不会像您预期的那样运行,但是各种破坏 可以 接踵而至,没有限制。

  1. Since the pointer content doesn't seem to matter, why does a null pointer crash, I would expect the allocated pointer to crash as well, since it points to a small piece of memory.

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

  1. Why does the pointer work, but an array(char temp[1];) crashes?

您没有演示您的 1 字符数组替代方案,但再次超出对象的边界(在本例中为数组)会产生未定义的行为。它是 undefined 因此假设行为与超出已分配对象的边界相同,甚至这些行为中的任何一个是一致的都是不合理的。