考虑这么一段代码,编译时会产生警告void *指针用于算术运算,那么如果消除这个告警?
ssize_t Send(int sockfd, const void *buf, size_t len, int flags)
{
size_t sendcount = 0x00;
ssize_t nsend = 0x00;
do{
nsend = send(sockfd, buf + sendcount, len - sendcount, 0);
if(nsend < 0x00 && errno == EINTR)
continue;
else
return -1;
sendcount += nsend;
}while(sendcount != len);
return sendcount;
}
main.cpp:11:30: warning: pointer of type ‘void *’ used in arithmetic [-Wpointer-arith]
nsend = send(sockfd, buf + sendcount, len - sendcount, 0);
很简单,只需要将那行改成以下这样就行,也就是说,需要将buf类型转换为具体类型的指针即可,在这里改成uint8_t *即可。但是我们会好奇编译器为什么会发出这样的告警。
nsend = send(sockfd, (uint8_t *)buf + sendcount, len - sendcount, 0);
编写测试代码如下,编译后我们进行反汇编。可以发现转换到汇编角度,并非简单的算术加减。是需要乘于其步长即指向类型的占用内存大小。因为编译器并不知道void *真正指向的类型内存大小,所以发出警告。修改需要根据程序上下文语义转换为具体类型的指针,在这里buf+sendcount目的是按字节移动指针,因此转换成uint8_t *即可。
- ptr16 += 0x01 对应于 ptr16 += 0x01 * sizeof(uint16)
- ptr32 += 0x02 对应于 ptr32 += 0x02 * sizeof(uint32)
#include <unistd.h>
#include <stdint.h>
int main(int argc, char *argv[])
{
uint8_t *ptr = new uint8_t[32];
uint16_t *ptr16 = (uint16_t *)ptr;
ptr16 += 0x01;
*ptr16 = 0x01;
uint32_t *ptr32 = (uint32_t *)ptr;
ptr32 += 0x02;
*ptr32 = 0x02;
delete ptr;
ptr = NULL;
return 0;
}
(gdb) disassemble main
Dump of assembler code for function main(int, char**):
...
0x0000000000400661 <+20>: callq 0x400530 <_Znam@plt> # new申请内存
0x0000000000400666 <+25>: mov %rax,-0x18(%rbp) # 返回值放入局部变量ptr
0x000000000040066a <+29>: mov -0x18(%rbp),%rax # 局部变量ptr的值放入rax寄存器
0x000000000040066e <+33>: mov %rax,-0x10(%rbp) # 寄存器rax(等于ptr)放入ptr16
0x0000000000400672 <+37>: addq $0x2,-0x10(%rbp) # ptr16 += 0x02;
0x0000000000400677 <+42>: mov -0x10(%rbp),%rax # 取出ptr16的值放入rax
0x000000000040067b <+46>: movw $0x1,(%rax) # *ptr16 = 0x01;
0x0000000000400680 <+51>: mov -0x18(%rbp),%rax # 局部变量ptr的值放入rax寄存器
0x0000000000400684 <+55>: mov %rax,-0x8(%rbp) # 寄存器rax(等于ptr)放入ptr32
0x0000000000400688 <+59>: addq $0x8,-0x8(%rbp) # ptr32 += 0x08;
0x000000000040068d <+64>: mov -0x8(%rbp),%rax
0x0000000000400691 <+68>: movl $0x2,(%rax) # *ptr32 = 0x02
...