程序中解析主机地址的memset函数

Memset function in programm that resolves host adresses

首先,我了解代码(请参阅 post 末尾处)的一般作用,我不想进行全面解释。

我不明白的是这一行代码: 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;

}

是的,你没看错。

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

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

你是对的,这里使用 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 的调用首先会处理其余部分。

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

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

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

struct addrinfo hints = {0};

这将值初始化第一个字段(ai_flags)为0,默认初始化其余字段到它们的默认值,在这种情况下也是零。