考虑一下:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
char *create_filename(const char *prefix,
const struct tm *timestamp,
const char *suffix)
{
/* YYYYMMDD-HHMMSS is 15 characters. */
const size_t timestamp_len = 15;
const size_t prefix_len = (prefix) ? strlen(prefix) : 0;
const size_t suffix_len = (suffix) ? strlen(suffix) : 0;
char *buffer;
size_t len;
/* Abort if no timestamp provided. */
if (!timestamp) {
fprintf(stderr, "create_filename(): No timestamp! (timestamp == NULL)\n");
exit(EXIT_FAILURE);
}
/* Allocate enough memory for prefix, timestamp, suffix,
and the end-of-string nul byte, '\0'. */
buffer = malloc(prefix_len + timestamp_len + suffix_len + 1);
if (!buffer) {
fprintf(stderr, "create_filename(): Out of memory.\n");
exit(EXIT_FAILURE);
}
/* Copy the prefix part, if any. */
if (prefix_len > 0)
memcpy(buffer, prefix, prefix_len);
/* Copy the timestamp part. */
len = strftime(buffer + prefix_len, timestamp_len + 1, "%Y%m%d-%H%M%s", timestamp);
if (len < 1 || len > timestamp_len) {
free(buffer);
fprintf(stderr, "BUG in create_filename(): timestamp_len is too small for strftime() pattern.\n");
exit(EXIT_FAILURE);
}
/* Copy the suffix part, if any. */
if (suffix_len > 0)
memcpy(buffer + prefix_len + len, suffix, suffix_len);
/* Add the terminating nul byte. */
buffer[prefix_len + len + suffix_len] = '\0';
return buffer;
}
希望 cmets 足够清晰,可以让新程序员继续学习。
请注意,表达式const size_t prefix_len = (prefix) ? strlen(prefix) : 0; 使用三元运算符(expression) ? (if-true) : (if-false)。 strlen(NULL) 不安全,因此需要单独检查; (prefix) 进行检查。 (相当于(prefix != NULL),可以读作"if prefix is non-NULL"。)简单来说,如果prefix为NULL,则计算为0,否则为长度字符串:
size_t prefix_len;
if (prefix != NULL) {
prefix_len = strlen(prefix);
} else {
prefix_len = 0;
}
在你的主函数中,你可以这样使用create_filename() 函数:
int main(void)
{
time_t now;
struct tm *nowtm;
char *path;
now = time(NULL);
nowtm = localtime(&now);
path = create_filename(NULL, &nowtm, ".txt");
printf("The path is '%s'.\n", path);
free(path); path = NULL;
return EXIT_SUCCESS;
}
您可以在create_filename() 调用(分配它的位置)之后使用path,就好像它是一个字符数组一样。当您不再需要它时,请致电free(path)。这就是真正简单的动态内存管理。
当程序退出时(使用主函数中的return,或exit() 或_Exit() 函数),操作系统将自动释放所有动态分配的内存。因此,从技术上讲,不需要在主函数的 return 之前或 exit() 之前使用 free()。不过,出于学习目的,养成跟踪free()s 的习惯是个好主意。
最常见的错误是一个接一个,释放后使用。在create_filename() 中,不可能出现非一错误,因为会计算前缀和后缀的长度,并明确检查strftime() 生成的模式的长度。
为了避免释放后使用,我通常在释放指针后将指针显式设置为 NULL。 (这在编程中通常被称为“中毒”:将值设置为可识别的,通常是无效的值,以便更容易检测到 use-after-free。)许多程序员不打扰,但他们'只是很傻。这不像它会减慢程序或造成任何伤害;但它确实使调试更容易。在这种情况下,如果您在 path 被释放并设置为 NULL 后尝试使用它,您的程序应该会崩溃(由于段冲突、NULL 指针取消引用或类似错误;这取决于您使用的操作系统)。简而言之,它只是让查找错误变得更加容易。
如果您没有在free(path); 之后设置path = NULL;,并且不小心在其后添加了printf("path is now '%s'\n", path);,它可能会起作用,也可能不会,具体取决于系统,月相,以及由于未定义的行为可能有朝一日飞出你的鼻子的鼻小鬼的情绪。 (开个玩笑:未定义的行为实际上是未定义的行为,尽管任何事情都可能发生,但我们相信它不会破坏您的计算机或破坏您的鼻子。只是让程序以可靠的方式运行真的很难如果您有任何未定义的行为。访问已释放的指针或 NULL 指针是未定义的行为。)