为什么使用 GLib 函数?

发布于 2024-08-21 04:59:59 字数 122 浏览 4 评论 0原文

在使用 C 和 GTK+ 进行编程时,为什么使用 g_strdup_printfg_freeg_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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

痴骨ら 2024-08-28 04:59:59

一般来说,GLib 的目的是一个实用程序和可移植性库。这些本身就是考虑使用它的理由。

您提到的特定函数都在其 C 标准库变体之上提供了一些额外的功能:

  • g_strdup_printf 类似于 sprintf,但实际上为您分配了缓冲区,并省去了您的猜测缓冲区应该有多大。 (返回值应为 g_free'd。)
  • g_freefree 类似,但会检查 NULL 指针。
  • g_strcmp0strcmp 类似,但将 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 like sprintf, but actually allocates the buffer for you and saves you the guesswork of how large the buffer should be. (The return value should be g_free'd.)
  • g_free is like free, but checks for a NULL-pointer.
  • g_strcmp0 is like strcmp, but treats a NULL-pointer like an empty string, and thus sorts it in front.
笨死的猪 2024-08-28 04:59:59

为了在多个操作系统中保持一致的行为。这是一个可移植性的事情。

在 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.

旧话新听 2024-08-28 04:59:59

GLib 提供了您现在期望从任何编程语言中获得的可移植性和基本功能,例如集合类型(链接列表、数组、哈希表等)。以下是 GLib 可以给您带来的一些好处。

可移植性


GLib 的重点不是可移植到 C 标准,而是可移植到该标准的实现。 GLib 负责处理已知的怪癖,这些怪癖乍一看似乎毫无用处,直到您需要将代码移植到具有这些令人讨厌的错误的平台上。

让我们以 g_free 为例,因为很多人都批评它。 在某些平台上,free(NULL) 会失败,即使 C99 说它应该可以工作。该检查至少从 1998 年就存在(我在 git 历史记录中跟踪过它)。有些人可能会说不再需要它了,但即使在 2017 年,我也在一家公司工作,该公司在调用 free 之前会检查 NULL,因为否则它会在其嵌入式平台上崩溃。当您想要进行一些严重的内存调试时,它还可以用作代码检测的包装器。

可读性


它通过提供一些包装函数来帮助提高代码的可读性,这些函数不仅提高了可移植性,而且还帮助您避免了许多语言陷阱。你们中有多少人测试 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 这样的通用的、经过彻底测试的、广泛使用的库有助于避免这种胡言乱语。除非您有非常具体的性能限制,否则您不应该重新开发这些东西。

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 where free(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 for NULL before calling free 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 returns NULL? How many of you have a way to recover if it returns NULL, 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 have g_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:

char *buffer = g_malloc(30);
/* Do something with it ... */
g_free (buffer);

...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.

忆依然 2024-08-28 04:59:59

它们的行为在 GTK+ 支持的任何平台上都有明确定义,这与有时可能半途而废的本机函数不同。

Their behavior is well-defined on any platform that GTK+ supports, as opposed to the native functions which may perhaps sometimes partway work.

ぽ尐不点ル 2024-08-28 04:59:59

不得不说,这个想法是好的,但执行得不好。当您多次尝试 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.

李不 2024-08-28 04:59:59

10 年前,使用 Gnome 库可能是有意义的,但现在它是一个遗留问题。 C89 可以说是世界上最标准的语言,具有非常稳定的功能和语法,因此调试别人的 C89 代码是可行的。

相比之下,Gnome 的 glib 更改了 C 标准之外的函数功能,因此您不仅要处理由 C 组成的模糊包装器代码的调试,而且您的代码可能会停止工作,因为 Gnome 更改了它的包装器函数。

图表A:g_snprintf()

标准 sprintf() 函数的更安全形式。输出是
保证不超过 n 个字符(包括终止 nul
字符),因此很容易确保缓冲区溢出不会
发生。

另请参见 g_strdup_printf()。

在 1.2.3 之前的 GLib 版本中,如果
输出被截断,并且截断的字符串可能不是
以 null 结尾。在 1.3.12 之前的版本中,该函数返回
输出字符串的长度。

g_snprintf()的返回值符合snprintf()函数
按照 ISO C99 标准。请注意,这不同于
传统的 snprintf(),它返回输出字符串的长度。

格式字符串可以包含位置参数,如中指定的
单一 Unix 规范。

我不太高兴我能够编写(又一个)链接列表来替换 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 中的这个 gem...

/**
 * 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 慢,你会注意到它们分配了整个byte,是的,整个 gchar c,用于存储,这保证了溢出,但谁在乎呢?他们获得了 malloc() 所需的字符串长度 - 因为他们将转身并再次执行整个 printf() - 这次,“神奇地”只有足够的存储空间。

当然,他们会在大小上添加+1,这样你就有空间容纳一个零终止符,当然,代价是可怕的,但他们把它隐藏在他们打赌你的代码中太深了。我会放弃并盲目使用它,而不会注意到这是一个多么大的脑残。哎呀,谢谢大家。我只是喜欢爬行我的代码。

不要让 _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 中做得更好的好东西。

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()

A safer form of the standard sprintf() function. The output is
guaranteed to not exceed n characters (including the terminating nul
character), so it is easy to ensure that a buffer overflow cannot
occur.

See also g_strdup_printf().

In versions of GLib prior to 1.2.3, this function may return -1 if the
output was truncated, and the truncated string may not be
nul-terminated. In versions prior to 1.3.12, this function returns the
length of the output string.

The return value of g_snprintf() conforms to the snprintf() function
as standardized in ISO C99. Note that this is different from
traditional snprintf(), which returns the length of the output string.

The format string may contain positional parameters, as specified in
the Single Unix Specification.

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().

g_strdup_printf ()

Similar to the standard C sprintf() function but safer, since it calculates the maximum space required and allocates memory to hold the result. The returned string should be
freed with g_free() when no longer needed.

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....

/**
 * 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;
}

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();

/* 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__ */

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.

妄想挽回 2024-08-28 04:59:59

这是一个更新。看来开发者已经意识到了他们的错误:

<块引用>

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

Here is an update. It looks the developer's realized their mistake:

g_mem_is_system_malloc has been deprecated since version 2.46 and should not be used in newly-written code.

GLib always uses the system malloc, so this function always returns TRUE.

Checks whether the allocator used by g_malloc() is the system's malloc implementation. If it returns TRUE memory allocated with malloc() can be used interchangeable with memory allocated using g_malloc(). This function is useful for avoiding an extra copy of allocated memory returned by a non-GLib-based API.

https://developer.gnome.org/glib/stable/glib-Memory-Allocation.html#g-mem-is-system-malloc

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文