c语言数据类型

数据声明与字长

1.首先字长是计算机可以处理的二进制数的位数,字节是基本单位,一字节等于8位,1位是0或1。

2.关于int,short,long,long long , unsigned声明与可处理二进制数位数的关系:

首先提醒:

如-3:可以表示为,10000011,其中最左边的那个数所在的位置叫符号位,在没有声明为unsigned的情况下,符号位默认为最左边那位二进制数所占位置,若有unsigned,则不存在符号位。

eg:以64位计算机为例,int处理数据并将其转化为二进制数,为32位的二进制数,而long long 为至少为64位二进制数表示的数字
我的意思就是,如果拿应该给long声明处理的超大数据,在printf时使用%d占位符,不用%ld占位符,就会使这个数据被截断,即截断前32位表示成10进制数后输出,不能通过完整的64位二进制数转化位十进制数后输出

3.当然%d可以完整输出的数据,用%ld也可以完整输出,没问题的

源码反码和补码

1.首先,我们说说,int表示的最大正数数/最小负数是多少,(64位操作系统的情况下)根据二进制和十进制的转换:

正数的范围:最大的正数是符号位为0,其余位全为1的情况。对于 32 位的int,最大正数的二进制表示为01111111 11111111 11111111 11111111。

如下图:

对于有unsigned前缀声明的变量,没有符号位,32位都是二进制,所以直接2的32次幂。
所以最大整数就是:

2.有关源码反码和补码
为了计算的方便,诞生了这个概念:
计算:
5-3=2,在计算机内被转化为:
5+(-3)=2
可能有些抽象,我先来定义一下这几个概念:

在计算方面:

5-3的计算,就相当于5的二进制数加上3的二进制数的补码,可以化减法为加法

可移植类型:<stdint.h>和<inttypes.h>

详细说明,见C prime plus教材48页
这里进行补充:

1.这两种库的区别:

2.输出宏和占位符的区别:

浮点数后缀解释

是的,正如你所理解的那样。如果你声明一个 float 类型的变量并给它赋一个没有加后缀 f 的浮点数(比如 11.0),那么这个数会被默认当作 double 类型来处理,而不是 float。这种情况下,虽然你声明的是 float 类型的变量,但浮点常量(比如 11.0)默认是 double 类型,所以在赋值时会发生隐式转换,将 double 转换为 float

流程

  • 没有 f 后缀时11.0 被当作 double 类型。赋值给 float 类型变量时会发生类型转换(从 double 转为 float),这个过程中可能会有精度丢失,因为 float 的精度比 double 要低。

  • 加上 f 后缀时11.0f 被明确当作 float 类型,直接赋值给 float 类型的变量时,没有类型转换,避免了精度损失。

内存处理:

  • 在内存中,double 类型的数据通常占 8 字节,而 float 类型占 4 字节。所以如果你不加 f 后缀,虽然变量是 float,但是你给它赋的 double 数值会先被存储为 double,然后再强制转换为 float 存储,可能会丢失一些精度。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

int main() {
float f_num = 11.0; // 11.0 默认是 double 类型
float f_num_with_f = 11.0f; // 明确指定为 float 类型

printf("f_num: %.6f\n", f_num); // 打印 float 类型的 f_num
printf("f_num_with_f: %.6f\n", f_num_with_f); // 打印明确指定为 float 的 f_num_with_f

return 0;
}

结论:

  • 没有 f 后缀:浮点常量 11.0 会被视为 double 类型,赋值给 float 类型时会进行类型转换。
  • 加上 f 后缀:常量被明确作为 float 类型,避免了隐式转换。

这样,当你希望显式控制数据的类型时,加上后缀 f 是非常重要的,特别是在性能要求较高或精度敏感的场合。

补充:

你可以通过统一声明为 float 类型,并加上 f 后缀来控制常量的类型,这样可以避免隐式类型转换时造成的精度损失。

然而,是否选择 float 作为默认类型,需要根据你的实际需求来判断。如果需要更高的精度,double 可能会是更好的选择,因为它的精度更高且范围更大。

转换说明(%转换字符)

在 C 语言中,浮点数的转换说明符(也叫转换字符)用于格式化 floatdoublelong double 类型的数据。以下是与浮点数相关的常见转换说明符:


1. %f:标准浮点数表示

  • 用于以小数点形式显示浮点数。
  • 默认保留 6 位小数,可通过指定精度改变小数位数。

示例:

1
2
3
float f = 3.14159;
printf("%f\n", f); // 输出:3.141590
printf("%.2f\n", f); // 输出:3.14(保留两位小数)

2. %e%E:科学计数法表示

  • 用于以科学计数法(指数形式)表示浮点数。
  • %e 使用小写 e 表示指数,%E 使用大写 E 表示指数。

示例:

1
2
3
double d = 12345.6789;
printf("%e\n", d); // 输出:1.234568e+04
printf("%E\n", d); // 输出:1.234568E+04

3. %g%G:自动选择简洁表示

  • 自动在 %f%e 之间选择最简洁的表示形式。
  • %g 使用小写 e%G 使用大写 E(当选择科学计数法时)。

示例:

1
2
3
4
double d = 0.000123456;
printf("%g\n", d); // 输出:0.000123456(普通形式)
d = 123456.789;
printf("%g\n", d); // 输出:1.23457e+05(科学计数法)

4. %a%A:十六进制浮点数表示(C99 标准引入)

  • 用于以十六进制表示浮点数,指数以 pP 表示(表示 2 的幂次)。
  • %a 使用小写字母,%A 使用大写字母。

示例:

1
2
3
double d = 123.456;
printf("%a\n", d); // 输出:0x1.edd2f1a9fbe77p+6
printf("%A\n", d); // 输出:0X1.EDD2F1A9FBE77P+6

5. %Lf%Le%Lg:用于 long double

  • 当处理 long double 类型时,需要在标准说明符前加 L 前缀。
    • %Lf:以小数点形式输出 long double
    • %Le:以科学计数法输出 long double
    • %Lg:以最简洁的形式输出 long double

示例:

1
2
3
4
long double ld = 3.141592653589793238L;
printf("%Lf\n", ld); // 输出 long double 的值
printf("%Le\n", ld); // 科学计数法表示
printf("%Lg\n", ld); // 自动选择简洁表示

6. 宽度与精度控制

可以通过格式说明符进一步控制输出的宽度和精度:

  • %m.nf:指定总宽度 m 和小数部分的位数 n
    • m:整个数值占的最小宽度,不足时补空格。
    • n:小数部分的位数。

示例:

1
2
3
double d = 123.456;
printf("%10.2f\n", d); // 输出: 123.46(宽度 10,保留两位小数)
printf("%-10.2f\n", d); // 输出:123.46 (左对齐,宽度 10)

小结

转换字符 含义
%f 按小数点形式输出浮点数
%e/%E 按科学计数法输出浮点数
%g/%G 自动选择 %f%e 的简洁形式
%a/%A 按十六进制表示浮点数(C99)
%Lf 以小数点形式输出 long double
%Le 以科学计数法输出 long double
%Lg 以最简洁形式输出 long double
%o 输出整数的八进制形式
%#o 输出带有0前缀的八进制形式
根据需要选择合适的格式化符来输出浮点数。
注意:float和double都是用%e或%E直接表示科学计数法形式