【Linux网络编程】域名转IP后的一些深层(计算机底层)的思考

以下代码运行于64位Linux系统中

先看一下代码样本

HOSTENT *H = gethostbyname("www.csdn.net");
char **p = H->h_addr_list;
SOCKADDR_IN info;
while(*p) {
    
    
    memcpy(&info.sin_addr, *p, 4);
    cout << inet_ntoa(info.sin_addr) << endl;
    ++p;
}
  1. 第一行创建了一个结构体指针H,指向函数gethostbyname,并传入了一个参数(即域名)
  2. 第二行创建了一个指向指针的指针p,用来储存指针H中存放的所有IP地址。
  3. 第三行创建了一个结构体info
  4. 第四行是一个while语句,用来判断p中第一个元素是否为空指针,否就继续执行
  5. 第五行是将p中第一个元素从内存层面复制到结构体info成员sin_addr的地址中
  6. 第六行是将info.sin_addr中存放的IP地址二进制值转为一个char指针并被打印屏幕上。

思考

这段代码当初是我刚学网络编程时抄的一位程序员的,但是由于目前我的技术水平有了质的飞跃,于是发现了一些问题。
也不能说是问题吧,毕竟这段代码完全没有问题(除非你把它原封不动的抄到Windows上执行,因为还需要初始化WSADATA)。

可以看到第三行声明了一个结构体变量info(struct sockaddr_in),因为在上述代码中我们是要用它来暂时性的储存IP地址的,所以才创建了它。
但其实,完全没必要声明(创建)这个变量,接下来看我说为什么。

大家都知道指针是指向一个内存地址的,也就意味着你可以用指针查看你权限范围内的内存中所有数据。
这是什么意思呢?

char c1 = 'A';     // 声明一个字符型变量c1并赋值为字母A
char c2 = 'B';     // 声明一个字符型变量c2并赋值为字母B
char *p = nullptr; // 声明一个指针p,暂时将它赋值为空

size_t c1_addr = (size_t)&c1; // 将c1的内存地址数字存入变量c1_addr
size_t c2_addr = (size_t)&c2; // 将c2的内存地址数字存入变量c2_addr

if(c1_addr < c2_addr) // 判断哪个地址值最小,将p指向最小的地址值的变量
    p = &c1;
else
    p = &c2;

cout << *(p + 1) << endl; // 打印为A

你无论如何怎么执行,它永远只会输出A,为什么呢?
变量c1的地址为0x0000002
变量c2的地址为0x0000001
c1被赋值为了A
c2被赋值为了B

指针p永远指向最小的那个内存地址,此处就指向了c2的内存地址,我们将p指向的地址加了1字节,也就变成了0x0000002,而这段地址中储存的值为A,这就是为什么这段代码永远只输出A的原因。

说这些是为了证明什么呢?

为了证明所有数据你都可以通过指针操控,也就意味着我们一开始的那段代码,其实根本不需要多声明一个变量info。

因为在第二行char **p = H->h_addr_list;执行之后,此域名下所有的IP地址已经存入内存了。

而我们又知道,一字节为8比特,可以存放0~255之间所有的数字。

那IP地址是怎么组成的呢?
也就是4个0~255之间的数字组合在一起。
也就意味着他们在内存中的表现其实已经是一个完整的地址了,不需要再通过inet_ntoa等函数进行转换了。

为什么呢?

一开始那段代码,只执行前两行的话,p的第一个元素指向的地址中的数据是3c cd ac 02,而哪个域名的IP地址是60.205.172.2,有没有发现什么奇妙的地方?

没错,IP地址已经出来了,只不过它是以十六进制的形式出现的。

如果你还没明白我想表达的意思,你可以执行一下下面这段代码。

unsigned char ip[4] = {
    
    0x3c, 0xcd, 0xac, 0x02};
printf("%d.%d.%d.%d\n", *ip, *(ip+1), *(ip+2), *(ip+3));

结语

C语言是一个非常灵活非常不可思议的语言,只要你掌握好了它,那么你就可以在计算机中做任何你想做的事情。

猜你喜欢

转载自blog.csdn.net/qq_37435462/article/details/128439080
今日推荐