【问题标题】:Memset function in programm that resolves host adresses解析主机地址的程序中的 Memset 函数
【发布时间】:2021-03-28 10:18:48
【问题描述】:

首先,我了解代码(见文章末尾)的一般作用,我不想要一个全面的解释。

我不明白的是这行特定的代码: memset(&hints, 0, sizeof(struct addrinfo));

到目前为止,我得到的是 memset() 是一个函数来填充它指向的字符串。

它有三个参数,第一个是指向字符串的指针,第二个是要设置的值,第三个是要设置的值的字节数。

在这种情况下,要填充的值是 &hints,这将是变量提示的地址。应该设置的值是 0,所以它用零填充。最后填充到 struct addrinfo 的大小。

所以在这种情况下,memset() 为变量提示生成结构体大小的零。我做对了吗?

如果是,为什么在我的示例中需要这样做?

#include <stdlib.h>/* EXIT_SUCCESS */
#include <stdio.h>/* printf */
#include <string.h>/* memset() */
#include <errno.h>/* int errno */
#include <sys/types.h>/* socket defines */
#include <sys/socket.h>/* socket() */
#include <netdb.h>/* getaddrinfo() */
#define ECHO_PORT "7"

int main (int argc, char* argv[]){
if (argc != 2) {
printf ("Usage: %s HOSTNAME\n", argv[0]);
exit(EXIT_FAILURE);
}

/* Resolve host addresses: */

struct addrinfo hints;
struct addrinfo* result, *rp;

memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;/* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM;/* Stream socket */
hints.ai_flags = 0;
hints.ai_protocol = 0;/* Any protocol */

int err = getaddrinfo(argv[1], ECHO_PORT, &hints, &result);

/* Handle potential error: */

if (err) {
printf("Error: getaddrinfo: %s\n", gai_strerror(err));
exit(EXIT_FAILURE);
}

/* Print names found: */

printf("Addresses for %s:\n", argv[1]);
for (rp = result; rp != NULL; rp = rp->ai_next) {
int af = rp->ai_family;
char* address = NULL;
int ok;

if (AF_INET == rp->ai_family) {
uint8_t in_addr =((struct sockaddr_in*)rp->ai_addr)->sin_addr.s_addr;
address = malloc(INET_ADDRSTRLEN);
ok = inet_ntop(af, &in_addr, address, INET_ADDRSTRLEN);
}

if (AF_INET6 == rp->ai_family) {
char* in6_addr =((struct sockaddr_in6*)rp->ai_addr)->sin6_addr.s6_addr;
address = malloc(INET6_ADDRSTRLEN);
ok = inet_ntop(af, in6_addr, address, INET6_ADDRSTRLEN);
}

if (ok) {
printf("%s\n", address);
}
else {
perror("inet_ntop");
}

free(address);
}

freeaddrinfo(result);

return EXIT_SUCCESS;

}

【问题讨论】:

  • 你知道未初始化的变量默认值是不确定的吗?此处 memset 的重点是确保对象没有,因此任何可能未设置的字段都具有零值 - 而不是读取未定义行为的垃圾。如果这 4 个分配覆盖了结构中的所有字段,那么从技术上讲,memset 是多余的 - 但这可能是一种很好的做法,因为它可能会在以后获得更多字段,然后如果结构没有先清零,然后有人会爆炸,那么事情可能会爆炸试图读取未初始化的成员。顺便说一句,C++ 有更好的方法来做到这一点
  • 请不要同时标记 C 和 C++,除非问题涉及两种语言之间的交互或差异。对于 C 和 C++,问题的答案可能不同,这可能会令人困惑。要针对 C 和 C++ 提出相同的问题,请输入两个单独的问题。由于您的代码似乎是 C 代码,因此我将删除 C++ 标记。如果您使用的是 C++ 而不是 C,那么您可以更改标签。
  • @EricPostpischil 我同意 - 我不得不改变我的 C++ 答案:-)
  • @underscore_d 感谢您提供背景信息。我不知道。
  • @EricPostpischil 谢谢你的提示。我会在以后的帖子中考虑这一点

标签: c memset


【解决方案1】:

是的,你理解正确。

在下面的代码中需要它,因为在

struct addrinfo hints;

hints 未初始化,程序员希望确保所有字段都归零。

一个更简单的解决方案是直接初始化它:

addrinfo hints{}; // C++11 and later

struct addrinfo hints = {0}; /* C and C++ */

并跳过memset

另一个选择是使用designated initializers(C99 和 C++20)用正确的值初始化它。在 C 中,您可以乱序指定字段,但在 C++ 中则不行,因此此顺序适用于两者:

struct addrinfo hints = {  /* "struct" not needed in C++ */
    .ai_flags    = 0,
    .ai_family   = AF_UNSPEC,
    .ai_socktype = SOCK_STREAM,
    /* .ai_protocol and the rest will be zeroed */
};

对于没有指定初始化器的旧标准:

struct addrinfo hints = {
    0,
    AF_UNSPEC,
    SOCK_STREAM
};

【讨论】:

  • C 版本的初始化程序至少需要一个值。 { 0 } 是惯用的。我不确定 C++ 是否接受。
  • @JohnBollinger C++ 也接受了,所以我会补充一下,谢谢。
  • 请注意,{} 仅适用于 C++11 及更高版本,但 {0} 将适用于所有 C++ 版本。
  • @RemyLebeau 哦,是的,最好也记下这一点,谢谢!
【解决方案2】:

您说得对,这里使用了memsethints 的所有字节设置为零。

这样做是为了使以后未显式设置的任何字段都具有值 0。因为 hints 未初始化,其字段具有不确定的值,因此这样做会将所有字段设置为 0。如果您查看定义struct addrinfo:

       struct addrinfo {
           int              ai_flags;
           int              ai_family;
           int              ai_socktype;
           int              ai_protocol;
           socklen_t        ai_addrlen;
           struct sockaddr *ai_addr;
           char            *ai_canonname;
           struct addrinfo *ai_next;
       };

您可以看到该程序仅显式设置了前 4 个字段。对memset 的调用首先会处理其余的事情。

【讨论】:

    【解决方案3】:

    是的,你的理解基本正确。代码只是用0x00 字节填充整个hints 变量,然后将其传递给getaddrinfo()。这是将hints 初始化为默认状态所必需的,这很重要,因为addrinfo 包含用于控制getaddrinfo() 行为的标志和内存指针。所以你不能只让hints 处于未初始化状态,它会包含随机垃圾,会导致未定义的行为、混淆getaddrinfo() 和/或什至导致内存损坏、崩溃等.

    使用memset() 是一种在一次快速操作中将hints 的所有字段初始化为零的快速方法,而不是单独初始化每个字段。这样,您可以专注于为您真正感兴趣的字段分配值。

    初始化hints 的更简单方法是:

    struct addrinfo hints = {0};
    

    这将 value-initialize 第一个字段 (ai_flags) 为 0,而 default-initialize 其余字段为其默认值,在本例中为也是零。

    【讨论】:

    • 当然,“旧编译器”不仅包括那些实现旧版本 C++ 的编译器,还包括所有不提供初始化器语法扩展的 C 编译器。
    • 问题现在被标记为 C。= {} 不是 any 语言的首选初始化方式。
    猜你喜欢
    • 2013-03-25
    • 1970-01-01
    • 1970-01-01
    • 2021-07-13
    • 2010-11-05
    • 2011-08-21
    • 2011-03-12
    • 2011-01-10
    • 2021-01-10
    相关资源
    最近更新 更多