C语言深入学习

       

一、计算机储存

1.1、计算机对数据类型的辨别

         编译器在编译C程序时将其转变为汇编指令,其中指明了数据类型,此外,每种数据类型都有固定的存储长度,计算机运行程序时,会根据具体类型读出相应长度的数据进行计算

1.2、程序的储存

指令空间+静态数据空间+动态数据空间

1.3、字长

计算机进行一次运算所能处理的二进制最大位数,常用的有32位、16位、8位等。

二、数据类型和运算篇

2.1、C语言数据长度(机器字长32位):

int :4字节(=字长) long :4字节

float:4字节 double:8字节

short:2字节 char:1字节

一个字节代表8bit,就是8个2进制位

%p:以地址的形式打印

%x:打印16进制数字

%o:打印8进制数字

2.2、赋值运算中的类型自动转换

将数据长度短的转换为数据长度长的; 

数据类型不同,则转换为相同类型

浮点运算总是转换为double类型

有符号与无符号混合运算时,总是转换为无符号

当赋值号右边式子计算完后,其结果类型自动转换为左边的数据类型

2.3、负数右移,在补码的右边补1,因此,多次右移后,补码每一位都变为1,即负数值为-1

2.4、自增自减运算的代码执行速度比赋值块【i++比i=i+1快】

2.5、复合赋值语句的代码执行速度比先运算再赋值块【直接一个等式出】

三、控制语句篇

3.1、除了二维数组,还可以定义更高维的数组,如a22[2],意义上表示空间,但是使用更高维的数组会使得计算机计算下标的工作量变大,影响效率

3.2、数组初始化特殊赋值方法

C99特性:

int a[40]={2,[10]=3,[30]=9};        //其他元素值都为0
int b[10][10]={[5][6]=2};           //其他元素都为0

3.3、动态分配数组

int *a;
a=(int*)malloc(10,sizeof(int));
a[0]=1;a[1]=2;
a=(int*)realloc(15,sizeof(int));    //数组扩展(原数据保留)
free(a);

四、函数篇

4.1、可变函数创建

void func(int length,……)
{
    int i;
    va_list vp;
    va_start(vp,length);
    for(i=0;i<length:i++)
        printf("%d",var_arg(v[,init]));
    va_end(vp);
}

4.2、void函数

  1. void代表无返回值,不需要return

  2. void代表返回值的类型是无类型,return要写但后面不加变量。

五、特殊数据类型篇

5.1、联合

类似于结构体,但成员公用一段内存,该内存的大小为成员最大长度,当为一成员赋值时,其他成员的值就会被覆盖

union myunion
{
    char a;
    int b;
};
union myunion c;

联合器的各个成员公用内存,并应该只能有一个成员得到这块内存的使用权(即对内存的读写)

结构体是选取最大的内存为储存空间,联合体是累加为储存空间

5.2、位域

将一个字的每一位看成成员来操作,位域不能跨越两个字节,因此其长度不能超过8位,定义方法:

struct font{
    unsigned char italic:1;
    unsigned char bold:1;
    unsigned char :4;
    unsigned char underline:2;
}
struct font font1;
font1.italic=0;
font1.bold=1;
font1.underline=3;

该位域成员包括:占用字节bit0位的italic、占用bit1位的bold、占用bit2-bit5四位的保留位、占用bit6和bit7的underline

5.3、位域与联合的组合运用

union Byte
{
    unsigned char byte;
    struct
    {
          bit0:1;
       bit1:1;
     bit2:1;
     bit3:1;
     bit4:1;
   bit5:1;
   bit6:1;
   bit7:1;
    }bit;
};

通过上面的组合,既可以整体操作字节,也可以方便实现位操作

六、内存管理篇

6.1、内存组织形式:

静态存储分配:编译时确定的变量空间,像全局变量与静态变量采用这种方式分配

栈:在编译时不分配空间,但需要知道程序所需的空间大小,然后在程序运行时进行分配,像函数内部的局部变量就采用这种分配方式,栈的分配方向是高地址方向向低地址,并且分配时连续的,是先入后出的队列结构,栈由编译器分配与释放,它的空间小于堆,当申请的空间超过最大栈空间时,会提示:“堆栈溢出”

堆:堆的分配时不连续块为形式的,系统通过链表将这些块连接起来,例如malloc等函数就是在堆中进行分配。堆的空间一般比较大。

七、goto,void,extern,sizeof、define分析

7.1、Goto语句分析

高手潜规则:禁用goto

项目经验:程序质量与goto出现的次数成反比

尽量少用

7.2、void的意义

void修饰的函数返回值和函数。void修饰函数返回值和参数仅为了表示无,C语言中没有定义void究竟是多达内存的别名

void指针的意义

C语言规定只有相同类型的指针才能相互赋值。void指针作为左值用于“接收”任意类型的指针,void指针作为右指针赋值给替他指针时需要强制类型转换

7.3、extern中隐藏的意义

extern用于声明外部定义的变量和函数,extern用于告诉编译器C方式编译

7.4、sizeof关键字分析

sizeof是编译器的内置指示符,不是函数,sizeof的值在编译期就已经确定了,单位是字节,返回的都是类型的字节

C语言规定sizeof返回size_t类型的值,这是一个无符号整数类型,这是因为C头文件系统使用typedef把size_t作为unsigned int或unsigned long的别名

typedef  unsigned int size_t
 int main(){
    int a=10;
    printf("%d\n",sizeof(a));
    printf("%d\n",sizeof(int));     //a的类型
    printf("%d\n".sizeof a);        //a两端的括号省略---->sizeof不是函数,函数必须有()
    
    int arr[10]={1,2,3,4,5,6};
    printf("%d\n",sizeof(arr));
    printf("%d\n",sizeof(int[10]));
}

7.5、const修饰变量

在C语言中const修饰的变量是只读的,其本质还是变量。const不是真的变量,可以通过指针改变其值。本质上,const只对编译器有用,在运行时无用

const int* p; //p可变,p指向的内容不可变

int const* p; //p可变,p指向的内容不可变

int* const p; //p不可变,p指向的内容可变

const int const p; //p和p指向的内容都不可变

*口诀:左数右指*

当const出现在*号左边时指针指向的数据为常量。

当const出现在*后右边时指针本身为常量。

7.6、volatile

可以理解为“编译器警告指示字”,用于告诉编译器必须每次去内存中取变量值。主要修饰可能被多个线程访问的变量,也可以修饰被未知因数更改的变量

多线程,嵌入式。给编译器看,让CPU必须从内存中调取变量。

优化时,编译器会将变量没有变化的自动返回原来的值,而不会去内存中再去调用。

7.7、define

由调整器去处理,无类型安全检测,不会分配内存,储存在代码段。

可通过#undef取消

八、条件编译

类似于C语言中的if……else,条件编译是预编译指示命令,用于控制是否编译某段代码。#if……#else……#endif被预编译器处理

#pragma指令用于指示编译器完成一些特定的动作,是一个预处理指令,用于向编译器提供窗外信息的标准方法。

九、scanf和scanf_s的区别

9.1、使用区别

sanf()不会检查输入边界,可能会造成数据溢出

scanf_s()会进行边界检查

9.2、意思

scanf表示从键盘输入指定格式的数据,因为带“_s”后缀的函数是为了让原版函数更安全,传入一个和参数有关的大小值,避免用到不存的元素,防止hacker利用原版的不安全性(漏洞)黑掉系统

9.3、参数不同

例如scanf("%s",&name,n),整形n为name类型的大小,如果name是数组,那n就是该数组的大小

十、if的判断

0为假,非0都为真,

引入stdbool.h库可以使用true和false

十一、操作符篇

11.1、算法操作符

11.1.2、除法操作符

  1. 对于/ 操作符,如果两个操作符都为整数,执行整数除法,但是会截断计算结果的小数部分,不会四舍五入

  2. 对于负数来说,驱0截断

  3. 两个操作符只要有一个是浮点数执行的就是浮点数除法,计算的结果是浮点数(事实上,计算机不会真的用浮点数除以整数,编译器会把两个运算对象转换为相同的类型,)

11.1.3、求模运算符

  1. 操作符的两个操作数必须是整数;返回的是整除之后的余数

  2. 负数求模的结果取决于第一个操作数

11.2、移位操作符

“<<” 左移操作符:左边抛弃、右边补0

">>" 右移操作符:逻辑右移:补0(无符号必须逻辑右移)

算术右移:补最高位(基本对有符号数使用算术右移)用原该值的符号填充

注:移位操作符的操作数只能是整数

11.3、位操作符

  • 按位与&:同1为1,有0则0

  • 按位或|:有1为1,同0为0

  • 按位异或^:相异为1;相同为0

注:他们的操作数必须是整数

int main(){
    int a=3;    //  00000000 00000000 00000000 00000011 —3的补码
    
    int b=-5;   //            11111111 11111111 11111111 11111011  -5的补码
    
    int c=a&b;    // == 3
    int d= a | b;   // == -5
    int e= a^b;  //按位异或  == -8
      
}

例题1:不能创建临时变量(第三个变量),实现两个数的交换

//法1:方法不太好,a+b时可能会导致正溢出
int main()
{
	int a=3;
	int b=5;
	printf("交换前:a=%d  b=%d",a,b);
	a = a+b;
	b = a -b;
	a = a -b;
	printf("交换后:a=%d b=%d",a,b);
	return 0;
}
//异或法
a^a=0;
0^b=b;

int main()
{
    int a=3;
    int b=4;
   printf("交换前:a=%d b=%d\n", a, b);
	a = a ^ b;
	b = a ^ b;//a^b^b=a
	a = a ^ b;//a^b^a=b
	printf("交换后:a=%d b=%d\n", a, b);
	
	return 0;

}

11.4、赋值操作符

11.4.1、左值与右值

左值:是可以放在等号左边的,一般是一块空间

右值:是可以放在等号右边的,一般是一个值,或者一块空间的内容

复合赋值符

a+=b  <----> a=a+b
a-=b  <----> a=a-b
a*=b  <----> a=a*b
a/=b  <----> a=a/b
a%=b  <----> a=a%b
a>>=b <----> a=a>>b
a<<=b <----> a=a<<b
a&=b  <----> a=a&b
a|=b  <----> a=a|b
a^=b  <----> a=a^b

11.5、单目操作符

! 	逻辑反操作
-	负值
+	正值
& 	取地址
sizeof 操作数的类型长度(以字节为单位)
~	对一个数的二进制按位取反
--	前置、后置--
++
*	间接访问操作符

按位取反(~),可以灵活地用较小的数表示极大数

注意事项

  1. 递增和递减操作符只能影响一个变量(只能影响一个可修改的左值)

    1. x * y++理解为x *(y++)

  2. 递增和递减操作具有很高的结合优先级,只有圆括号的优先级比他们高

注意:不要对自增、自减操作符太过关注

如果一个变量出现在一个函数的多个参数中,不·要对该变量使用递增或递减操作符

如果一个变量多次出现在一个表达式中,不要对该变量使用递增或递减符。方法是记录

间接访问操作符(解引用操作符)(*)

int a=10;
int * pa=&a;
*pa=20;   //*解引用操作符
printf("%d\n",a);
int *px=&(*pa); 	//其实是对a取地址
*px=30;
printf("%d\0n",a);
return 0;

(类型)强制转换

1、在某个量面前放置用

11.5、关系操作符

11.6、逻辑操作符

&& 逻辑与

|| 逻辑或

逻辑操作符只关注真假

逻辑与------都得满足才行11

逻辑或------满足其中一个即可

面试题:

#include <stdio.h>
int main(){
    int i=0,a=0,b=2,c=3,d=4;
    i=a++&&++b&&d++;	//第一个a为假,则后边被短路,不会被计算
    printf("a=%d\n  b=%d\n  c=%d\n  d=%d\n",a,b,c,d);		//1.2.3.4;
    return 0;
}

逻辑或与的短路原则:左操作数为真,右边不计算

#include<stdio.h>
int main()
{
    int i=0,a=1,b=2,c=3,d=4;
    i=a--||++b||d++;	//此处为a--,先使用a的值,a为1,为真,则后边的短路
       printf("a=%d\n  b=%d\n  c=%d\n  d=%d\n",a,b,c,d);		//0.2.3.4;
    return 0;
}

11.7、条件操作符

也叫作三目操作符

表达式1?表达式2:表达式3

11.8、逗号表达式

用逗号隔开多个表达式,从左到右7依次执行、整个表达式的结果是最后一个表达式的结果

·2//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);-------关系操作符a>b不影响a,b的值,不用管
是多少?       a=2+10  12  b=12+1=13
 
 
//代码2
if (a =b + 1, c=a / 2, d > 0) ----------d大于0 ,才执行if语句
 
//代码3 
a = get_val();
count_val(a);//先处理一遍
while (a > 0) 
{ 
        //业务处理
        a = get_val();
        count_val(a);
}
 
如果使用逗号表达式,改写:
while (a = get_val(), count_val(a), a>0)
{
         //业务处理
}
 
也可以使用do while来简化

11.9、下标引用、函数调用和结构成员

11.9.1、【】下标引用操作符

操作数:一个数组名+一个索引值

arr【2】:arr是首元素地址,arr【2】-------->编译器计算后:*(arr+2)----------->(7+arr)<---------->7[arr]

11.9.2、函数调用操作符

接收一个或者多个操作数:第一个操作数是函数名、剩余的操作数就是传递个函数的参数

11.9.3、结构成员

整型提升的意义

  1. 表达式的整型运算要在CPU的相应运算器内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。因此,即使两个char类型的想加、在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度

  2. 通用CPU是难以直接实现两个8比特字节字节相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须转换为int或unsigned int,然后才能送入CPU去执行运行计算

转换规则:向大的转换

一些问题表达式

1、计算路径不唯一

优先级可以保证先进行乘法运算然后再进行加法运算,不同的编译器选择的方案不同,因此产生了不唯一的计算路径

a*v+c*d+e*f   只能保证*的计算是比+早,但是优先级并不能决定第三个*比+早执行

2、操作数取值不唯一

c + --c;--c先算,但是+左边的c不知道是--c前取还是--c后取的

注释:操作符的优先级只能决定自减--的运算在+的运算的前面,但是我们没有办法得知,+操作符的左操作符的获取在右操作符之前还是之后求值,所以结果是不可预测的

3、完全非法错误代码

int main()
{
    int i=10;
    i=i-- - --i *(i=-3)*i++ + ++i; 	//错误代码
    printf("i=%d\n",i);
    return 0;
}

4、函数调用先后顺序不确定

int fun()
{
    static int cont=1;
    return ++count;
}
int main()
{
    int answer;
    answer=fun()-fun()*fun();
    printf("%d\n",answer);
    return 0;
}

上述代码answer=fun()-fun()*fun();中我们只能通过操作符的优先级得知,先算乘法,再算减法,但是函数调用先后顺序无法通过操作符的优先级确定。

5、操作数取值不唯一

#include <stdio.h>
int main()
{
 int i = 1;
 int ret = (++i) + (++i) + (++i);
 printf("%d\n", ret);
 printf("%d\n", i);
 return 0;
}

十二、(Linux)多线程

12.1、线程

轻量级的进程,线程虽然不是进程,但却可以看做是Unix进程的表亲,在同一进程中的多条线程将共享该进程的全部系统资源,如虚拟地址空间,文件描述和信号处理等等。但统一进程的多个线程有各自的调用栈,自己的寄存器环境,自己的线程本地储存,一个进程可以有很多线程,每条线程并行执行不同的任务。

线程可以提高应用程序在多核环境下处理诸如文件I/O或者socket I/O等会产生阻塞的情况的表现性能。

使用了多线程,可以使用共享的全局变量,所以线程间的通信(数据交换)变得非常高效。

创建线程 pthread_creat

pthread_create (thread, attr, start_routine, arg)
参数描述
thread指向线程标识符指针
attr一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值NULL
start_routine线程运行函数起始地址,一旦线程被创建就会执行
arg运行函数的参数,它必须通过把引用作为指针强制转换为void类型进行传递。如果没有传递参数,则使用NULL

For example:-pthread_t thrd1;  -pthread_arr_t attr;  -void thread_function(void argument); -char *some_argument;


phread_create(&thrd1,NULL,(void*)&thread_function,(void*)&some_argument)

结束线程

线程结束调用实例:phread_exit(void*retval); //retval用于存放线程结束的退出状态

用于显式地退出一个线程,通常情况下,pthread_exit()函数是在线程完成工作后无需继续存在时被调用

连接和分离线程

pthread_join(threadid,status);

pthread_detach(thread)

pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止。当创建一个线程时,它的某个属性会定义它是否是可连接的(joinable)或可分离的(detached)。只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连。pthread_join() 函数来等待线程的完成。

包含两个参数

pthread_t th //th是要等待结束的线程标识
    void **thread_return //指针thread_return指向的位置存放的是终止线程的返回状态

调用实例:pthread_join(thrd1,NULL);

简单例子:

十三、Windows的多线程

13.1、定义线程函数

线程就是描述进程的一条执行路径,进程内代码的一条执行路径。一个进程至少有一个主线程,且可以有多个线程,线程共享进程的所有资源。线程主要包括两个部分:

  • 一个是线程的内核对象,操作系统用它来对线程实施管理,内核对象也是系统用来存放线程统计信息的地方

  • 另一个是线程堆栈,它用于维护线程在执行代码时需要的所有函数参数和局部变量。

实现多线程,就要启动线程函数,线程函数不同于常规函数,线程函数需要用到WINAPI来定义,

DWORD WINAPI welcomedonghuasub(void *p)
{
    char str[50];
    int i;
    for(i=0;i<43;i++)
    {
        memset(str,0,sizeof(char)*50);
        printf(str,"welcome".i+1)
    }
    ExitThread(0);
}

一、CreateThread

函数功能:创建线程

函数原型:

HANDLE WINAPI CreateTtread (

LPSECURITY_ATTRIBUTESlpThreadAttributes,

SIZE_TdwStackSize,

LPTHREAD_START_ROUTINElpStartAddress,

LPVOIDlpParameter,

DWORDdwCreationFlags,

LPDWORDlpThreadId

);

函数说明:

第一个参数表示线程内核对象的安全属性,一般传入NULL表示使用默认设置

第二个参数表示线程栈空间大小,传入0表示使用默认大小

第三个参数表示新线程所执行的线程函数地址,多个线程可以使用同一个函数地址

第四个参数是传给线程函数的参数

第五个参数指定额外的标志来控制线程的创建,为0表示线程创建之后就可以进行调度,如果为CREATE_SUSPENDED则表示线程创建后暂停运行,这样它就无法调度,直到调用Resume Thread()。

第六个参数将返回线程的ID号,传入NULL表示无需要返回该线程ID号

函数返回值:

成功返回新线程的句柄,失败返回NULL

HANDLE handle = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL);

二、WaitForSingleObject

函数功能:等待函数-使线程进入等待状态,直到指定的内核对象被触发·

函数原形:

DWORD WINAPI WaitForSingleObject(

HANDLE hHandle,

DWORD dwMilliseconds

);

函数说明:

第一个参数为要等待的内核对象

第二个参数为最长等待时间,以毫秒为单位,如传入5000就表示5秒,传入0就立即返回,传入INFINETE表示无线等待。

因为线程的句柄在线程运行时是未触发的,线程结束运行,句柄处于触发状态。所以可以用WaitForSingleObject()来等待一个线程结束运行。

函数返回值:

在指定的时间内对象被触发,函数返回WAIT_OBJECT_0. 超过最长等待时间对象任未触发返回WAIT_TIMEOUT。传入参数有错误将返回WAIT_FAILED

#include <stdio.h>
#include<Windows.h>

DWORD WINAPI ThreafFunc(LPVOID);

int main()
{
    HANDLE hThread;
    hThread = CreateThread(NULL,0,,ThreadFunc,0,0,NULL);
    printf("我是主线程,pid=%d\n",GetCurrentThreadId());
    
    WaitForSingleObject(hThread,0);
    return 0;
}

DWORD WINAPI ThreadFunc(LPVOID p)
{
    Sleep(10000);
    printf("我是子线程·,pid=%d\n",GetCurrentThreadId());
    return 0;
}

三、CreateThread和__beginthreadex()的区别

首先是从标准C运行库与多线程的矛盾说起,标准C运行库在1970年被实现了,由于当时没任何一个操作系统提供对多线程的支持。因此编写标准C运行库的程序员根本没有考虑多线程使用标准C库运行的情况。

多个线程访问修改导致的数据覆盖问题。

Windows操作系统提供了这样的一种解决方案——每个线程都将拥有自己专用的一块内存区域来供标准C运行库中所有有需要的函数使用。而且这块内存区域的创建就是由C/C++运行库函数_beginthreadex()来负责的

因此,如果在代码中有使用标准C运行库中的函数时,尽量使用_beginthreadex()来代替CreateThread()。

//创建多子个线程实例
#include <stdio.h>
#include <process.h>
#include <windows.h>
//子线程函数
unsigned int __stdcall ThreadFun(PVOID pM)
{
	printf("线程ID号为%4d的子线程说:Hello World\n", GetCurrentThreadId());
	return 0;
}
//主函数,所谓主函数其实就是主线程执行的函数。
int main()
{
	printf("     创建多个子线程实例 \n");
	printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");
	
	const int THREAD_NUM = 5;
	HANDLE handle[THREAD_NUM];
	for (int i = 0; i < THREAD_NUM; i++)
		handle[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);
	WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
	return 0;
}

四、等待多个线程返回WaitForMulitpleObjects

函数原型:

DWORD WINAPI WaitForMultipleObjects(
	_In_ 	DWORD nCount,
    _In_ const HANDLE *lpHadnles,
    _In_ BOOL bWaitAll,
    _In_ DWORD dwMilliseconds
)

参数说明:

  • 第一个参数DWORD dwCount为等待的内核对象个数,可以是0到MAXIMUM_WAIT_OBJECTS(64)中的一个值

  • 第一个参数CONST HANDLE*phObjects为一个存放被等待的内核对象句柄的数组。\

  • 第一个参数BOOL bWaitAll是否等到所有内核对象为已通知状态后才返回,如果为TRUE,则只有当等待的所有内核对象为已通知状态函数才返回,如果为FALSE,则只要一个内核对象为已通知状态,则该函数返回

  • 第一个参数DWORD dwMilliseconds为等待时间,和WaitForSingleObject中的dwMilliseconds参数类似

实例

#include<stdio.h>
#include<windows.h>

const unsigned int THREAD_NUM = 10;,
DWORD WINAPI ThreadFunc(LPVOID);
int main(){
    printf("我是主线程,pid=%d\n",GetCurrentThreadId());
    HANDLE hThread[THREAD_NUM];
    for(int i=0;i<=THREAD_NUM;i++)
    {
        hThread[i]=CreateThread(NULL,0,ThreadFunc,&i,0,NULL);
    }
    WaitForMultipleObjects(THREAD_NUM,hThread,false,INFINITE);
    return 0;
}

DWORD WINAPI ThreadFunc(LPVOID p)
{
    int n=*(int *)p;
    Sleep(1000*n);
    printf("我是,pid=%d的子线程\n",GetCurrentThreadId());
    printf("pid =%d 的子线程退出\n",GetCurrentThreadId());
    
    return 0;
}

参数bWaitAll为true,等待所有线程返回

四、线程终止

void ExitThread(DWORD dwExitCode);

该函数将终止线程的进行,并导致损伤系统清除该线程所有操作系统资源。

即便要强制终止线程,也要使用_endThreadEx(不使用endThread_),因为它会兼顾多线程资源的安全

BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);

异步函数,即它告诉系统去终止指定线程,但是不能保证函数返回线程已经被终止了,因此调用者必须使用WairForSingleObject函数来确定线程是否终止。因此此函数调用终止后终止的线程堆栈资源不会释放,一般不建议使用该函数。

十四、乱码

一、之前的字符集

ASCLL字符 ,适用于128个字符

GB2312

CBK :大陆的

二、Unicode

Unicode:包含最广泛。

字符集只是字符和对应码点的集合,不代表字符一定会以对应码点被存储在计算机里

字符编码才是从字符到计算机存储内容的映射,32比特

UTF-8,针对不同字符,编码后的长度可以是32比特、24比特、16比特、8比特【主流 】

具体规则是:码点在0到127范围内的字符字节映射为1字节长度的二进制数,

码点在128到2047范围内的字符映射为2字节的二进制数。让二进制编码的第一个字节由110开头,第二个字节由10开头。

码点在2048到65535范围的字符。映射为3字节的二进制数。第一个字节由1110开头、第二个字节由10开头、第三个字节由10开头。分为三个部分

码点在65536到1114111范围的字符,映射为4字节的二进制数,第一个字节由11110开头,后面都有10开头

但储存效率会越来越低

优点:

  • 兼容ASCLL

  • 节约空间

三、乱码的诞生

一种是Unicode和中文编码转换时产生。无法识别的用特殊符号代替【锟斤拷】

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值