【c基础】数据类型与存储

目录

(1)数的进制和存储单位

①四种进制的对应关系

②二进制和bit,以及其它存储单位

③进制转化技巧

(2)数的原码、反码和补码

①计算机内部用二进制编码

②数分为有符号数和无符号数

③有符号数的编码会进一步区分为原码、反码和补码

④补码推算原码

⑤计算机用补码存储数字和参与运算的原因

⑥-128的补码等于127的补码+1

⑦有无符号数的大小范围

(3)关键字sizeof

(4)unsigned关键字

(5)四种整型:int, short, long, long long

(6)int不同进制的定义与printf输出

①如何定义不同进制的整数

②printf输出按不同进制输出整数

③输出结果的过程详解

(7)常量(宏常量和const常量)

(8)三种浮点类型:float, double, long double

①内存大小

②printf函数设置浮点数输出格式

(9)char类型

①char的本质是⼀个BYTE的整数

②char与整数,与字符串的区别

③char本身的值是ASCII码整数,可按ASCII码和符号两种⽅式输出

④char转义符号

⑤整数类型初始化为小数时取整数部分

(10)字符串

(11)printf输出格式

(12)整数溢出

(13)大端对齐和小端对齐

(14)volatile关键字

(15)register关键字与数据计算过程

(16)getchar函数与scanf函数

(17)运算符与++和--

(18)强制类型转化


(1)数的进制和存储单位

四种进制的对应关系

十进制二进制八进制十六进制
1111
711177
81000108
10101012a
15111117f
16100002010

②二进制和bit,以及其它存储单位

  • 一个二进制的位就是一个bit(比特),比如10001是5个比特,111是3个比特8个bit是一个BYTE(字节),计算机的最小存储单位是字节
  • 2个BYTE是一个WORD(字)
  • 2个WORD
  • 2个WORD是一个DWORD(双字)
  • 1024个BYTE 是1K
  • 1024K 是1M
  • 1024M 是1G
  • 1024G = 1T

不同场景下的单位:签合同时一定要注意这一点,写500M是模糊不清的,到底是500MB还是500Mb,这两者可是8倍差距

  • 512GB 硬盘 (大B,单位是字节)
  • 12Mb 网络带宽 (小b,单位是比特)
  • 200MB 文件的大小 (大B,单位是字节)
  • 100Mb网卡 (小b,单位是比特)

③进制转化技巧


2进制其实和8进制和16进制是一一对应的

2进制转化为8进制
010 101 010 111 111 010 101 011
2 5 2 7 7 2 5 3

2进制转化为16进制
0101 0101 0111 1110 1010 1011转化为16进制
5 5 7 e a b

二进制其实和8进制和16进制是一一对应的,在计算机语言中一般不直接用二进制,c语言更多的用8进制或者16进制

c语言不能直接书写二进制,用8进制和16进制来替代。因为2 8 16是一一对应的。


2进制转化为10进制
10101010 11111101
用2的n次方累加可以计算出


10进制转化为2进制

1. 先把这个数转化为8进制。以56为例,用56除以8,分别取余数和商数。然后用得到商数去除8。循环下去。直到新的商数为0时,停止循环。最后将所有余数从右到左拼起来就是最终结果。(辗转相除法)
56 8: 7(商数) 0(余数)
7 8: 0(商数) 7(余数)
所以十进制的56转化为8进制是70

2. 然后把8进制直接对应为2进制。
7 0
111 000
所以十进制的56转化为2进制是111000

(2)数的原码、反码和补码

①计算机内部用二进制编码

计算机内部使用二进制形式表示数据,即用”0“和”1“的编码表示数据。
最小存储单位为字节,就是8个bit,也就是8位二进制。
比如十进制的56,对应一个字节的编码方式为 00111000

②数分为有符号数和无符号数

有符号数,就是将编码的最高位用来表示正负,用0代表正数,1代表负数。
无符号数,就是将编码的最高位作为数的一部分,不是正负的意思。
无符号数的一个字节:0000 0000到 1111 1111,最小0000 0000为0,最大 1111 1111为255。因此无符号数时,一个字节的范围是在0-255。

有无符号数的原码对比
无符号数的原码
0000 0000 → 0
0000 1000 → 8
1000 1000 → 88 (左2进制,右16进制)

有符号数的原码
0000 0000 → 0
0000 1000 → 8
1000 1000 → -8

③有符号数的编码会进一步区分为原码、反码和补码

有符号数才区分原码补码,无符号数都是原码。
最高为符号位,符号位0代表正数,1代表负数。剩下的那些位数表示绝对值部分。
有符号数时,用补码表示的一个字节表示范围是-128到127 。

原码
7的二进制是多少 111
0000 0111 用一个BYTE表示
0000 0000 0000 0111 用一个WORD表示
0000 0000 0000 0000 0000 0000 0000 0111 用一个DWORD表示
-7的二进制是多少
1000 0111 一个BYTE
1000 0000 0000 0111 一个WORD
1000 0000 0000 0000 0000 0000 0000 0111一个DWORD

反码

正数和负数的反码不同。
  • 正数的反码就是原码本身。
  • 负数的反码,就是原码的基础上,除了符号位以外,其它位置上的数变为相反数,即1变为 0,1变为0。
7的反码
0000 0111 一个BYTE
0000 0000 0000 0111 一个WORD
0000 0000 0000 0000 0000 0000 0000 0111一个DWORD
-7的反码
1111 1000 一个BYTE
1111 1111 1111 1000 一个WORD
1111 1111 1111 1111 1111 1111 1111 1000 一个DWORD

补码

正数的原码、反码和补码是相同的。
负数的补码,等于反码加1。
7的补码
0000 0111 一个BYTE 0000 0000 0000 0111 一个WORD
0000 0000 0000 0000 0000 0000 0000 0111一个DWORD
-7的补码
1111 1001 一个BYTE
1111 1111 1111 1001 一个WORD
1111 1111 1111 1111 1111 1111 1111 1001 一个DWORD

④补码推算原码

先通过首位符号位判断正负。如果是正数,则原码=补码。
如果是负数,通过补码求原码有两种方法:
  • 补码的补码就是原码。一个数补码的补码就是原码,所以只要将补码再求补码,就是它的原码了。
  • 求补码的过程逆向,就是先将补码的先减1,再取反码(将除符号位以外的数取反,即1 变为0,0变为1)。

⑤计算机用补码存储数字和参与运算的原因

原码和反码都不能解决+0和-0的问题
假设用一个字节的八位来表示
[+0]原 = 0 0000000 → [+0]反 = 0 0000000
[-0]原 = 1 0000000 → [-0]反 = 1 1111111
缺点1: 对于同一个0,0这个数占用了有两套原码,有两套反码,因此无论是用原码还是反
码来存储东西都是不统一的。
缺点2: 对于8位机器数来说,8位共有256中不同的状态,理论上是可以表示256个数的。 但是由于一个0就占了两种状态,所以实际上原码和反码只能表示255个数。其中从原码来 看,1 1111111是最小的-127,1 0000001是-1,有两种编码表示0,,0 0000001表示 1,0 1111111表示最大的127 ,总共255个状态。

补码解决了+0和-0不同状态的问题

先看+0,它的补码等于原码,即0 0000000
再看-0,求它的补码,负数的补码是在反码的基础上末尾加1 = [-0]反 + 1 = 1 1111111 + 1
= 1 0000 0000
此时出现位溢出现象,将溢出位丢掉即将最左边的1丢掉,得到0000 0000,与+0的补码相同, 实现了统一。也就是对于0,不管是+0状态,还是-0状态,它们的补码都是0000 0000。

补码的运算效率更高
用原码运算还要判断符号位,效率不高。用补码参与运算可以不用考虑符号位,能提高运算效 率。比如用补码运算7-6:7的补码+(-6的补码) = 00000 111+11111 010=1 00000 001, 位溢出,丢弃最前面的1,得到 00000 001,转化为原码,就是1。

⑥-128的补码等于127的补码+1

由于补码的0状态的统一,所以8位机器数的补码是可以表示256个数的。最大的和原来是一样的 +127(0 1111111),按照这样推理,负数应该能能表示到-128才能一共有256个数。那 么-128的补码是多少呢? 实际上,补码有这样的规律,它是不断循环的,将+127的补码加1就 变成了-128
所以-128的补码是127的补码加1即0 1111111+1=1 0000000。
所以-128的补码就是1 0000000。
同理,-127的补码等于-128的补码加1。那么将-128的补码加1后再转化为原码是-127么?显然是这样的。看下 1 0000000+1=1 0000001。将1 0000001转成原码。1 0000001转成原码后是1 1111111,这个数显然是-127了。 -127的补码是1 0000001
明显可以看到- 1的补码等于-128的补码加127就是1 1111111。 将-1的补码试着加1看看是否是0的补码。[-1]补 + 1 = 1 1111111+1 = 0 0000000,的确是0 的补码。
所以从上面可以看出,一个数的补码表示形式是循环的,对这个数不断的加1会由负的最小逐渐的增加到0,再由0逐渐的增加到正的最大,此时再加1就又变成负的最小了,不断循环。

⑦有无符号数的大小范围

无符号数时,0000 0000到 1111 1111,最小为0,最大为255。共256种。
有符号数时,用补码来进行表示(在计算机中,有符号数都是以补码的形式来进行存放的),以8位补码为例,范围是-128到127。
  • 00000000表示零
  • 00000001~01111111 正数1~127
  • 10000000~11111111表示负数-128~-1。注意这里10000000表示-128,11111111表 示-1。

(3)关键字sizeof

注意sizeof是关键字,不是函数,所以不需要导入头文件
它的作用返回一个变量占的内存大小,单位是字节
#include <stdio.h>

int main()
{
    int a = 1;  // 直接写一个整数,比如这里的1,默认是10进制的整数
    int b;
    b = sizeof(a);
    printf("%d\n", b); // 4 // %d的意思是按照10进制的有符号数输出

    a = 11;
    int c = sizeof(a);
    printf("%d\n", c); // 4

    return 0;
}

c语言不规定数据类型的大小,具体某种数据类型占多大内存,和系统相关
在同一个系统下,具体的一种数据类型大小是相同的(比如上面的int类型是4个字节的大小)

在32位系统下,sizeof返回值是unsigned int,在64位系统下,unsigned long
int a = 100;
printf("%u\n", sizeof(a));  // 32位系统 //要注意这个小的细节
printf("%lu\n", sizeof(a)); // 64位系统

(4)unsigned关键字

定义无符号数的关键字

#include <stdio.h>

#define MAX 100u  // 如何定义宏时设为无符号整数

int main()
{
    // 1.定义有符号数
    int a;         // 定义了一个有符号的int
    signed int b;  // 与上面等价

    // 2.用unsigned关键字定义无符号数
    unsigned int c;

    return 0;
}

(5)四种整型:int, short, long, long long

int类型能表示的数的范围:整数占4个字节,一个字节是8位,因此一个无符号数最大可以表示到2^32=4294967296=40多亿(0到40多亿),有符号的就是除2可以表达20多亿个数(-20多亿到20多亿)。int在64位的windows或者64位linux一般是4个字节大小,但在其它不同的系统可能会有所变化。

  • short ,短整型,一般short是int的一半大小,即2个字节
  • int,整型,4个字节(int比较稳定,无论在哪个系统都是4个字节大小)
  • long,长整型,long在32位系统下一般是4个字节,在64位linux系统一般是8个字节,64位的windows一般是4个字节
  • long long,长长整型,long long在32位系统和64位系统下都是8个字节
c语言在不同系统上的变量类型大小可能是不一样的,不要死记,可以用sizeof看一下
  • 200   // 有符号整数
  • 200u  // 无符号整数
  • 200l或者200L  // 有符号长整数
  • 200lu或者200Lu或者200LU // 无符号长整数
  • 100ll或者100LL // 有符号长长整数
  • 100LLu或者100ull  // 无符号长长整型

#include <stdio.h>

int main()
{
    short a;
    printf("%d\n", sizeof(a)); // 2

    int b;
    printf("%d\n", sizeof(b)); // 4

    long c;
    printf("%d\n", sizeof(c)); // 8
    
    long long d;
    printf("%d\n", sizeof(d)); // 8

    unsigned int e;
    printf("%d\n", sizeof(e)); // 4

    return 0;
}

(6)int不同进制的定义与printf输出

①如何定义不同进制的整数

#include <stdio.h>


int main()
{

    int a; // 定义了一个整数,默认10进制

    int b = 020;  // 8进制的20(等于10进制的16)(注意开头是数字0)

    int c = 0X20;  // 16进制的20(等于10进制的32)(注意开头是数字0)


    return 0;
}

printf输出按不同进制输出整数

10进制还可以分为有符号数和无符号数输出

8进制和16进制只能无符号输出

#include <stdio.h>


int main()
{
    int a = 1000;
    printf("%d\n", a); // 按10进制的有符号数输出:1000
    printf("%u\n", a); // 按10进制的无符号数输出:1000

    printf("%o\n", a); // 按8进制(无符号)输出:1750
    printf("%x\n", a); // 按16进制(无符号)输出, abcd这些用小写:3e8
    printf("%X\n", a); // 按16进制(无符号)输出, abcd这些用大写:3E8

    return 0;
}

③输出结果的过程详解

  1. 先求出原码的补码(因为计算机存储时按照补码存储的);
  2. 对求出来的那个补码,按输出方式(是否有无符号)来改变补码的首位是符号位还是数的一部分:
  3. 求出原码,然后输出原码。
#include <stdio.h>


int main()
{
    int x = 10;  // 默认是10进制的有符号数
    printf("%d\n", x);  //10  //按10进制输出

    int y = 010;  // 8进制的10(等于10进制的8)
    printf("%d\n", y); // 8

    int z = 0x1b;
    printf("%d\n", z); // 27   // 按10进制输出
    printf("%o\n", z); // 33   // 按8进制输出
    printf("%x\n", z); // 1b   // 按16进制的小写输出
    printf("%X\n", z); // 1B   // 按16进制的大写输出

    int a = -11;        // 默认是10进制,有符号数
    printf("%u\n", a);  //4294967285 //按十进制的无符号数输出
    printf("%x\n", a);  //fffffff5   //按16进制的无符号数输出

    /*
    -11按4个字节在计算机在存储时的补码为:
        原码:1000 0000 0000 0000 0000 0000 0000 1011
        补码:1111 1111 1111 1111 1111 1111 1111 0101
    %u表示将这个数按照无符号数看待
    求其原码,由于是无符号数,原码就是补码
    然后进行输出,对应16进制
             f     f    f     f    f   f    f    5
              对应10进制 4294967285
    */


    return 0;
}

(7)常量(宏常量和const常量)

c语言里面用宏常量比较多
c++用const比较多
常量是不可修改的
#include <stdio.h>

#define MAX 100    //宏常量 //宏常量是不可修改的 //宏常量习惯用大写字母
#define NAME "ABC" //字符串形式的宏常量

#define MAX 100

int main()
{
    //MAX = 0; //宏常量是不可修改的
    printf("%dhello world\n", MAX);

    int a = 1; //整数
    a = 2;   //整数可以修改
    printf("%d\n",a);

    const int b = 10; //const生成常量,不可修改 //c++用const比较多
    //b = 100; //因为不可修改,所以会报错
    printf("%d\n", b);

    printf("%s\n", NAME);

    return 0;
}

(8)三种浮点类型:float, double, long double

①内存大小

#include <stdio.h>

int main()
{
    int pai=3.14;
    printf("%d\n", pai);

    float a = 3;
    a = a / 2;
    printf("%f\n", a); //1.500000 默认小数位数6位

    double b;
    long double c;
    printf("%lu,%lu,%lu\n", sizeof(a), sizeof(b), sizeof(c));//4,8,16

    // 作业:要求四舍五入输出
    double d = 4.49;
    int result = d + 0.5;
    printf("%d\n", result); // 5

    return 0;
}

②printf函数设置浮点数输出格式

#include <stdio.h>

int main()
{
    float x = 1.25678;
    printf("%f\n",x);    //1.256780 //默认6位小数
    printf("%4.f\n",x);  //"   1"     //4表示宽度,.和f之间啥都没有表示0个小数位  
    printf("%4.0f\n",x); //"   1" 
    printf("%4.2f\n",x); //"1.26"   //4表示宽度,.和f之间2表示2个小数位
    printf("%.3f\n",x);  //"1.257"  //3位小数,自适应宽度

    return 0;
}

(9)char类型

①char的本质是⼀个BYTE的整数

单引号引起来的字符就是char常量
'a'是⼀个字符常量a(单引号代表字符常量)
"a"是⼀个字符串
char、short、int、long、longlong这些类型都是存放整型,这些类型⽆法处理⼩数
#include <stdio.h>

int main()
{
    char a = 'x';
    int i = sizeof(a);
    printf("%d\n", i); //1

    return 0;
}
char a;定义了⼀个变量,名字叫a
char的本质其实是⼀个整数,⼤⼩是⼀个BYTE,在c语⾔中没有BYTE这种数据类型,⽤char替代
1111 1111
0000 0000

signed char 127-128
unsigned char 0-255
在C语⾔中,很多时候⽤unsigned char去表示⼀个BYTE类型
char常量就是对应的该字符对应的ASCII码
char是⼀个字节的整型,⽤来处理ASCII码
ASCII码是美国⼈对键盘上每⼀个符号进⾏的编码
int a = 'a';  //可以存放
char a = 500; //不可以存放 //char⼀个字节最⼤127,所以这⾥会出错,因为500太⼤了

char a = 97;
printf("%d\n",a); //结果是97
printf("%c\n",a); //结果是字符a

②char与整数,与字符串的区别

'2' 区别 2 
'2'是字符2,其对应ASCII值不是整数2
2是整数2

"100"100区别在哪⾥?

'1' '0' '0' '\0'
100是⼀个int型整数常量

③char本身的值是ASCII码整数,可按ASCII码和符号两种⽅式输出

#include <stdio.h>

int main()
{
    char a = 'x';

    // 1.内存大小
    int i = sizeof(a);
    printf("%d\n",i); //1

    // 2.两种打印结果
    a = 65;
    printf("%d\n",a); //65 //输出ASCII码
    printf("%c\n",a); //A //输出ASCII码对应的字符

    char b = 'A';
    if (a == b) // true
    {
        printf("char 65 == char 'A'\n");
    }

    // 3.与整数运算
    a += 2;
    printf("%d\n",a); //67 //输出ASCII码
    printf("%c\n",a); //C //输出ASCII码对应的字符

    // 4.整型溢出
    a = 127;
    a += 1;
    printf("%d\n",a); //-128 整型溢出

    return 0;
}

④char转义符号

char类型只能放标准的英⽂字⺟,因为标点符号那些。不能来定义中⽂。

char a = '我';  //会出错
char a = '\'';  //输出单引号'
不可打印char转义符号
\a,警报
\b,退⼀格
\n,换⾏
\r,回⻋
\t,制表符,就是⼀个tab键\\,就是\
\',单引号
\'',双引号
\?,问号
#include <stdio.h>

int main()
{
    char a = 'x';

    a = '\b'; //等于一个退格的ASCII码
    printf("(%d%c)\n", a, a); //(8)

    a = '\t'; //等于一个tab的ASCII码
    printf("(%d%c)", a, a); //(9	)

    return 0;
}

//回车\r 换行\n
回车就是退格到最左边,换行就是去到下一行abc(这里其实是回车+换行)
bcd
abc(只有换行,没有回车)
  bcd
bcd(abc是被bcd重叠了)这叫回车
在printf里面用\n时会自动加一个\r

⑤整数类型初始化为小数时取整数部分

#include <stdio.h>

int main()
{
    int x = 3.66;
    printf("%d\n", x); // 3

    return 0;
}

(10)字符串

字符串在内存中是一段连续的char空间,以'\0'结尾
  • 'a' 字符,在内存里就是一个字符a
  • "a" 字符串,在内存里两个连续的字符,第一个是'a',第二个是'\0'
  • "hello world"  → 'h' 'e' ....... 'd' '\0'
  • "a"字符串常量 → 'a' '\0' 'a'字符常量
  • "abc"  → 'a' 'b' 'c' '\0'
  • 'abc' 这样写是不对的,因为字符常量只能是一个ASCII字符

(11)printf输出格式

格式对应类型含义
%d
int
有符号十进制整数
%u
unsigned int
无符号十进制整数
%hd
short
短整数
%hu
unsigned short
无符号短整数
%o
无符号 八进制整数
%x
%X
无符号 16进制整数
x对应着abcdef
X对 应ABCDEF
%f
float或者double
%e
%E
double
科学计数法
%c
char
%s
%S
char * / wchar_t *
字符串。
特别地,当char *p = NULL时,%S将输出(null)
%p
void *
以16进制形式输出指针。
%%
输出一个百分号%
printf附加格式
l或者ll
放在d,u,x,o前面表示长整数
比如输出long 用%ld
输出long long用%lld
比如 无符号长 整数%lu
m(代表一个整数)
数据最小宽度
0
将输出前面补上0,直到占满指定宽度为止
-(减号)
左对齐(不可以搭配'-'使用)
特殊类型打印建议
类型建议
size_t%I64d
#include <stdio.h>

int main()
{
    putchar(100); //输出ASCII码为100所对应的字符 → d
    putchar(50000); //ASCII码循环后的那个ASCII码锁对应的字符 → P
    putchar('a'); //直接输出字符a
    //putchar('ab'); //putchar最多一个字符,这里会warning

    printf("hello world\n"); //用printf输出字符串

    // char x = 'abcd';  // 警告
    printf("%s\n", "abc");

    printf("%s + %s\n","abc", "bcd");

    int a = 100;
    printf("%p\n", &a); //000000000061FE18
    //&a表示取内存中的地址,输出地址用%p

    printf("(%6d)\n",a);   // (   100)
    printf("(%06d)\n",a);  // (000100)
    printf("(%-6d)\n",a);  // (100   )
    printf("(%-06d)\n",a); // (100   )
    //会warning, 直接忽略掉0,因为要是加上0就是100000,与原来有变化

    return 0;
}

(12)整数溢出

数的运算过程:先将原码转化为补码进行运算,然后将结果补码 按输出格式的是否有符号 转化为 原码再 按输出格式的某种进制 输出
#include <stdio.h>
int main()
{
    char x = 127;
    x += 1;
    printf("%d\n", x); //-128
    /*
    (1)先得到运算后的结果
    127 有符号char
    => 原码0111 1111
    => 补码0111 1111
    => 补码参与运算 加1 => 1000 0000
    => 转化为原码 = 1111 1111 + 1 = 1 0000 0000
    => 位溢出 丢掉除符号的最左边那位 1 000 0000
    => 1 000 0000 其实是 -128的原码
    => -128的原码:1 1000 0000 位溢出 丢掉除符号的最左边那位 => 1 000 0000
    (也可以从127 + 1的角度得到-128的补码和原码)
    所以x现在的值就是 -128

    (2)再看输出方式
    %d 是将 x看做有符号整数输出,本身就是有符号数,所以输出 -128
    具体拆解过程:
    -128对应的 int 型原码 为 1000 0000 0000 0000 0000 0000 1000 0000
    => 补码 1111 1111 1111 1111 1111 1111 0111 1111 + 1
    => 1111 1111 1111 1111 1111 1111 1000 0000
    %d 是将 x看做有符号整数输出,看做有符号数,因此要将补码的首位看做有符号数
    => 因此原码 = 1000 0000 0000 0000 0000 0000 1000 0000
    => 转化为10进制 就是 -128
    */

    printf("%u\n", x); // 4294967168
    /*
    (1)x现在是-128
    (2)按输出方式输出值
    => -128 原码 1000 0000 0000 0000 0000 0000 1000 0000
    => 补码 1111 1111 1111 1111 1111 1111 1000 0000
    => %u是当做无符号数输出,因此补码的首位要看是数的一部分
    => 无符号数补码就是原码,因此对应的整就是16进制的 ffffff80
    => 对应的10进制数就是4294967168
    */
    printf("%x\n", 4294967168); // ffffff80
    int y = 0x7fffffff;
    printf("%d\n", y); //2147483647
    y += 1;
    printf("%d\n", y); //-2147483648
    y += 2;
    printf("%d\n", y); //-2147483646

    return 0;
}
整形溢出的后果:本质上就是循环,比如整形的范围是-2147483648到2147483647之间,则 2147483647加1就为-2147483648,2147483647加2就为-2147483647

(13)大端对齐和小端对齐

如下图,一个int类型的变量a存放在内存地址中,需要占用4个Byte(注意计算机内存地址的最 小单位是Byte)。一个大于Byte的数据类型放到在内存中存放的时候要有先后顺序,现在要把 变量a拆成4部分分别放到4个字节中去,如何放呢?

  • 低内存地址放整数的低位,高内存地址放整数的高位,这种方式叫倒着放,术语叫小端对齐。电脑X86和手机ARM都是小端对齐的。下图左边圆圈存地址越小放的是整数越高位的数,比如内存地址为0对应整数的12,就是小端对齐。
  • 低内存地址放整数的高位,高内存地址放整数的低位,这种方式叫正着放,语叫大端对齐。很多unix的CPU都是大端对齐的。如下图右边圆圈部分,内存地址越小放的是整数越高位的数,比如内存地址为0对应整数的12,就是大端对齐。
  • 对于数字而言,现在无论是windows还是linux,一般是小端对齐,不过具体是哪种可能需要验证一下
  • 对于字符串而言,从左到右,拆分为一个一个的char,每个char对应一个字节,然后该char对应的ASCII值存储在一个BYTE上(大端对齐)

(14)volatile关键字

加volatile时C编译器不会⾃作聪明地去优化
#include <stdio.h>

int main()
{
    //volatile int a = 2;
    int a = 2;
    a = a + 1;
    a = a + 4;
    //无volatile也就是正常情况下时C的编译器会优化前两行代码,它们是不合理的  //会编译成a = a + 5
    printf("%d\n", a);  // 7

    //volatile int a=2;
    //a=a+1;
    //a=a+4;
    //加volatile时C编译器不会自作聪明地去优化

    //在特定场景下需要加volatile
    //也就是每一步都是有用的时候,但合并起来就会出错的情况,比如电路什么

    return 0;
}

(15)register关键字与数据计算过程

#include <stdio.h>

int main()
{
    int a = 2;
    a = a + 1;

    return 0;
}
  1. 先把数据放到内存⾥
  2. 再从内存⾥取出数据放到寄存器(寄存器是CPU中的基本存储单位)中运算
  3. 然后再从寄存器中取出数据到内存中
对应的汇编语⾔如下
mov b, 0
mov eax, b
add eax 1
mov b, eax

加上register关键字后

int main()
{
    //int a=2;

    //b这个变量在CPU的寄存器里面,而不在内存中  
    register int b=2; 
    b=b+1;
    printf("%d\n",b);

    return 0;
}
对应的汇编语⾔如下
两⾏代码⽐刚才4⾏代码少,可以增加代码的效率
不过register是建议型的指令,不是命令性的指令,如果CPU有空闲寄存器,那么register就⽣效,如果没有空闲寄存器,就⽆效
mov eax,0 
add eax,1

(16)getchar函数与scanf函数

  • getchar是从标准输⼊设备读取⼀个char
  • char a = getchar();//从标准输⼊设备读取⼀个char,并赋值给a
  • getchar();//可以看做是⼀个暂停,此时按回⻋就继续程序
  • scanf("%d",&b);//输⼊⼀个整数,使得b的内存地址存放的数为这个数
  • 通过scanf输⼊的时候,最后按的是⼀个什么键?回⻋键,回⻋键scanf会认为是输⼊完成,⽽不是字符串的内容
  • scanf认为回⻋和空格都代表输⼊完成
#include <stdio.h>

int main()
{
    int a;
    printf("int a = ");

    scanf("%d",&a); //第二个参数是变量的内存地址,
    // 输入什么数,这个变量内存地址就放什么

    int b;
    printf("int b = ");
    scanf("%d",&b);

    printf("char x =");
    getchar(); //这句话的作用是把回车吃掉,没有这句话回车会给a   
    char x = getchar();

    printf("%d%d%c\n",a,b,x);

    return 0;
}

(17)运算符与++和--

  • 不要写a+=1,显得不专业,⽤a++或者++a
  • a++先返回a的值,然后再⾃加1
  • ++a的意思是a先⾃加1,然后再返回a的值
  • 如果不能确定,或者要改变默认的优先级,⽤()

(18)强制类型转化

#include <stdio.h>

int main()
{
    int a=5;
    int b=2;
    double f=a/b;
    printf("%lf\n",f); //2.000000

    /*
    两个整数计算的结果也是一个整数
    浮点数与整数计算的结果是浮点数,浮点数和浮点数计算的结果还是浮点数  
    所以上面运算 5/2=2,然后double f=2;所以f=2.000000
    */

    double x = (double)a/b;
    //(double)a表示强制把a转化为double类型  
    printf("%f\n",x); //2.500000

    return 0;
}

  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值