1. 类型转换
自动转换
自动类型转换就是编译器默默地、隐式地、偷偷地进行的数据类型转换,这种转换不需要程序员干预,会自动发生。
在赋值运算中,赋值号两边的数据类型不同时,需要把右边表达式的类型转换为左边变量的类型,这可能会导致数据失真,或者精度降低;所以说,自动类型转换并不一定是安全的。对于不安全的类型转换,编译器一般会给出警告。
#include<stdio.h>
void main() {
float f = 100.34f;
int n = f;
printf("%d", n);
}
转换的规则如下:
- 转换按数据长度增加的方向进行,以保证数值不失真,或者精度不降低。例如,int 和 long 参与运算时,先把 int 类型的数据转成 long 类型后再进行运算。
- 所有的浮点运算都是以双精度进行的,即使运算中只有 float 类型,也要先转换为 double 类型,才能进行运算。
- char 和 short 参与运算时,必须先转换成 int 类型。

#include<stdio.h>
int main() {
float PI = 3.14159;
int s1, r = 5;
double s2;
s1 = r * r * PI;
s2 = r * r * PI;
printf("s1=%d, s2=%f\n", s1, s2);
return 0;
}

一个变态的自动转换
#include <stdio.h>
int main() {
float d= 10 + 'a' + 1.5 - 8765.1234 * 'b';
printf("total=%f", d);
return 0;
}
强制类型转换
在代码中明确地提出要进行类型转换,这称为强制类型转换。
强制类型转换是程序员明确提出的、需要通过特定格式的代码来指明的一种类型转换。换句话说,自动类型转换不需要程序员干预,强制类型转换必须有程序员干预。
强制类型转换的格式为:
(type_name) expression
#include <stdio.h>
int main() {
int sum = 103; //总数
int count = 7; //数目
double average; //平均数
average = (double)sum / count;
printf("平均数:%f!\n", average);
return 0;
}
sum 和 count 都是 int 类型,如果不进行干预,那么sum / count的运算结果也是 int 类型,小数部分将被丢弃;虽然是 average 是 double 类型,可以接收小数部分,但是心有余力不足,小数部分提前就被“阉割”了,它只能接收到整数部分,这就导致除法运算的结果严重失真。
类型转换只是临时性的
无论是自动类型转换还是强制类型转换,都只是为了本次运算而进行的临时性转换,转换的结果也会保存到临时的内存空间,不会改变数据本来的类型或者值。
#include <stdio.h>
int main() {
double total = 200.48;
int count = 5;
double unit;
int int_total = (int)total;
unit = total / count;
printf("total=%f,int_total=%d,unit=%f", total, int_total, unit);
return 0;
}

这里(int)total并不会改变total的值。
2. 跨平台移植的整数
不同平台,不同编译器,整数的大小不一样
int 16位系统下2个字节,32位下4个字节
在liunx 64位下是8个字节,对于Windows 32,64都是4个字节
#include<stdio.h>
void main() {
printf("%d", sizeof(long));//输出4
}
在debain下编译输出

引入stdint.h头文件解决此问题,需支持C99的编译器

#include<stdio.h>
#include<stdint.h>
void main() {
printf("%d", sizeof(int64_t));
}
3. bool类型
在 C 语言标准(C89)没有定义布尔类型,所以 C 语言判断真假时以 0 为假,非 0 为真。所以我们通常使用逻辑变量的做法:
用int来做标记
#include<stdio.h>
void main() {
int flag;
flag = 0;
flag = 1;
}
宏定义布尔类型
//宏定义布尔类型
#define BOOL int
#define TRUE 1
#define FALSE 0
#include<stdio.h>
void main() {
BOOL flag = TRUE;
printf("%d", flag);
flag = FALSE;
printf("%d", flag);
}
在最新的 C 语言标准(C99)解决了布尔类型的问题。C99 提供了 _Bool 型,所以布尔类型可以声明为 _Bool flag。
_Bool 依然仍是整数类型,但与一般整型不同的是,_Bool 变量只能赋值为 0 或 1,非 0 的值都会被存储为 1。
#include<stdio.h>
void main() {
_Bool b1 = 1;
_Bool b2 = 2;
_Bool b3 = 0;
_Bool b4 = -5;
printf("b1=%d,b2=%d,b3=%d,b4=%d", b1,b2,b3,b4);
}

在C++中,通过bool来定义布尔变量,通过true和false对布尔变量进行赋值。C99为了让我们能够写出与C++兼容的代码,添加了一个头文件<stdbool.h>
#include<stdio.h>
#include<stdbool.h>
void main() {
bool b1 = true;
bool b2 = false;
bool b3 = 2 == 2;
printf("%d,%d,%d", b1, b2,b3);
}
4. 基础数据类型进阶
浮点数数据丢失
void main() {
float f1 = 3.14159264;
printf("%f", f1);//小数点后6位,6位内正确,超出6位,不可预知
float f2 = 3.14159263;
if (f2 == f1) {
printf("等于了!");
}
}
double
void main() {
double d1 = 1.123456789123456789123456789;
printf("%f,%.20f", d1, d1);//1.123457,1.12345678912345681155
//小数点后15位,15位内正确,超出15位,不可预知
}
#define _CRT_SECURE_NO_WARNINGS //关闭安全检查
#include<stdio.h>
#include<float.h>
void main() {
float f;
double d;
long double ld;
printf("%d,%d,%d\n", sizeof(f), sizeof(d), sizeof(ld));
//分别输入
scanf("%f", &f);
scanf("%lf", &d);
scanf("%Lf", &ld);
printf("%f,%lf,%Lf\n", f, d, ld);
printf("%f,$f\n", FLT_MAX, FLT_MIN);
//在windows下max是一样的
printf("%lf,%lf\n", DBL_MAX, DBL_MIN);
printf("%Lf,%Lf", LDBL_MAX, LDBL_MIN);
}
#include<stdio.h>
#include<float.h>
void main() {
double d1 = 1.11111111111111111111111113;
double d2 = 1.11111111111111111111111112;
if (d1 == d2) {
printf("相等!");
}
double d3 = 1.11111111111111011101111113;
double d4 = 1.11111111111111011111111112;
if (d3 == d4) {
printf("相等!");
}
}
看一下float在内存中是怎么保存的

查看32位浮点
一个float在内存中占4个字节,32个bit

5. long double与long long
#include<stdio.h>
void main() {
int num = 17600807885;
printf("%d\n", num);//这个手机号存不对了
long long mobile = 17600807885;
printf("%lld\n", mobile);//需要lld
printf("\n%d", sizeof("17600807885"));
}
#include<stdio.h>
#include<limits.h>
void main() {
printf("%lld,%lld\n", LLONG_MAX, LLONG_MIN);
printf("%llu", ULLONG_MAX);//无符号
}
6. 自动变量
函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量)都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间,这类局部变量称为自动变量。
#include<stdio.h>
#include<stdlib.h>
void main() {
int x = 10;
auto int y = 9;
auto int z = x + y;
printf("%p,%d", &z, z);
}
void main() {
int x = 10;
auto y = 9;
auto z = x + y;
printf("%p,%d", &z, z);
}

7. 基本输入输出
putchar函数是字符输出函数,其功能是在终端(显示器)输出单个字符。其函数原型为:
#include<stdio.h>
#include<stdlib.h>
void main() {
putchar('A');
putchar('av1');
//输出A1
}
getchar函数的功能是接收用户从键盘上输入的一个字符。其一般调用形式为:
#include<stdio.h>
#include<stdlib.h>
void main() {
char c; /*定义字符变量c*/
c = getchar(); /*将读取的字符赋值给字符变量c*/
printf("%c", c);
}
scanf函数称为格式输入函数,即按照格式字符串的格式,从键盘上把数据输入到指定的变量之中。
|
转换说明符 |
|
|
%c |
把输入解释成一个字符 |
|
%d |
把输入解释成一个有符号十进制整数 |
|
%e,%f,%g,%a |
把输入解释成一个浮点数(%a是C99的标准) |
|
%E,%F,%G,%A |
把输入解释成一个浮点数(%A是C99的标准) |
|
%i |
把输入解释成一个有符号十进制整数 |
|
%o |
把输入解释成一个有符号的八进制整数 |
|
%p |
把输入解释成一个指针(一个地址) |
|
%s |
把输入解释成一个字符串:输入的内容以第一个非空白字符作为开始,并且包含直到下一个空白字符的全部字符 |
|
%u |
把输入解释成一个无符号十进制整数 |
|
%x,%X |
把输入解释称一个有符号十六进制整数 |
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
void main() {
int num;
printf("请输入一个数字:\n");
scanf("%d", &num);//用地址接收
printf("您输入的数字是:%d", num);
}
8. 两个例子
用户输入任务一数字,输出其对应的十进制,八进制,十六进制
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
void main() {
int num;
printf("请输入一个数字:\n");
scanf("%d", &num);//用地址接收
printf("您输入的数字是:%d,0%o,0x%x", num,num,num);
}
已知半径r,求一个圆的面积是多大。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<math.h>
#define PI 3.1415926
void main() {
float num;
printf("请输入一个半径:\n");
scanf("%f", &num);//用地址接收
printf("面积是:%5f",num*num*PI);
}