有很多方法可以修复显示的代码片段。这段代码显示了其中的三个。如问题的 cmets 所述,您需要在循环内分配至少 2 个字符(因为 %[…] 扫描集创建一个以 null 结尾的字符串),但是您可以使用 %1[^, ] 作为转换来获得一个字符一次。请注意,您需要测试 sscanf() 的返回值以检查您是否得到了预期的结果。您还需要增加 read 以免一遍又一遍地读取相同的字符。在更一般的情况下,您会使用%n 来告知扫描停止的位置(请参阅Using sscanf() in a loop)。扫描集不会跳过空格(%c 或 %n 也不会——所有其他标准转换都会跳过前导空格,包括换行符)。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { LIST_SIZE = 3 };
static void free_array(size_t n, char **arr)
{
for (size_t i = 0; i < n; i++)
free(arr[i]);
free(arr);
}
int main(void)
{
char str[] = "rony is a man";
char **string_of_letters;
char *read = str;
printf("Variant 1:\n");
string_of_letters = (char **)malloc(LIST_SIZE * sizeof(char *));
for (int i = 0; i < LIST_SIZE; i++)
{
string_of_letters[i] = (char *)malloc(2 * sizeof(char));
if (sscanf(&read[i], "%1[^, ]", string_of_letters[i]) != 1)
printf("Conversion failed on %d\n", i);
else
printf("%s\n", string_of_letters[i]);
}
free_array(LIST_SIZE, string_of_letters);
printf("Variant 2:\n");
string_of_letters = (char **)malloc(LIST_SIZE * sizeof(char *));
for (int i = 0; i < LIST_SIZE; i++)
{
string_of_letters[i] = (char *)malloc(sizeof(char));
*string_of_letters[i] = read[i];
printf("%c\n", *string_of_letters[i]);
}
free_array(LIST_SIZE, string_of_letters);
printf("Variant 3:\n");
strcpy(str, " r o n");
char char_list[LIST_SIZE + 1]; // NB: + 1 provides space for null byte
int offset = 0;
for (int i = 0; i < LIST_SIZE; i++)
{
int pos;
printf("Offset = %d: ", offset);
if (sscanf(&read[offset], " %1[^, ]%n", &char_list[i], &pos) != 1)
{
printf("Conversion failed on character index %d\n", i);
break;
}
else
printf("%c\n", char_list[i]);
offset += pos;
}
return 0;
}
显示的代码在运行 macOS 10.13.6 High Sierra 和 Valgrind 3.14.0.GIT(从 Git 中提取的版本,而不是正式发布的一组源代码)的 Mac 上在 Valgrind 下干净地运行。
输出:
Variant 1:
r
o
n
Variant 2:
r
o
n
Variant 3:
Offset = 0: r
Offset = 3: o
Offset = 5: n
正如已经观察到的那样,问题中的代码可以正常工作,更多的是偶然而不是设计。 malloc() 返回的指针受到约束,因此它指向可用于任何目的的内存位置:
C11§7.22.3 Memory management functions
¶1 ...
如果分配成功则返回的指针经过适当对齐,以便可以分配给
指向具有基本对齐要求的任何类型对象的指针,然后使用
访问分配的空间中的此类对象或此类对象的数组(...)。 …
这意味着由于其他类型的对齐要求,单个char 的连续分配将不连续。通常,您会发现分配的最小空间为 8 或 16 字节(在 32 位或 64 位平台上),但这绝不是必需的。这确实意味着分配的空间通常比您请求的要多(特别是如果您请求单个字节)。但是,访问该额外空间会导致未定义的行为。您对示例代码的运行表明,有时“未定义行为”的行为或多或少符合预期。