目录
6关键字总结:
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,goto,return
- 存储类关键字(5个)
auto(自动变量),extern(外部类型变量),register(寄存器类型变量),static(静态类型变量),const
- 其他关键字(3个)
sizeof,typedef,volatile
6.1define(宏定义)
#define NUM 100
预处理部分,会将程序中所有的NUM全部单纯的替换为100,只是单纯的替换,不做其他判断。
以上过程称为宏展开
说明:
- 一般大写
- 可以是常数或者表达式
- 宏定义不作任何的语法处理
- 宏定义不是C语言,语句后面不用加分号
- 宏名有效范围的作用域从定义开始到该文件结束
- 可以用#undef命令终止宏定义的作用域
有一种特殊的宏定义:一般称为宏函数
偶尔出现较为简单的函数操作,可以使用宏函数,效率高,不会去动用多余空间
但是也请看清楚宏函数的本质,如上文所述,只是单纯的进行文本替换而已
如果打算宏代替函数,以此来提高程序的运行速度,假如在程序中只使用一次宏对程序的运行时间没有太大提高
#define SUN(x,y) ( (x) * (y) )
定义时还请务必注意,因为只是单纯的文本替换,所以在(x)* (y)外面还需要加一层括号。
说明:
- 宏的名字不能有空格
- 用括号括住每一个参数,并括住宏定义的整体
- 用大写字母表示宏的函数名
- 以上情况,在遇到++或--时,又会是不一样的情况
6.2const(恒定)
const定义常量(在C中不稳定)
const int a = 10;//const定义不稳定常量
之所以不稳定,是因为以上方式使用const,在C中,是可以使用一级指针对变量a的值进行修改的。
const int a = 10;//使用const定义不稳定常量
int* p = &a;//将不稳定常量a的地址赋给int类型的指针p,及p指向a的地址
*p = 100;//对a进行修改,是可以进行如此操作
//所以综上所述,使用const定义常量在C中不稳定
const修饰指针类型:
1:可以重新赋值,但是不能访问内容
int a = 10;
int b = 20;
const int * p = &a;
p = &b;//成立
*p = 100;//不成立
注意,此时的const后面跟的是指针的定义及赋值过程,const此时修饰指针类型,就const作用于int *
对指针进行重新赋值是可以的,但是无法改变指针所指向地址的内容;
被const修饰的指针类型,此时是无法修改内存中的数值的。
2:可以修改内容,但是无法改变指向(引用声明的本质)
int a = 10;
int b = 20;
int * const p = &a;//只能在此时进行赋值
*p = 100;//成立
p = &b;//不成立
注意,此时的const后面跟的是指针变量,const此时修饰指针变量,就const作用于p
对指针进行重新赋值是不允许的,指针指向是固定的,但是可以改变指针所指向地址的内容;
被const修饰的指针变量,此时是无法修改指针的指向。
那么综上,定义一个指针,其指向无法被修改且指向内存中的内容也无法修改,定义如下:
const int * const p = &a;//注意,只能在此时进行赋值
但是如此定义依旧不稳定,我们可以通过二级指针,对其指向的内存中的内容,进行修改。
const修饰全局变量时,存储在常量区,不能修改。
const修饰局部变量时,放在栈区,是可以修改的,通过指针。
const也常用于函数结构参数的修饰:
void Function(const int i,const double j,const struct MODE * Test)
{
.......
}
在一般传递占用较大内容的数据时,多选用指针传递,快捷方便,但是当函数只是使用这段数据而不是修改时,会出行一个风险
当指针成为参数时,效率很高,但是副作用也很明显,就是有能会出现失误,导致原始数据被修改
所以此时,使用const关键字,使得其无法被修改。高效率的同时,加强了程序的稳定性。
虽然依旧可以使用二级指针对其进行修改,但是如此明显的意图与参数加const矛盾
const此时更多提供的是一种声明,防止意外,这个参数,请不要修改,改了也没有意义。
在C++中,const会更加稳定和适用
6.3 sizeof
sizeof只关心一件事儿,就是数据类型,及获取某个数据类型所占用空间的字节数。
sizeof实际上是获取了数据在内存中所占用的存储空间,以字节为单位来计数。
其返回值是一个无符号整形(unsigned int)
括号里面甚至可以不是变量:
printf("size_int=%d",sizeof(int));
printf("size_short=%d",sizeof(short));
printf("size_double=%d",sizeof(double));
输出结果:
size_int=4
size_short=2
size_double=8
sizeof(常规变量);
如上所述,输出该数据类型所占用的内存大小,即多少个字节(Byte)。
sizeof(指针);
printf("size_int=%d",sizeof(int *));
printf("size_short=%d",sizeof(short*));
printf("size_double=%d",sizeof(double *));
printf("size_int=%d",sizeof(char *));
32位系统下,以上程序的输出结果均为4,64位为8;
sizeof(数组名);获取整个数组所有元素在内存中占有的内存空间大小。
int arr[] = {1,2,3};
printf("arr:%d",sizeof(arr));
输出结果是4x3 = 12;
int类型占有4个字节,arr数组中共3个元素,总计12字节(Byte)。
但是,当数组名作为指针在函数中传递时,数组名会降级成同类型的指针。
int main()
{
int arr[] = {1,2,3};
sizeforarr(arr);
printf("arr = %d",sizeof(arr));
return 0;
}
void sizeforarr(int test[])
{
printf("test = %d",sizeof(test));//将会降级成为int类型的指针
}
输出如下:
test = 4
arr = 12
可以使用sizeof()求数组的长度(使用时,主要数组名是否降级)
int arr[] = {1,2,3};
int len_arr = sizeof(arr)/sizeof(arr[0]);
sizeof(字符串数组名);
char str[]="hello";
printf("str = %d",sizeof(str));
输出:
str = 6
C语言会自动在在双引号""括起来的内容的末尾补上"\0"代表结束,字符串结束符占用一个字符。
以下几篇文章中关于sizeof的阐述也非常清晰,写的很好。
https://blog.csdn.net/jollyhope/article/details/1895357
https://blog.csdn.net/weixin_40087851/article/details/82190289
https://blog.csdn.net/u013812502/article/details/81198452
6.4static(静态)
c语言中static关键字用法详解:https://blog.csdn.net/guotianqing/article/details/79828100(主要参考,文章写的很好)
被声明为静态类型的变量,无论是全局的还是局部的。
都存储在全局区(静态区)(Global data Segment)中,其生命周期为整个程序。
静态变量如果没有被初始化,则自动初始化为0,只能够初始化一次。
6.4.1static修饰局部变量
静态局部变量使用static修饰符定义,即使在声明时未赋初值,编译器也会把它初始化为0。仅仅初始化一次,之后一直保留,保存在静态区/全局区中。
且静态局部变量存储于进程的全局数据区,即使函数返回,它的值也会保持不变。
主函数:
...........
for(int i = 0;i<10;i++)
{
StaticFunction();
}
...........
void MainWindow::StaticFunction()
{
static int a = 10;
a++;
qDebug()<<"a"<<a;
}
输出:
对比一下普通局部变量:
主函数:
...........
for(int i = 0;i<10;i++)
{
StaticFunction();
}
...........
void MainWindow::StaticFunction()
{
int a = 10;
a++;
qDebug()<<"a"<<a;
}
输出:
可见,静态局部变量的效果跟全局变量有一拼,但是位于函数体内部,就极有利于程序的模块化了。
6.4.2static修饰全局变量
全局变量定义在函数体外部,在全局数据区分配存储空间,且编译器会自动对其初始化。
普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。
也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量)。
静态全局变量仅对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。
6.4.3static修饰函数
函数声明为static,除了对该函数声明所在的文件可见外,其他文件都无法访问。
static函数的作用域仅在本文件,在内存中只有一份,普通函数在每个被调用中维持一份拷贝。
函数的使用方式与全局变量类似,在函数的返回类型前加上static,就是静态函数。其特性如下:
- 静态函数只能在声明它的文件中可见,其他文件不能引用该函数
- 不同的文件可以使用相同名字的静态函数,互不影响
6.4.4在C++等面对对象编程语言中
静态数据成员和静态成员函数:https://blog.csdn.net/qiana_/article/details/82083313
静态数据成员
在类内数据成员的声明前加上static关键字,该数据成员就是类内的静态数据成员。其特点如下:
- 静态数据成员存储在全局数据区,静态数据成员在定义时分配存储空间,所以不能在类声明中定义(在类声明在声明,在全局作用域中定义中进行定义)
- 静态数据成员是类的成员,无论定义了多少个类的对象,静态数据成员的拷贝只有一个,且对该类的所有对象可见。也就是说任一对象都可以对静态数据成员进行操作。而对于非静态数据成员,每个对象都有自己的一份拷贝。
- 由于上面的原因,静态数据成员不属于任何对象,在没有类的实例时其作用域就可见,在没有任何对象时,就可以进行操作
- 和普通数据成员一样,静态数据成员也遵从public, protected, private访问规则
- 静态数据成员的初始化格式:<数据类型><类名>::<静态数据成员名>=<值>
- 类的静态数据成员有两种访问方式:<类对象名>.<静态数据成员名> 或 <类类型名>::<静态数据成员名>
同全局变量相比,使用静态数据成员有两个优势:
静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性
可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能
静态成员函数
与静态数据成员类似,静态成员函数属于整个类,而不是某一个对象,其特性如下:
静态成员函数没有this指针,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数
出现在类体外的函数定义不能指定关键字static
非静态成员函数可以任意地访问静态成员函数和静态数据成员
6.5 auto(自动变量)
参考:浅析C语言auto关键字和C++ 中的auto关键字https://blog.csdn.net/LiuBo_01/article/details/80752734
C语言中,auto关键字的作用:声明一个变量为自动变量,存储于栈区
在函数内定义变量时,如果没有被声明为其他类型的变量都是自动变量,也就是说,省去类型说明符auto的都是自动变量。
修饰变量可以不用初始化,但是在C++中不可以这么操作。
6.6 4个与储存类型相关的关键字总结
6.6.1.auto
auto只能用来标识局部变量的存储类型。auto是默认的存储类型,不需要显示的指定。auto标识的变量存储在栈区中。
6.6.2.extern
extern用来声明在当前文件中引用,但是是在当前项目的其它文件中定义的全局变量***。
如果全局变量未被初始化*,那么将被存在BBS区(静态数据区)中,且在编译时自动将其值赋值为0;
如果已经被初始化,就被存在数据区中。
全局变量,不管是否被初始化,其生命周期是整个程序运行过程。为了节省内存空间,在当前文件中使用extern来声明其它文件中定义的全局变量时,就不会再为其分配内存空间。
6.6.3.register
声明为register的变量在由内存调入到CPU寄存器后,则常驻在CPU的寄存器中,因此访问register变量将在很大程度上提高效率,因为省去了变量由内存调入到寄存器过程中的好几个指令周期。但编译器可以忽略此选项。
在C语言中的register修饰的变量表示将此变量存储在CPU的寄存器中,由于CPU访问寄存器比访问内存快很多,可以大大提高运算速度。但在使用register时有几点需要注意。
1.用register修饰的变量只能是局部变量,不能是全局变量。CPU的寄存器资源有限,因此不可能让一个变量一直占着CPU寄存器。
2.register变量一定要是CPU可以接受的值。
4.不可以用&运算符对register变量进行取址。
5.register只是请求寄存器变量,不一定能够成功。
参考:https://blog.csdn.net/ccjoe/article/details/44756395
6.6.4. static
被声明为静态类型的变量,无论是全局的还是局部的,都存储在数据区中,其生命周期为整个程序。静态变量如果没有被初始化,则自动初始化为0,只能够初始化一次。
函数声明为static,除了对该函数声明所在的文件可见外,其他文件都无法访问。
常量区: 存放常量的区间,如字符串常量等,注意在常量区存放的数据一旦初始化后就不能被修改。 程序结束后由系统释放。
char s1[] = “abcdef”;
//1) s1在静态区,"abcdef"无需额外存放,就是数组s1内部,总共占用一个串的内存
const char *p =“abcdef”;
//2)p在静态区,“abcdef”,必须额外存放(在常量区,通常也在静态区),/总共占用一个指针,和一个串的内存
free()释放的是指针指向的内存!注意!释放的是内存,不是指针!这点非常非常重要!指针是一个变量,只有程序结束时才被销毁。释放了内存空间后,原来指向这块空间的指针还是存在!只不过现在指针指向的内容是未定义的。
非静态的全局变量可以用extern扩展到组成源程序的多个文件中,而静态的全局变量的作用域只限于本文件,不能扩展到其它文件。把全局变量改变为静态全局变量后是改变了它的作用域,限制了它的使用范围。
static函数的作用域仅在本文件,在内存中只有一份,普通函数在每个被调用中维持一份拷贝。
源自:https://blog.csdn.net/better_eleven/article/details/90046566
6.7 typedef
typedef(替换名称)
C 语言提供了 typedef 关键字,您可以使用它来为类型取一个新的名字。下面的实例为单字节数字定义了一个术语 BYTE:
typedef unsigned char BYTE;
在这个类型定义之后,标识符 BYTE 可作为类型 unsigned char 的缩写,例如:
BYTE b1, b2;
#include <stdio.h>
#include <string.h>
typedef struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} Book;
int main( )
{
Book book;
strcpy( book.title, "C 教程");
strcpy( book.author, "Runoob");
strcpy( book.subject, "编程语言");
book.book_id = 12345;
printf( "书标题 : %s\n", book.title);
printf( "书作者 : %s\n", book.author);
printf( "书类目 : %s\n", book.subject);
printf( "书 ID : %d\n", book.book_id);
return 0;
}
来源:CSDN
作者:SAW1113
链接:https://blog.csdn.net/qq_41605114/article/details/104497181