c语言中转换按编译器是否自动转换可以分为隐式转换和强制转换,按对象与对象之间的转换可以分为指针,复数转实数等。
转换的目的是将某种类型x当成另一种类型y来使用,但是x还是x.
疑惑?c语言怎么看待?
int x = 99;
void* p = 0x1000;
p = (int*)p;
指针转换
c语言指针类型可以分为 void pointer,null pointer, qualifiled pointer(被const,volatile等修饰的指针), function pointer,structure pointer,普通变量指针等。
指针与整数
指针是一个变量,它的值是某个对象的地址值。下面两段代码一个是c语言程序,一个是该c语言程序可能所对应的汇编代码。
#include "stdio.h"
int main(void)
{
int x1 = 10;
int* p1 = &x1;
printf("%d",*p1);
}
.section .data
x1: .long 10
.section .text
.globl main
main:
movl $x1, %eax
movl (%eax), %ebx
pushl %ebx
pushl $format
call printf
addl $8, %esp
movl $0, %eax
ret
.section .rodata
format: .string "%d\n"
整数转化为指针
整数转化为指针是可以的,但实际中应避免这种行为。除非有具体的规定,否则结果是有具体编译器实现的。将整数转化为指针可能会造成未对齐问题(3转化为指针,对齐要求数据的起始地址是2或4或8或16的整数倍),可能出现未指向一个有效实体。比如
int x = 10;//假设地址为0x1000
char* p;
p = 1000;
int数据的存储方式不同于char型;指针不仅告诉了编译器变量存储的地址,还让编译器明白了变量的使用方式(怎能读取,怎么清除,怎么进行运算等)。因此用p不能正确表示int型变量x,还可能让编译器出现崩溃。
参考英文:
指针转为整数
指针转化为整数是可能可以的,但实际中应该避免这种行为。具体的实现有编译器决定.而且如果地址的长度大于该类型整数的长度,出现的结果是未定义的。比如下段代码和下张图。
#include "stdio.h"
#include "stdint.h"
unsigned int* g(void) {
unsigned int *ptr = 0xdeadbeeffff;
/* ... */
return ptr;
}
int main(void)
{
unsigned int x;
x = g();
printf("%lx",x);
}
参考段落
void 指针的转换
c语言规定void指针可以转化为任何类型来使用,任何类型也可以转为void类型来使用。
例子fat文件系统中的f_read函数。
FRESULT f_read (
FIL *fp, /* Pointer to the file object */
void *buff, /* Pointer to data buffer */
UINT btr, /* Number of bytes to read */
UINT *br /* Pointer to number of bytes read */
)
{
FRESULT res;
DWORD clst, sect, remain;
UINT rcnt, cc;
BYTE *rbuff = buff;
.........
}
f_read(,buff,btr,br)。
buff为任何类型都可以,都可以转化为void类型,而void类型都可以转化为byte(unsigned char)类型,这样做语法上不会产生任何错误,但是比如buff是uint16_t*的话,数据是按照16位无符号整数进行存储的,但经过这样的转化,数据是按照8位的方式进行存储的,但用的时候是按照16位无符号整数进行使用,所以会造成错误。所以这样的用法造成数据失真。
参考段落
null指针
空指针是不指向任何地址的指针,c语言规定如果一个类型的指针赋值为这3中方式,那么该指针就为空指针。
int* x;
x = 0;
x = (void*)0;
x = NULL;//这3中赋值方式都为空指针
如果一种类型的空指针被转化为另一类型的指针,那么就会产生另一类型的空指针,比如
#include "stdio.h"
#include "stdint.h"
int main(void)
{
int* x ;
x = NULL;
printf("%d",sizeof(*x));
printf("%d",sizeof(*((char*)x)));
}
参考段落
qualified 指针
被const,volatile等修饰的类型的指针不能转化为没有被修饰的指针(可以的话常量被修改),而没有被这些关键字修饰的指针可以转化为被修饰的指针。
参考段落
function pointer
c语言中,函数名代表了函数中指令开始的地址。函数指针转化,如果被转化的函数的返回值,入参与函数指针所返回的参数,入值参数不一样,那么可能出现未定义的错误。
#include <stdio.h>
double myfun1(void)
{
double x = 1;
printf("这是个小数");
return x;
}
float mufun2(int x)
{
float z = x;
printf("这是个整数");
return z;
}
int main(void)
{
float (*xfun)(int) = mufun2;
xfun(1);
xfun = myfun1;
xfun();
}
比如这段代码myfun1的形参和返回值与xfun不一样,导致编译错误,或者函数运行时产生未定义的错误。
参考资料:
struct pointer
结构体指针进行转化。因为一些硬件平台有特定的对齐规则,如果指向的对象类型没有正确的指向,可能会出现未定义错误,如果再转化回来,应该跟原来一样;结构体指针转化为字符指针,那么这个字符指针指向这个结构体的最低地址。
所以可以把结构体指针转化为字符指针然后遍历结构体中的所有元素。
https://blog.csdn.net/qhairen/article/details/51766118
算术操作转换
使存储的数字能够按照同一种存储规则进行加减法运算。
算术隐式转换规则:
如果有浮点数,有long double全部转变为long double,否则有double转为double,否则有float全部转为float。
如果有整数,类型全部相同的不转换;如果有一个unsigned 类型的等级大于等于其他等级,那么全部转为unsigned类型。如果有不是unsigned的类型的等级高于其他等级,那么则转化为不是该unsigned的类型。
例子:
#include <stdio.h>
int main(void)
{
short int x1 = -1;
unsigned short int x2 = x1;
x2 = x2 + 1;
printf("%d",x2);
}
输出结果是0,原因是:
该环境short 类型有2字节,x1为short int,为-1,补码则是0xffff,那么转化为unsigned int就是65535,65535+1,unsigned溢出,那么就是0了。
float类型和相关表达式可能以超过该类型的精度和范围来表达float,但其实仍然你存储的是float。