C语言数据类型

219 阅读17分钟

常量与变量

关键字

C语言的关键字共有32个

数据类型关键字(12)
  • char
  • short
  • int
  • long
  • float
  • double
  • unsigned
  • signed
  • struct
  • union
  • enum
  • void
控制语句关键字(12)
  • if
  • else
  • switch
  • case
  • default
  • for
  • do
  • while
  • break
  • continue
  • go
  • return
存储类关键字(5)
  • auto
  • extern
  • register
  • static
  • const
其他关键字(3)
  • sizeof
  • typedef
  • volatile

数据类型

数据类型的作用:编译器预算对象(变量)分配的内存空间

数据类型分为三种:

  • 基本类型
    • 整型
      • int
      • short
      • long
    • 字符型
      • char
    • 浮点型
      • float
      • double
  • 构造类型
    • 数组类型
    • 结构类型(struct)
    • 联合类型(union)
    • 枚举类型(enum)
  • 指针类型
    • char *
    • int *
    • int **

常量

  • 常量是在程序运行过程中,其值不被改变的量
  • 常量一般出现在表达式或者赋值语句中
  • 常量在代码中使用const 来修饰
  • 也可以 使用define 来声明一个宏
# include <stdio.h>
# include <stdlib.h>
#define MAX = 100
int main(){
    const int a = 10;
    //MAX =99; error 不可以修改
   // a = 12; error  不可以修改
    return 0;
}

变量

变量在使用前必须要定义,定义变量前必须有相应的数据类型 变量的声明和定义是有区别的

  • 声明一个变量不要建立存储空间 使用extern 修饰 如 extern int a;
  • 定义一个变量是需要建立存储空间,int b
# include <stdio.h>
# include <stdlib.h>
int main(){
    extern int a = 10; // error
    return 0;
}

一般来说,定义中包含着声明,对于int b 来说 它既是声明,同时也是定义
而对于extern int b 来说,它只是声明并没有定义

整型

整型的输出

打印格式含义
%d输出一个有符号的10进制的int类型
%o输出8进制的int类型
%x输出16进制的int类型,字母以小写输出
%X输出16进制的int类型,字母以大写输出
%u输出一个10进制的无符号数
正数示例
# include <stdio.h>
# include <stdlib.h>
int main(){
     int a = 10;
     printf("有符号十进制 %d\n", a);
     printf("8进制 %o\n", a);
     printf("16进制小写 %x\n", a);
     printf("16进制大写 %X\n", a);
     printf("无符号十进制 %u\n", a);     
    return 0;
}

输出内容

有符号十进制 10
8进制 12
16进制小写 a
16进制大写 A
无符号十进制 10
负数示例
# include <stdio.h>
# include <stdlib.h>
int main(){
     int a = -10;
     printf("有符号十进制 %d\n", a);
     printf("8进制 %o\n", a);
     printf("16进制小写 %x\n", a);
     printf("16进制大写 %X\n", a);
     printf("无符号十进制 %u\n", a);     
    return 0;
}

输出内容

有符号十进制 -10
8进制 37777777766
16进制小写 fffffff6
16进制大写 FFFFFFF6
无符号十进制 4294967286

仅仅是在整数前面添加了一个符号,输出的值差别很大,这是因为在计算机中存储的是补码,后面补码部分会分析这个示例

整型变量的赋值

整型变量的定义 都是 int a ,在赋值的时候可以以不同进制的形式来赋值

# include <stdio.h>
# include <stdlib.h>
int main(){

     int a = 123; // 十进制
     int b = 0123; // 8进制
     int c = 0x123; // 16进制 

     printf("有符号十进制 a = %d\n", a);
     printf("有符号十进制 b = %d\n", b);
     printf("有符号十进制 c = %d\n", c);

     printf("8进制 a = %o\n", a);
     printf("8进制 b = %o\n", b);
     printf("8进制 c = %o\n", c);

     printf("16进制小写 a = %x\n", a);
     printf("16进制小写 b = %x\n", b);
     printf("16进制小写 c = %x\n", c);

     printf("16进制大写 a = %X\n", a);
     printf("16进制大写 b = %X\n", b);
     printf("16进制大写 c = %X\n", c);
    return 0;
}

输出内容

有符号十进制 a = 123
有符号十进制 b = 83
有符号十进制 c = 291
8进制 a = 173
8进制 b = 123
8进制 c = 443
16进制小写 a = 7b
16进制小写 b = 53
16进制小写 c = 123
16进制大写 a = 7B
16进制大写 b = 53
16进制大写 c = 123

整型变量的输入

变量的输入通过库函数scanf来完成,这个函数第一个参数是输入的格式,第二参数是变量的地址

# include <stdio.h>
# include <stdlib.h>
int main(){
    int a;
    printf("请输入变量a的值:");
    scanf("%d",&a);
    printf("a = %d\n",a);
    return 0;
}

打印结果:

a = 1

注意:scanf("%d",&a) 这个%d后面不能添加换行符号\n,否则就会报错

还可以通过其他格式的输入

# include <stdio.h>
# include <stdlib.h>
int main(){
    int a;
    printf("请输入变量a的值:");
    scanf("%o",&a); /// 以8进制的方式输入
    printf("a = %d\n",a); /// 以16进制的方式输出
    return 0;
}

输出结果

请输入变量a的值:12
a = 10

其他整数类型

数据类型占用空间
short(短整型)2字节
int(整型)4字节
long(长整型)window为4字节,linux 4字节(32位),8字节(64位)
long long(长长整型)8字节
  • 整型在内存中所占的字节数与所选择的操作系统有关,虽然C语言标准中没有明确规定整型数据的长度,但是long类型的长度不能小于int类型,short类型的长度不能大于int类型
  • 当一个小的数据类型赋值给一个大的数据类型的时候,不会出错,因为编译器会自动转化,但是当一个大的数据类型赋值给一个小的数据类型的时候,就有可能会丢失高位
整型常量的表示
整型常量代表类型
10int 类型
10l,10Llong 类型
10ll,10LLlong long 类型
10u,10Uunsigned int类型
10ul,10ULunsigned long类型
10ull,10ULLunsigned long long 类型

补充打印格式

打印格式含义
%hd输出short类型
%ld输出long类型
%lld输出longlong类型
%hu输出无符号short类型
%lu输出无符号long类型
%llu输出无符号longlong类型
数值存储方式
原码
  • 原码是一个数的二进制码
  • 最高位作为符号位,0 表示正 1 表示为负
  • 其他数值部分就是绝对值的二进制数
  • 负数的原码是在其绝对值的基础上,最高位变为1
十进制原码
+150000 1111
-151000 1111
+00000 0000
-01000 0000

原码表示法简单易懂,与带符号数转换方便,但是当两个正数相减或者不同符号数相加的时候,需要确定哪个数的绝对值大,才能确定结果的正负,所以原码不便于加减运算

反码
  • 对于正数,反码与原码相同
  • 对于负数 符号位不变,其他位置取反(0 变 1 ,1 变 0)
十进制反码
+150000 1111
-151111 0000
+00000 0000
-01111 1111

反码运算也不方便通常用来作为补码的过渡

补码

在计算机系统中,数值一律使用补码来存储

  • 对于正数,原码,反码,补码相同
  • 对于负数,补码为它的反码+1
  • 如果想要得到原码,补码符号位不变,其他位取反,最后整个数+1,就得到原码(这个方式是得到负数的原码)
# include <stdio.h>
# include <stdlib.h>
int main(){
 int a = -15;
 printf("a = %X\n",a);
return 0;
}

分析: a 是int类型 占四个字节 因此原码应该是 8000000F,转成反码,符号位不变 其他按位取反应该是FFFFFFF0,补码是反码+1 应该是 FFFFFFF1 打印结果:

a = FFFFFFF1

分析结果: 计算机中存储都是补码,使用原码只是人为看着方便,补码转原码是符号位不变,其他按位取反之后再+1 ,因为 FFFFFFF1---->8000000E+1 ----->8000000F ---->-15

补码运算

-7+-5

分析: -7 的源码4个字节的十六进制表示 80000007,它的反码是FFFFFFF8,它在计算机中的存储的补码是FFFFFFF9
-5 的源码是80000005,它的反码是FFFFFFFC,在计算机中的存储的补码是FFFFFFFD
两数相加相当于在补码相加
 11111111 11111111 11111111 11111001
+11111111 11111111 11111111 11111011
------------------------------------------
 11111111 11111111 11111111 11110100  === FFFFFFF4

FFFFFFF4 ==取反===> 8000000B==+1===> 8000000C = -12

# include <stdio.h>
# include <stdlib.h>
int main(){
 int a = -7;
  int b = -5;
 printf("a = %X\n",a);
 printf("b = %X\n",b);
 printf("a+b = %X\n",a+b);

return 0;
}

打印结果 
a = FFFFFFF9
b = FFFFFFFB
a+b = FFFFFFF4

-7+5

分析: -7 的源码4个字节的十六进制表示 80000007,它的反码是FFFFFFF8,它在计算机中的存储的补码是FFFFFFF9
5 的源码是00000005,它的反码是00000005,在计算机中的存储的补码是00000005
两数相加相当于在补码相加
 11111111 11111111 11111111 11111001
+00000000 00000000 00000000 00000101
------------------------------------------
 11111111 11111111 11111111 11111110 ==FFFFFFFE
FFFFFFFE ==取反===> 80000001==+1===> 80000002 = -2

# include <stdio.h>
# include <stdlib.h>
int main(){
 int a = -7;
  int b = 5;
 printf("a = %X\n",a);
 printf("b = %X\n",b);
 printf("a+b = %X\n",a+b);

return 0;
}

打印结果 
a = FFFFFFF9
b = 5
a+b = FFFFFFFE

-7+9

分析: -7 的源码4个字节的十六进制表示 80000007,它的反码是FFFFFFF8,它在计算机中的存储的补码是FFFFFFF9
9 的源码是00000009,它的反码是00000009,在计算机中的存储的补码是00000009
两数相加相当于在补码相加
 11111111 11111111 11111111 11111001
+00000000 00000000 00000000 00001001
------------------------------------------
 00000000 00000000 00000000 00000010 ==00000002
00000002 =====>2

# include <stdio.h>
# include <stdlib.h>
int main(){
 int a = -7;
  int b = 9;
 printf("a = %X\n",a);
 printf("b = %X\n",b);
 printf("a+b = %X\n",a+b);

return 0;
}

打印结果 
a = FFFFFFF9
b = 9
a+b = 2

-7+7

分析: -7 的源码4个字节的十六进制表示 80000007,它的反码是FFFFFFF8,它在计算机中的存储的补码是FFFFFFF9
9 的源码是00000007,它的反码是00000007,在计算机中的存储的补码是00000007
两数相加相当于在补码相加
 11111111 11111111 11111111 11111001
+00000000 00000000 00000000 00000111
------------------------------------------
 00000000 00000000 00000000 00000000 ==00000000
00000000 =====>0

# include <stdio.h>
# include <stdlib.h>
int main(){
 int a = -7;
  int b = 7;
 printf("a = %X\n",a);
 printf("b = %X\n",b);
 printf("a+b = %X\n",a+b);

return 0;
}

打印结果 
a = FFFFFFF9
b = 7
a+b = 0

在计算机系统中,数值一律用补码来存储,主要的原因

  • 统一了0的编码
  • 将符号位和其他位统一处理
  • 将减法运算转变为加法运算
  • 用两个补码表示的数相加时,如果最高位有近卫,进位被舍弃掉
有符号数和无符号数
  • 有符号数的最高位为符号位,0 表示整数 1 表示负数
  • 无符号数的最高位不是符号位,而是数的一部分 无符号数不可能为负值
数据类型占用空间取值范围
short2字节-215~215-1
int4字节-231~231-1
long8字节-263~263-1
unsigned short2字节0~ 216-1
unsigned int4字节0~ ~232-1
unsigned long8字节0~ ~264-1

sizeof关键字

  • sizeof 不是函数,所以不需要包含任何头文件,它的功能是计算一个数据类型的大小,单位是字节
  • sizeof的返回值类型是size_t
  • size_t类型在32操作系统下是unsigned int 是一个无符号的整数,在64系统中是unsigned long
# include <stdio.h>
# include <stdlib.h>
int main(){
short a = 10;
int b = 10;
long c = 10;
unsigned short d = 10;
unsigned int e = 0;
unsigned long f = 0;
printf("sizeof(short) = %lu\n",sizeof(a));
printf("sizeof(int) = %lu\n",sizeof(b));
printf("sizeof(long) = %lu\n",sizeof(c));
printf("sizeof(unsigned shot) = %lu\n",sizeof(d));
printf("sizeof(unsigned int) = %lu\n",sizeof(e));
printf("sizeof(unsigned long) = %lu\n",sizeof(f));
return 0;
}

输出结果:

sizeof(short) = 2
sizeof(int) = 4
sizeof(long) = 8
sizeof(unsigned shot) = 2
sizeof(unsigned int) = 4
sizeof(unsigned long) = 8

char类型

char类型的定义和输出

char类型的变量用于存储一个单一的字符,每个字符占用一个字节,在给char类型的字符赋值时,使用单引号

字符变量实际上并不是把该字符本身放到变量的内存单元中去.而是将该字符对应的ASCII编码放到变量的存储单元中,char的本质就是一个字节的整型

#include <stdio.h>
int main()
{
	char ch = 'a';
	printf("sizeof(ch) = %lu\n", sizeof(ch)); //1

	printf("ch[%%c] = %c\n", ch); //打印字符 ch[%c] = a
	printf("ch[%%d] = %d\n", ch); //打印‘a’ ASCII的值 ch[%d] = 97
	char A = 'A';
	char a = 'a';
	printf("a = %d\n", a);		//97
	printf("A = %d\n", A);	//65

	printf("A = %c\n", 'a' - 32); //小写a转大写A
	printf("a = %c\n", 'A' + 32); //大写A转小写a

	ch = ' ';
	printf("空字符:%d\n", ch); //空字符ASCII的值为32
	printf("A = %c\n", 'a' - ' '); //小写a转大写A
	printf("a = %c\n", 'A' + ' '); //大写A转小写a

	return 0;
// }
char类型的输入
#include<stdio.h>
int main()
{
    char ch;
    printf("请输入ch的值");
    scanf("%c",&ch); // 输入不要带回车\n
    printf("ch = %c\n", ch);
    return 0;
}

转义字符
转义字符含义
\a警报
\b退格,将当前位置移到前一列
\f换页,将当前位置移动到下一页开头
\n换行,将当前位置移动到下一行开头
\r回车,将当前位置移动到本行开头
\t水平制表,跳到下一个table位置
\v垂直制表
\代表一个反斜杠字符“\”
'代表一个单引号
\“代表一个双引号
?代表一个问号
\0代表数字0
\ddd8进制转义字符,d范围0~7
\xhh16进制转义字符,h范围09,af,A~F
#include<stdio.h>
int main(int argc, char const *argv[])
{
    
    printf("abc");
    printf("\rfg\n"); //  /r是回到了行首,fg覆盖掉了ab 整体打印的效果是 fgc

    printf("abc");
    printf("\befg\n"); // /b 是向前退了一格,efg覆盖了c 整体的打印效果是abefg

    printf("%d\n",'\123'); // 123 转换成10进制是83
    printf("%d\n",'\x80'); // 80是二进制保存的 char 只存储一个字节 80是 10000000 这个是补码   值为-128

    return 0;
}

浮点型

浮点型白娘是用来存储小数数值的,分为两种

  • 单精度浮点数 float 内存中占4个字节
  • 双精度浮点数 double 内存中占8个字节

由于浮点型变量是由有限的存储单元组成,因此只能提供有限的有效数字,在有效位以外的数字将会被舍去,这样可能会产生一些误差

  • 以f结尾的常量是float类型(例如3.14f) ,其他类型的都是double类型的(例如3.14)
#include <stdio.h>
int main(int argc, char const *argv[])
{
    // 传统方式赋值
    float a = 3.14f;
    double b = 3.14;
    printf("a = %f\n",a); //打印float 使用%f a = 3.140000
    printf("b = %lf\n",b); //打印double 使用%lf b = 3.140000

    // 科学法赋值
    a = 3.2e3f; //// 3.2*1000 = 3200; e可以写作E
    printf("a = %f\n",a); // a = 3200.000000
    a = 3.2e-3f; //// 3.2*0.0.001 = 0.0032; 
    printf("a = %f\n",a); // a = 0.003200
    a = 3.141592657f;
    printf("a = %f\n",a); //a = 3.141593
    b = 3.141592657;
    printf("b = %lf\n",b); //a = 3.141593
    return 0;
}

  • 打印时,默认输出6位小数点
  • float类型能够保证的精度是7位有效数字
  • double  类型能够保证的精度是15位有效数字
#include <stdio.h>
int main(int argc, char const *argv[])
{
        float a = 3.1415926456;
        double b = 3.1415926456;
        printf("a=%.8f\n",a); //a=3.14159274
        printf("b=%.8lf\n", b); //b=3.14159265
    return 0;
}

类型限定符

限定符含义
extern声明一个变量,并不会创建存储空间
const定义一个常量,其值不能被修改
volatile防止编译器优化代码
register定义寄存器变量,提高效率,这是一个建议型的指令,如果CPU有空闲寄存器,这个指令就会生效,如果没有就不会生效

字符串

  • 字符串是内存中一段连续的char空间,以‘\0’(数字0)结尾
  • 字符串常量是由双引号扩起来的字符串序列

字符常量与字符串常量的不同

  • ‘a’ 表示的是字符常量,在内存中存储的是‘a’
  • "a" 表示的是字符串常量,在内存中存储的是‘a’'\0'
  • 每个字符串结尾,编译器都会自动添加一个结束标志位‘\0’

字符串/字符的输出

printf

printf是输出一个字符串,格式字符总结:

打印格式对应的数据类型含义
%dint接受整数数值并将表示为有符号的十进制整数
%hdshort int短整数
%huunsigned short无符号短整数
%ounsigned int无符号8进制
%uunsigned int无符号10进制
%x,%Xunsigned int无符号16进制
%ffloat单精度浮点数
%lfdouble双精度浮点数
%e,%Edouble科学计数法表示的数
%cchar字符
%schar*字符串
%%%输出一个百分号

字母l 加在d,u,x,o 前面 表示长整数

  • 表示左对齐 m 表示数据的最小宽度 0 表示输出的前面补上0,直到占满制定列宽为止,不可以搭配使用- m.n m 表示域宽,对应的输出项在输出设备上所占的字符数,n 指的是精度,用于说明输出的浮点数的小数位,对于数值型来说 如果未指定n时,隐含的精度为n = 6
#include <stdio.h>
int main(int argc, char const *argv[])
{

    int a = 100;
    printf("a = %d\n", a); //格式化输出一个字符串
    printf("%p\n",&a); //输出a在内存中的地址编号
    printf("%%d\n"); //输出%d

    int b = 10;
    printf("b=%6d\n",b);//最小宽度是6 前面补了4个空格 b=    10
    printf("b和a=%-6d%d\n",b,a);//左对齐,最小宽度是6 因为在后面补了4个0 :b和a=10    100
    printf("b=%06d\n",b);//b=000010 前面补0
    printf("b和a=%-06d%d\n",b,a);//左对齐如果后面补0的话改变了原来的含义,因此还是补的还是空格:b和a=10    100

    double d = 12.3;
    printf("d=%-10.3lf哈哈\n",d);//左对齐精度补0 剩余补空格d=12.300    哈哈

    printf("d=%e\n",d);//d=1.230000e+01

    return 0;
}
putchar

putchar 是输出一个字符

     putchar(65); /// 输出A