C++|C语言深度解剖笔记之关键字

1.c语言关键字有32个之多,这里主要挑选几个重要的说一说:
auto :声明自动变量,就是说这个auto声明一个变量是自动变量,所谓自动变量就是自动存储变量,也就是需要开辟内存来存储这个变量,编译器在默认情况下将所有变量都认为是auto的
c语言中使用auto修饰一个变量,这个变量可以先不初始化,编译器会使用一个随机值值初始化auto修饰的变量,即可以先定义再初始化,为了避免一个变量是随机值,最好对其初始化。
如 int x=9; int y=10
auto i ;
i=x+y; //输出的i是19;
而 float a=1.2; float b=3.1;
auto j;
j=a+b;这个输出是4,
所以在c语言中用auto修饰的变量默认是int类型,
而在c++中的auto除了c语言中的意思,新增一个类型识别的功能,通过变量的初始值或者表达式中参与运算的数据类型来推断变量的类型,
通过变量的初始值来推断就需要我们在编写c++文件是auto初始值进行初始化。如auto 1=1;
而表达式中参与运算的数据类型来判断
比如 string src;
for(auto c:src)
{
}
register:这个关键字是将变量尽可能的存储在寄存器以能够及时的(效率高)将变量供给内存使用,为什么是尽可能,而不是绝对呢,因为寄存器很贵,但是寄存器的存取速度又能够匹配上cpu的速度,所以一个cpu的寄存器数量有限,如果定义了很多的寄存器变量,它累死也不可能将全部的变量存储到这些寄存器中,所以可能这个时候寄存器被用光了,
我们的数据是从内存中拿出来先放到寄存器接着cpu从寄存器中拿数据,处理完了数据后,也是cpu将数据放到寄存器,接着数据通过寄存器存放到内存,寄存器与cpu之间的关系就像太监与皇帝之间的关系。
使用register需要注意:
1.register修饰的变量必须是cpu寄存器可以接受的类型。因为寄存器分为不同的类型,有数据寄存器,地址寄存器,指令寄存器等等, register 变量可能不存放在内存中,所以不能用取址运算符“&”获取register变量的地址,因为这个&获取的是变量在内存中的地址。
register是不能修饰全局变量的,因为全局变量是存储在内存中的,
只有局部自动变量和形式参数可以作为寄存器变量,其它(如全局变量)不行。
#include
#include

register int b; // error:有坏的存储类 C语言编译器对全局变量保存到内存中,与register(让其保存到cpu)冲突
int main()
{
register int a;
int * p = &a; //error:a为寄存器变量保存到cpu中不能取地址C语言不对其进行优化
register int a = 0x800000000; // error :从“__int64”到“int”截断
system("pause");
return 0;
}
const :
const int *p; // p 可变,p 指向的对象不可变
int const *p; // p 可变,p 指向的对象不可变
int *const p; // p 不可变,p 指向的对象可变
const int *const p; //指针p 和p 指向的对象都不可变
先忽略类型名(编译器解析的时候也是忽略类型名),我们看const 离哪个近。“近水楼
台先得月”,离谁近就修饰谁。
而const修饰一般变量时是可以与类型互换位置的。他们谁前谁后无所谓。如const int i和int const i一样。
const可以修饰局部变量,全局变量,函数参数,函数返回值,修饰指针,一般变量,数组等。
static:
修饰变量的时候,变量分为局部变量和全局变量
1.修饰全局变量的时候是静态全局变量,此时这个变量的作用域仅在该变量被定义的文件内,在其他文件中不能使用,即便在其他文件中用extern 声明这个变量也无法使用。
2.修饰局部变量,局部变量是定义在函数体内的,作用域也是只能在该函数内,但是由于静态变量是存储在内存中的静态存储区的,所以这个函数即便运行结束,这个变量的值还是不会被销毁,函数下次使用是还是这个值。
修饰函数,函数前加static 使得函数成为静态函数。修饰函数不是指的是函数的存储方式,而是将函数的作用域限制在函数所在的文件内。
3。在c++这种面向对象的语言中,static又被赋予了一种作用,(全都是属于类的,而不是类的对象)
1。修饰类的数据成员,在类内数据成员的声明前加上关键字static,该数据成员就是类内的静态数据成员,这时候的这个成员就属于类的,相当于全局变量,而不是属于某个实例或者对象。当这个静态成员变量被某个实修改后,其他的实例是能看到的,还有就是静态成员变量需要在类外初始化,这个静态数据成员可以被类访问,类的对象访问,类的对象指针访问。
2.普通函数都有this指针(这个this指针表明调用这个函数的对象),但是static修饰的函数是属于类的,不属于任何一个对象,所以c++中的静态函数是没有this指针的。
extern: 这个是修饰变量的,如extern int a; //声明一个全局变量a,extern 可以置于变量或者函数前,表示变量或者函数的定义在别的文件中.
extern“C”的用法:extern“C”是c++中新加的用法,如下:
extern "C"
{
int func(int);
int var;
}
意思是告诉编译器:将extern “C”后面的括号里的代码当做C代码来处理,当然我们也可以以单条语句来声明
如:extern "C" int func(int);
extern "C" int var;
关于这个可以详见C++中的extern “C”用法详解https://www.jb51.net/article/62351.htm
auto 是不加类型的,如auto a;

sizeof:怎么看这个sizeof是不是关键字,而不是函数呢,函数的形参是要带括号的
举个例子:
int a;sizeof a答案是4,这个不报错,所以sizeof是关键字
记住:sizeof 在计算变量所占空间大小时,括号可以省略,而计算类型(模子)大小时不能省略,如sizeof(int)这个的括号不能省略。
signed,unsigned:这两个关键字的意思是正负号的区别(0代表正号,1代表负号)。最高位表示符号,
对于一个32位的signed int类型整数的其值域范围是1111 11111111 1111 1111 1111 1111 1111到0111 1111 1111 1111 1111 1111 1111 1111也就是-2^31-1~2^31-1,一个32位的无符号的int类型整数其值表示范围是0~2^32-1.编译器缺省默认情况下数据为signed 类型。
计算机中的符号数有三种表示方法,即原码、反码和补码,在计算机系统中,数值一律用补码来表示(存储),补码的正零与负零表示方法相同,补码的求法:正数的补码与其原码一致,负数的补码,符号位为1,其余位为该数"绝对值"的补码按位取反,然后整个数加1
#include
#include
#include
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d\n", strlen(a));
char b[10];
b[0] = 1; b[1] = 2; b[2] = 0;
printf("%d", strlen(b));
system("pause");
return 0;
}
//整数0和结束符'\0'等价体现在,给字符串尾加上结束符时可以写成str[i]='\0'也可以写成str[i]=0。
//我们可以读到在这个题目中char是有符号的,而char是一个字节,即8位,所以char的取值范围是-2^7-1~2^7-1,即-127~127,
//但是由于+0和-0重复,就用- 0 代表 - 128了,所以取值范围是-128~127,当i是127时,a[127]=-128,。当i 继续增加,a[128]
//的值肯定不能是 - 129。因为这时候发生了溢出, - 129 需要9 位才能存储下来,而char 类型
//数据只有8 位,所以最高位被丢弃。剩下的8 位是原来9 位补码的低8 位的值,那么就要判断接下来的低8位的值,哪一个是0,,-256的补码(11 0000 0000)的
//低8 位为0。所以i为255的时候,a[i]=0; 在255前面共有0~244个数字,所以strlen(a)为255.-257的补码是10 1111 1111 ,低8位是1111 1111,又重新开始。

关于0和-0的原码 反码,补码
原码:0000 00001 0000 0000
反码:0000 00001 1111 1111
补码:0000 00000 0000 0000(这个是将最高位舍去了,最高位是1)
书上的几个问题
1.
int i = -20;
unsigned j = 10;
i+j 的值为多少?为什么?
以%d输出时是-10,%d是针对有符号输出的
以%u输出时需要计算i和j的补吗和,计算出来是11111111 11111111 11111111 11110110
低级的类型当与高级类型进行运算时会默认将低级类型转化为高级像你问的int 和unsigned会将整形转化为无符号整形 ,
至于上面的那个数字是全1的时候(2^32)减去2^3+2^0,即2^32-9=4294967286.
2.
unsigned i;
for(i=9; i>=0; i--)
{ printf("%u\n",i); }
这块代码有死循环的问题,因为unsigned得数时是永远大于0的。
volatile:保持内存的可见性,如果一个变量是被临时存在于寄存器里面,而这个时候内存里的变量变了,用这个关键字修饰就可以
让编译器访问内存。volatile 关键字和const 一样是一种类型修饰符,所以写作 volatile int a这样。
struct
struct将一些相关联的数据打包成一个整体,方便使用,比如网络协议、通信控制、嵌入式系统、驱动开发等地方经常要传送的不是简单的字节流(char 型数组),而是多种数据组合起来的一个整体,其表现形式是一个结构体。空结构体默认为1.
union:在union 中所有的数据成员共用一个空间(所有成员的起始地址一致),同一时间只能储存其中一个数据成员,所
有的数据成员具有相同的起始地址。如
union T
{
char a;
int b;
double c;
}一个union 只配置一个足够大的空间以来容纳最大长度的数据成员,最大长度是double 型态,所以StateMachine 的空间大小就是double 数据类型的大小。在C++里,union 的成员默认属性页为public。union 主要用来压缩空间。如果一些数据
不可能在同一时间同时被用到,则可以使用union。所以对联合体内任意一个变量的存取都是从起始位置开始。
还要注意大小端对联合体的影响,根据这个可以用联合体写一个判断大小端的代码。
enum :enum的使用和struct union的使用方式一样,只是enum的花括号内的变量都是枚举常量,都用大写字母表示。如果都没有赋值,它们的值从0 开始依次递增1。如果赋值了,那么就从赋值的那一个变量开始,将赋予的值依次加1,
typedef:给一个已经存在的数据类型取一个别名,关于typedef最常用的就是和结构体写在一起,其他的用法可以详见C/C++ typedef用法详解(真的很详细)https://blog.csdn.net/superhoy/article/details/53504472#commentBox
typedef 与#define 的区别
typedef只是为了增加可读性而为标识符另起的新名称(仅仅只是个别名),#define原本在C中是为了定义常量,而在C++中#define也可拿来进行起别名。不过个人感觉还是用typedef,因为宏定义只是简单的进行替换,如#defineINT int,INT a,b;相当于int a,b,而typedef是对一个类型进行了封装,新命名的标识符可以像int,float等类型一样,进行变量的定义
当const和typedef一起出现时,typedef不会是简单的字符串替换,尤其是typedef定义的是指针时,就要注意const与修饰指针究竟是修饰谁
typedef还有一个需要注意的是使用,typedef不能与存储类的几个关键字在一起使用,比如auto static extern register ,因为typedef也是存储类关键字,比如typedefstaticintINT; //编译error,会提示不能指定多个存储类









【C++|C语言深度解剖笔记之关键字】

    推荐阅读