【问题标题】:Weird packing in iov (writev)?iov(writev)中的奇怪包装?
【发布时间】:2016-05-02 17:57:58
【问题描述】:

我正在调查writevreadv,当我显示内存内容时,我得到了奇怪的数据:

struct iovec *iov = malloc(sizeof(struct iovec) * 3);
iov[0].iov_base = test_string1;
iov[0].iov_len  = strlen(test_string1);
printf("test1: IOV: &%#x, IOV: %#x, IOV_BASE: &%#x, IOV_BASE: %#x IOV_LEN: &%#x IOV_LEN: %#x\n",
&iov[0], iov[0], &iov[0].iov_base, iov[0].iov_base, &iov[0].iov_len, iov[0].iov_len);

我期待:

  • IOV&IOV的位置
  • IOV 放入存储的值。
  • IOV_BASE& 应该放IOV_BASE 的位置
  • IOV_BASE 应该放test_string1 的位置
  • IOV_LEN& 应该放IOV_LEN 的位置
  • IOV_LEN 应该放test_string1 的长度

但是,在运行它时,我得到了以下结果:

test1: IOV: &0x603010, IOV: 0x400d34, IOV_BASE: &0xe, IOV_BASE: 0x603010 IOV_LEN: &0x400d34 IOV_LEN: 0x603018

现在,当在 gdb 中打印 iov[0] 时,我得到了这些值:

(gdb) print iov[0]
$1 = {
  iov_base = 0x400d34, 
  iov_len = 14
}

真正奇怪的是iov_len。根据 gdb,test_string1 的长度为 14 个字符(确实如此);但是,程序说它的长度是 6303768(十进制)。该程序确实输出了正确的长度值,但它不在正确的位置 (IOV_BASE)。

关于为什么会发生这种事情的任何想法?

【问题讨论】:

  • -Wall 是你的朋友——使用它,查看它给出的警告,并尝试理解它们的含义。

标签: c io printf


【解决方案1】:

这与 readv()writev() 函数几乎没有任何关系,它只与它们都使用的 struct iovec 类型相切。

您必须始终确保传递给printf() 的格式字符串中的转换规范与同一调用中传递的其余参数的类型正确匹配。你还没有这样做。 %x 字段描述符要求相应的参数是适当宽度的无符号整数类型(# 标志不会改变这一点)。您的实际参数是 struct iovec *struct iovecvoid **void *size_t *size_t没有一个对于给定的字段类型是正确的,除了如果 size_tint 的宽度相同(通常不是),最后一个是正确的。

根据标准,“如果任何参数不是相应转换规范的正确类型,则行为未定义”(C2011、7.21.6.1/9、7.21.6.3/2)。这适用于您的电话。想太多或试图解释未定义的行为不是很有用,因为它是未定义的。

我不确定我是否遵循了您的预期行为,尤其是对于 iov[0]。那是structstructs 没有转换说明符,并且您碰巧获得的任何转换(如果程序不简单崩溃)可能与您的特定 C 实现以及您的程序特定,甚至可能月相(参考“未定义行为”)。

你可以试试这个来打印你的struct iovec的详细信息:

printf("test1: IOV &: %p, IOV_BASE &: %p, IOV_BASE: %p, IOV_LEN &: %p, IOV_LEN: %#zx\n",
        (void *) &iov[0], (void *) &iov[0].iov_base, iov[0].iov_base,
        (void *) &iov[0].iov_len, iov[0].iov_len);

%p 转换说明符用于打印指针(到 void)。最后一个转换说明符中的z 宽度说明符表示该参数是size_t 的宽度(事实上,该参数正是一个size_t)。你不能打印struct 本身,所以我省略了它。我插入了强制转换以将指针参数转换为类型void *。所有指针都可以转换为这种类型而不会丢失信息,具体而言,void * 必须与 %p 说明符对应。

【讨论】:

  • 在不使用void * 时,我很难不去弄清楚为什么数据会放在错误的位置。
  • @SailorCire,我认为你没有抓住重点。您最初的问题与未能转换为 void * 无关,因为您没有使用任何 %p 转换说明符。不要忽视我给出了不同的转换说明符。是你使用转换说明符%x,它用于类型unsigned int,来尝试格式化其他类型的参数。如果我要推测实际的未定义行为,我会特别怀疑参数iov[0],即结构,它可能作为多个实际参数逐元素传递。
  • @SailorCire,无论如何,我看不出将转换说明符与相应参数的类型相匹配的要求有什么难以理解的地方。
  • 那么,如果我将 int 传递到 long 中,行为仍然被认为是未定义的?如,如果参数可以封装到参数中,那么它就变成了未定义的。
  • @SailorCire,是的,如果与int 类型的参数相对应的转换说明符指示应该预期long(例如%ld),则行为未定义。无论如何,它可能会如你所料,具体取决于 C 实现和特定上下文,但这是未定义行为的乐趣之一:你不能依赖它来工作,但你不能依赖它来工作休息(一般来说)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-04
  • 1970-01-01
相关资源
最近更新 更多