在使用 C 和 GTK+ 编程时,为什么使用 , 等...以及其他 GLib 函数“g_strdup_printf
更好g_free
” g_strcmp0
?
7 回答
一般来说,GLib 的目的是一个实用和可移植的库。这些本身就是考虑使用它的理由。
您提到的特定函数都在其 C 标准库变体之上提供了一些额外的东西:
g_strdup_printf
就像sprintf
,但实际上为您分配了缓冲区,并为您节省了缓冲区应该有多大的猜测。(返回值应该是g_free
'd。)g_free
就像free
,但检查 NULL 指针。g_strcmp0
类似于strcmp
,但将 NULL 指针视为空字符串,因此将其排在前面。
为了在多个操作系统中保持一致的行为。这是一个便携的东西。
在 Linux 以外的其他一些 unix 环境中,或者如果您的程序是在 Windows 上编译的,则其中一些功能可能不存在或在目标操作系统上表现不同。
使用 glib 版本可确保一致的行为。
GLib 提供了当今任何编程语言所期望的可移植性和基本内容,例如集合类型(链表、数组、哈希表等)。以下是 GLib 可以为您带来的一些好处。
可移植性
GLib 的重点不是移植到 C 标准,而是移植到标准的实现。GLib 负责处理乍一看似乎无用的已知怪癖,直到您需要将代码移植到具有这些讨厌的错误的平台。
让我们g_free
以许多人批评它为例。有些平台free(NULL)
会失败,即使 C99 说它应该可以工作。该检查至少从 1998 年就存在(我在 git 历史记录中对其进行了跟踪)。有人可能会说不再需要它,但即使在 2017 年,我还在一家公司工作,该公司NULL
在打电话之前会进行检查,free
否则它会在他们的嵌入式平台上崩溃。当您想要进行一些严重的内存调试时,它还可以作为代码插入的包装器。
可读性
它通过提供一些包装函数来帮助提高代码的可读性,这些函数不仅可以提高可移植性,还可以帮助您避免许多语言陷阱。你们中有多少人测试malloc
它是否返回NULL
?如果它返回,你们中有多少人有办法恢复NULL
,因为你基本上内存不足?
g_malloc
如果应用程序无法分配您想要的内容,它将中止应用程序,并且在许多应用程序中,这正是您想要的行为。对于可能失败的非常大的分配,您有g_try_malloc
. 这与 malloc 相同,但仍然为您提供了作为可用于检测的包装器的好处。
能写:
char *buffer = g_malloc(30);
/* Do something with it ... */
g_free (buffer);
...解放思想,让开发人员专注于她想要完成的任务。它还避免了您的程序在很久以后崩溃,因为它试图使用NULL
指针进行写入并且您必须跟踪分配。
标准 C 库充满了陷阱,不必对您编写的每一行代码进行微管理是一种解脱。只需阅读手册页的BUGS部分了解某些功能,您就会看到。更少的样板代码来检查错误使代码更易于阅读,从而提高了可维护性并减少了错误。
特征
另一点是 GLib 提供的所有集合类型,您不必重新实现。仅仅因为重新实现一个链表很容易并不意味着你应该这样做。我曾在另一家公司工作,该公司的代码带有多个链表实现,因为一些开发人员只会出现“此处未发明”综合症并重新开发自己的代码。像 GLib 这样的通用的、经过彻底测试的、广泛使用的库有助于避免这种废话。除非您有非常具体的性能限制,否则您不应该重新开发这些东西。
它们的行为在 GTK+ 支持的任何平台上都是明确定义的,而不是有时可能会部分工作的本机函数。
我不得不说,这是出于好意,但执行得并不好。当您尝试多次释放指针时,您的程序不会崩溃和烧毁,这有点好。或对 NULL 字符串进行排序。但这是喜忧参半。它还可以防止您在代码中发现这些讨厌的错误。不仅有一天你会很难移植你的程序,因为你依赖于非标准函数,而且你会很难过,因为你的代码有错误而且你从未发现过。
10 年前,使用 Gnome 库可能是有意义的,但现在它已成为遗留问题。C89 可以说是世界上最标准的语言,具有非常稳定的特性和语法,因此调试别人的 C89 代码是可行的。
相比之下,Gnome 的 glib 在 C 标准之外更改了它的函数特性,因此您不仅可以调试由 C 制成的晦涩的包装器代码,而且您的代码可能会因为 Gnome 更改它的包装器函数而停止工作。
图表 A:g_snprintf()
标准 sprintf() 函数的更安全形式。保证输出不超过n个字符(包括终止的nul字符),所以很容易保证不会发生缓冲区溢出。
另请参见 g_strdup_printf()。
在 1.2.3 之前的 GLib 版本中,如果输出被截断,此函数可能会返回 -1,并且被截断的字符串可能不会以 nul 结尾。在 1.3.12 之前的版本中,此函数返回输出字符串的长度。
g_snprintf() 的返回值符合 ISO C99 中标准化的 snprintf() 函数。请注意,这与传统的 snprintf() 不同,后者返回输出字符串的长度。
格式字符串可能包含位置参数,如 Single Unix Specification 中所指定。
我不太高兴我可以编写(又一个)链表来替换 Gnome,还有另一个版本的 snprintf() 和一堆蹩脚的包装代码,它们默默地 malloc() 的内存,从而打破了一个绝对最大值C 编码:“始终在同一范围内使用 malloc() 和 free()”替换 g_strdup_printf()。
g_strdup_printf ()
类似于标准 C sprintf() 函数,但更安全,因为它计算所需的最大空间并分配内存来保存结果。不再需要时,应使用 g_free() 释放返回的字符串。
再加上在代码中进行大量字符串更改以执行“有用”的事情的快感,例如将 gchar 更改为 char、gint 到 int、gboolean 到 bool 等等,等等,等等,直到我的 Subversion 比较现在令人作呕电话簿。更糟糕的是,您最终不得不更改越来越多的代码,因为这些东西散落在 .h 文件中,所以它会不断扩大,就像一具被划船的尸体,变成一团糟。
如果您正在确定一份合同工作并在任何地方查看glib.h,请运行!!! 拒绝吧!。
PS:下载源代码,删除所有特定于 Gnome 的类型,然后重新编译它以使您自己的“g_”-less 函数排序,有点工作,并且可以节省大量时间。
图表 B:g_strdup_printf()
来自 Gnome 的更可怕的 Gnome crappola。Gnome 有很多“可爱”的函数,比如 g_strdup_vprintf(),它们“神奇地”知道你需要多少存储空间来保存返回的字符串,我有机会看看背后的“魔法”。这赢得了我有史以来最可怕的 C 滥用奖。
如果您通过所有包装器函数继续跟踪 g_strdup_vprintf(),您会来到 gmessages.c 中的这个宝石......
/**
* g_printf_string_upper_bound:
* @format: the format string. See the printf() documentation
* @args: the parameters to be inserted into the format string
*
* Calculates the maximum space needed to store the output
* of the sprintf() function.
*
* Returns: the maximum space needed to store the formatted string
*/
gsize
g_printf_string_upper_bound (const gchar *format,
va_list args)
{
gchar c;
return _g_vsnprintf (&c, 1, format, args) + 1;
}
不仅任何 printf() 函数在绝对为零时都比 snot 慢,您会注意到它们分配了整个字节,是的,整个 gchar c 用于存储,这保证了溢出,但是谁在乎呢?他们获得了 malloc() 所需的字符串的长度——因为他们将转身并再次执行整个 printf()——这一次,“神奇地”只有足够的存储空间。
当然,他们会在大小上增加 +1,所以你会有空间容纳一个 nul 终止符,保证,当然代价是可怕的,但他们把它隐藏在代码中很深,他们押注你'只会放弃并盲目使用它,而不会注意到这是多么巨大的大脑放屁。啧啧,谢谢各位。我只是喜欢我的代码爬行。
不要让 _g_vsnprintf() 函数让你失望,因为在 gprintfint.h 中你会发现 little gem 只是普通老香草 vsnprintf() 的另一个名称;
/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the GLib Team and others 2002. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
#ifndef __G_PRINTFINT_H__
#define __G_PRINTFINT_H__
#ifdef HAVE_GOOD_PRINTF
#define _g_printf printf
#define _g_fprintf fprintf
#define _g_sprintf sprintf
#define _g_snprintf snprintf
#define _g_vprintf vprintf
#define _g_vfprintf vfprintf
#define _g_vsprintf vsprintf
#define _g_vsnprintf vsnprintf
#else
#include "gnulib/printf.h"
#define _g_printf _g_gnulib_printf
#define _g_fprintf _g_gnulib_fprintf
#define _g_sprintf _g_gnulib_sprintf
#define _g_snprintf _g_gnulib_snprintf
#define _g_vprintf _g_gnulib_vprintf
#define _g_vfprintf _g_gnulib_vfprintf
#define _g_vsprintf _g_gnulib_vsprintf
#define _g_vsnprintf _g_gnulib_vsnprintf
#endif
#endif /* __G_PRINTF_H__ */
强烈建议您在使用 Gnome 之前再次观看绿野仙踪,这样您就会知道不要看幕后。欢迎来到我的噩梦!
任何认为 Gnome 比 C 更稳定的人都严重缺乏批判性思维。您可以用性能和透明度换取一些在 STL 中做得更好的好东西。
这是一个更新。看起来开发人员意识到了他们的错误:
g_mem_is_system_malloc 自 2.46 版以来已被弃用,不应在新编写的代码中使用。
GLib 总是使用系统 malloc,所以这个函数总是返回 TRUE。
检查 g_malloc() 使用的分配器是否是系统的 malloc 实现。如果它返回 TRUE,则使用 malloc() 分配的内存可以与使用 g_malloc() 分配的内存互换使用。此函数对于避免非基于 GLib 的 API 返回的已分配内存的额外副本很有用。
https://developer.gnome.org/glib/stable/glib-Memory-Allocation.html#g-mem-is-system-malloc