程序中解析主机地址的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
};
你是对的,这里使用 memset
将 hints
的所有字节设置为零。
这样做是为了使以后未明确设置的任何字段都具有值 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,默认初始化其余字段到它们的默认值,在这种情况下也是零。
首先,我了解代码(请参阅 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
};
你是对的,这里使用 memset
将 hints
的所有字节设置为零。
这样做是为了使以后未明确设置的任何字段都具有值 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,默认初始化其余字段到它们的默认值,在这种情况下也是零。