为什么使用 GLib 函数?
在使用 C 和 GTK+ 进行编程时,为什么使用 g_strdup_printf
、g_free
、g_strcmp0
等以及其他 GLib 函数“更好”?
While programming in C and GTK+, why is it "better" to use g_strdup_printf
, g_free
, g_strcmp0
etc... and fellow GLib functions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
一般来说,GLib 的目的是一个实用程序和可移植性库。这些本身就是考虑使用它的理由。
您提到的特定函数都在其 C 标准库变体之上提供了一些额外的功能:
g_strdup_printf
类似于sprintf
,但实际上为您分配了缓冲区,并省去了您的猜测缓冲区应该有多大。 (返回值应为g_free
'd。)g_free
与free
类似,但会检查 NULL 指针。g_strcmp0
与strcmp
类似,但将 NULL 指针视为空字符串,因此将其排序在前面。In general, GLib's purpose is a utility and portability library. Those in itself are reasons to consider using it.
The specific functions you mention all offer something extra on top of their C standard library variants:
g_strdup_printf
is likesprintf
, but actually allocates the buffer for you and saves you the guesswork of how large the buffer should be. (The return value should beg_free
'd.)g_free
is likefree
, but checks for a NULL-pointer.g_strcmp0
is likestrcmp
, but treats a NULL-pointer like an empty string, and thus sorts it in front.为了在多个操作系统中保持一致的行为。这是一个可移植性的事情。
在 Linux 以外的其他一些 UNIX 环境中,或者如果您的程序是在 Windows 上编译的,其中某些函数可能不存在或在目标操作系统上表现不同。
使用 glib 版本可确保行为一致。
For consistent behavior in multiple operating systems. It's a portability thing.
In some other unix environments other than Linux, or if your program is compiled on windows, some of those functions may not exist or behave differently on the target operating system.
Using the glib versions ensure consistent behavior.
GLib 提供了您现在期望从任何编程语言中获得的可移植性和基本功能,例如集合类型(链接列表、数组、哈希表等)。以下是 GLib 可以给您带来的一些好处。
可移植性
GLib 的重点不是可移植到 C 标准,而是可移植到该标准的实现。 GLib 负责处理已知的怪癖,这些怪癖乍一看似乎毫无用处,直到您需要将代码移植到具有这些令人讨厌的错误的平台上。
让我们以
g_free
为例,因为很多人都批评它。 在某些平台上,free(NULL)
会失败,即使 C99 说它应该可以工作。该检查至少从 1998 年就存在(我在 git 历史记录中跟踪过它)。有些人可能会说不再需要它了,但即使在 2017 年,我也在一家公司工作,该公司在调用free
之前会检查NULL
,因为否则它会在其嵌入式平台上崩溃。当您想要进行一些严重的内存调试时,它还可以用作代码检测的包装器。可读性
它通过提供一些包装函数来帮助提高代码的可读性,这些函数不仅提高了可移植性,而且还帮助您避免了许多语言陷阱。你们中有多少人测试
malloc
以查看它是否返回NULL
?如果返回NULL
,你们中有多少人有办法恢复,因为您基本上已经内存不足了?如果 g_malloc 无法分配您想要的内容,它将中止应用程序,并且在许多应用程序中,这正是您想要的行为。对于可能失败的非常大的分配,您可以使用
g_try_malloc
。这与 malloc 相同,但仍然为您提供了作为可用于检测的包装器的好处。能够写:
...解放思想,让开发人员专注于她想要实现的任务。它还可以避免程序稍后崩溃,因为它尝试使用 NULL 指针进行写入,并且您必须跟踪分配情况。
标准 C 库充满了陷阱,不必对您编写的每一行代码进行微观管理是一种解脱。只需阅读某些功能的联机帮助页的 BUGS 部分,您就会明白。使用较少的样板代码来检查错误使代码更易于阅读,从而提高了可维护性并减少了错误。
功能
另一点是 GLib 提供的所有集合类型,您无需重新实现。仅仅因为重新实现链表很容易并不意味着您应该这样做。我在另一家公司工作,该公司发布了带有多个链表实现的代码,因为一些开发人员只是患有“不是这里发明的”综合症,并且会重新开发自己的代码。像 GLib 这样的通用的、经过彻底测试的、广泛使用的库有助于避免这种胡言乱语。除非您有非常具体的性能限制,否则您不应该重新开发这些东西。
The GLib provides portability and basic stuff you'd expect nowadays from any programming language, like collection types (linked lists, arrays, hash tables, etc.). Here are some of the benefits GLib can give you.
Portability
The point of the GLib is to be portable not to the C standard, but to the implementations of the standard. The GLib takes care of the known quirks that might seem useless at first sight until you need to port your code to a platform that has those nasty bugs.
Let's take the example of
g_free
, as many criticize it. There are platforms wherefree(NULL)
will fail, even if C99 says it should work. That check exists since at least 1998 (I tracked it in git history). Some may say it's not needed anymore, but even in 2017 I worked at a company that checks forNULL
before callingfree
because otherwise it would crash on their embedded platform. It also serves as a wrapper for intrumentation of your code when you want to do some serious memory debugging.Readability
It helps improving the readability of your code by providing some wrapper functions that not only improve portabitlity, but also help you avoid many language pitfalls. How many of you test
malloc
to see if it returnsNULL
? How many of you have a way to recover if it returnsNULL
, since you're basicly out of memory?g_malloc
will abort the application if it can't allocate what you want, and in many applications, this is just the behavior you want. For very big allocations that may fail, you haveg_try_malloc
. That is the same as malloc but still gives you the benefit of being a wrapper that may be used for instrumentation.Being able to write:
...frees the mind and lets the developer focus on the task she's trying to achieve. It also avoids having your program crash much later because it's trying to write using
NULL
pointer and you have to track down the allocation.The standard C library is full of traps, and not having to micro manage every single line of code you write is a relief. Just read the BUGS section of the manpages for some functions and youo'll see. Having less boilerplate code to check for errors makes the code simpler to read, which improves the maintainability, and causes less bugs.
Features
Another point is the whole bunch of collection types GLib provides and that you don't have to reimplement. Just because reimplementing a linked list is easy doesn't mean you should do it. I worked at another company that shipped code with several linked list implementations, because some developpers would just have the Not Invented Here syndrome and would redevelop their own. A common, thouroughly tested, widespread library like GLib helps avoiding this nonsense. You shouldn't redevelop that stuff unless you have very specific performance constraints.
它们的行为在 GTK+ 支持的任何平台上都有明确定义,这与有时可能半途而废的本机函数不同。
Their behavior is well-defined on any platform that GTK+ supports, as opposed to the native functions which may perhaps sometimes partway work.
不得不说,这个想法是好的,但执行得不好。当您多次尝试 free() 指针时,程序不会崩溃,这真是太好了。或者对 NULL 字符串进行排序。但这是喜忧参半的。它还可以防止您在代码中发现这些令人讨厌的错误。有一天,您不仅会很难移植您的程序,因为您依赖于非标准函数,您还会遇到非常困难,因为您的代码有错误并且您从未发现出去。
I have to say, this is well intended but not well executed. It is sorta nice that your program won't crash and burn when you try to free() a pointer more than once. Or sort a NULL string. But that's a mixed blessing. It prevents you from discovering these nasty bugs in your code as well. Not only will you have a hard time getting your program ported some day, because you're relying on non-standard functions, you'll have a really hard time because your code was buggy and you never found out.
10 年前,使用 Gnome 库可能是有意义的,但现在它是一个遗留问题。 C89 可以说是世界上最标准的语言,具有非常稳定的功能和语法,因此调试别人的 C89 代码是可行的。
相比之下,Gnome 的 glib 更改了 C 标准之外的函数功能,因此您不仅要处理由 C 组成的模糊包装器代码的调试,而且您的代码可能会停止工作,因为 Gnome 更改了它的包装器函数。
图表A:g_snprintf()
我不太高兴我能够编写(又一个)链接列表来替换 Gnome 的链接列表,还有另一个版本的 snprintf() 和一堆蹩脚的包装代码,它们默默地 malloc() 内存,从而打破了一个绝对最大值C 编码:“始终在同一范围内的 malloc() 和 free()” 来替换 g_strdup_printf()。
除此之外,在代码中进行大量字符串更改以执行“有用”的操作(例如将 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 中的这个 gem...
不仅任何 printf() 函数在绝对零时都比 snot 慢,你会注意到它们分配了整个byte,是的,整个 gchar c,用于存储,这保证了溢出,但谁在乎呢?他们获得了 malloc() 所需的字符串长度 - 因为他们将转身并再次执行整个 printf() - 这次,“神奇地”只有足够的存储空间。
当然,他们会在大小上添加+1,这样你就有空间容纳一个零终止符,当然,代价是可怕的,但他们把它隐藏在他们打赌你的代码中太深了。我会放弃并盲目使用它,而不会注意到这是一个多么大的脑残。哎呀,谢谢大家。我只是喜欢爬行我的代码。
不要让 _g_vsnprintf() 函数让您失望,因为在 gprintfint.h 中您会发现 Little gem 只是普通旧版 vsnprintf() 的另一个名称;
强烈建议您在使用 Gnome 之前再次观看《绿野仙踪》,这样您就会知道不要看幕后的情况。欢迎来到我的噩梦!
任何认为 Gnome 比 C 更稳定的人都严重缺乏批判性思维。您可以用性能和透明度来换取一些在 STL 中做得更好的好东西。
10 years ago, using the Gnome lib may have made sense, but its now a legacy liability. C89 is arguably the most standard language in the world, with very stable features and syntax, so debugging someone else's C89 code is doable.
By contrast Gnome's glib changes it's function's features outside of the C Standard, so not only do you get to deal with debugging obscure wrapper code made of C, but your code may stop working because Gnome changes it's wrapper functions.
Exhibit A: g_snprintf()
I'm less than thrilled I get to write (yet another) linked-list to replace Gnome's, AND yet another version of snprintf() and a bunch of crappy wrapper code that silently malloc()s memory, thereby breaking the one absolute maxium of C coding: "Always malloc() and free() in the same scope" to replace g_strdup_printf().
Add to this the thrill of making massive numbers of string changes in the code to do "useful" things like change gchar to char, gint to int, gboolean to bool, etc, etc, etc, ad nauseam until my Subversion comparisons are now a phone book. Worse, you end up having to change more and more code because this stuff is littered all over the .h files, so it keeps expanding, like a boated corpse, into a huge mess.
If you're scoping a contract job and see glib.h anywhere, RUN!!!
Just say NO!.
PS: Downloading the source, removing all the Gnome-specific types, and recompiling it to make your own "g_"-less functions sorta, kinda works, and is a big time-saver.
Exhibit B: g_strdup_printf()
More horrific Gnome crappola from Gnome. Gnome has lots of "lovely" functions like g_strdup_vprintf() that "magically" know how much storage you need to hold your returned string, and I had occasion to look behind the "magic". This wins my award for the most hideous abuse of C ever.
If you keep tracing g_strdup_vprintf() back through all the wrapper functions, you come to this gem in gmessages.c....
Not only is ANY printf() function slower than snot at absolute zero, you'll notice they allocate an entire byte, yup, a whole gchar c, for storage, which guarantees overflow, but then who cares? they get the length of the string they'll need to malloc() - because they are going to turn around and do the whole printf() over again - this time, "magically" with just enough storage.
They'll add +1 to the size, of course, so you'll have room for a nul-terminator, guaranteed, at horrific expense of course, but they've hid it so deep in the code they're betting you'll just give up and use it blindly and not notice what a massive brain-fart this is. Gee, thanks guys. I just love my code to crawl.
Don't let the _g_vsnprintf() function throw you off, because in gprintfint.h you'll discover that little gem is just another name for plain old vanilla vsnprintf();
It's highly recommended that you watch the Wizard Of Oz again before working with Gnome, so you'll know not to look behind the curtain. Welcome to my nightmare!
Anyone that thinks Gnome is more stable than C is badly lacking in critical thinking. You trade performance and transparency for a few nice things that are done better in the STL.
这是一个更新。看来开发者已经意识到了他们的错误:
https://developer. gnome.org/glib/stable/glib-Memory-Allocation.html#g-mem-is-system-malloc
Here is an update. It looks the developer's realized their mistake:
https://developer.gnome.org/glib/stable/glib-Memory-Allocation.html#g-mem-is-system-malloc