C语言要点总结-关键字&保留字总结

目录

6关键字总结:

6.1define(宏定义)

6.2const(恒定)

6.3 sizeof

6.4static(静态)

6.4.1static修饰局部变量

6.4.2static修饰全局变量

6.4.3static修饰函数

6.4.4在C++等面对对象编程语言中

6.5 auto(自动变量)

6.6 4个与储存类型相关的关键字总结

6.6.1.auto

6.6.2.extern

6.6.3.register

6.6.4. static

6.7 typedef


 

6关键字总结:

C语言要点总结-关键字&保留字总结_第1张图片

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个)

sizeoftypedef,volatile

 

6.1define(宏定义)

#define NUM 100

预处理部分,会将程序中所有的NUM全部单纯的替换为100,只是单纯的替换,不做其他判断。

 以上过程称为宏展开

说明:

  1. 一般大写
  2. 可以是常数或者表达式
  3. 宏定义不作任何的语法处理
  4. 宏定义不是C语言,语句后面不用加分号
  5. 宏名有效范围的作用域从定义开始到该文件结束
  6. 可以用#undef命令终止宏定义的作用域

有一种特殊的宏定义:一般称为宏函数

偶尔出现较为简单的函数操作,可以使用宏函数,效率高,不会去动用多余空间

但是也请看清楚宏函数的本质,如上文所述,只是单纯的进行文本替换而已

如果打算宏代替函数,以此来提高程序的运行速度,假如在程序中只使用一次宏对程序的运行时间没有太大提高

#define SUN(x,y) ( (x) * (y) )

 定义时还请务必注意,因为只是单纯的文本替换,所以在(x)* (y)外面还需要加一层括号。

说明:

  1. 宏的名字不能有空格
  2. 用括号括住每一个参数,并括住宏定义的整体
  3. 用大写字母表示宏的函数名
  4. 以上情况,在遇到++或--时,又会是不一样的情况

我们再看一个示例:

#define  SUM(x) 3*x*x+1
int main()
{
int i=5,j=8;
printf("%d\n",SUM(i+j));
return;
}

3*i+j*i+j+1=3*5+8*5+8+1=64

define,就是单纯的就是替换内容,不进行其他操作

题目1:

若有宏定义:#define S(a,b) t=a;a=b;b=t由于变量 t 没定义,所以此宏定义是错误的。请问这句话的说法是正确的吗?

错误,宏定义不做语法检查,单纯的替换内容,编译时会显示t未定义,宏定义的时候不会检查错误。

题目2: 

定义宏#define DECLARE(name, type) type name##_##type##_type,
则DECLARE(val, int)替换结果为()

##是一种分隔连接方式,它的作用是先分隔,然后进行强制连接

“name”和第一个“_”之间被分隔了,所以预处理器会把name##_##type##_type解释成4段:“name”、“_”、“type”以及“_type”,name和type会被替换,而_type不会被替换

所以,结果是

int val_int_type

6.2const(恒定)

const只对它左边的东西起作用 ,  唯一的例外就是const本身就是最左边的修饰符,那么它才会对右边的东西起作用

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会更加稳定和适用 

const修饰指针的两种形式:

指针常量:关键字顺序为  *   、 const(和中文顺序一致), 例如 int * const a,表示指针a是一个常量,初始化后不可更改指向(永远指向某个对象),但是指向的对象的值可以修改,如*a=10;

常量指针:关键字顺序为 const、* ,例如 const  int  * a  (等同与int const * a) ,表示指针a所指向的对象是个常量(其值不可以改变),但指针a可以指向其它对象,如 *a=10;是错误的,a = b;是可以的

(来自牛客网@路飞的小伙伴9)

const一般都是修饰自己左侧最近的内容,左侧没有内容的时候,修饰右侧全部的内容,所以 * const 修饰的是指针,规定了指针的指向,const int * ....,修饰的就是值本身了。

既然要固定指针的指向,必然是要修饰指针,那么指针常量,自然是int * const。

6.3 sizeof

sizeof是C语言中的一个操作符(operator),不是函数调用

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"<

输出:

C语言要点总结-关键字&保留字总结_第2张图片

对比一下普通局部变量:

主函数:
...........

for(int i = 0;i<10;i++)
{
   StaticFunction();
}
...........

void MainWindow::StaticFunction()
{
    int a = 10;
    a++;
    qDebug()<<"a"<

输出:

C语言要点总结-关键字&保留字总结_第3张图片

可见,静态局部变量的效果跟全局变量有一拼,但是位于函数体内部,就极有利于程序的模块化了。

 

6.4.2static修饰全局变量

全局变量定义在函数体外部,在全局数据区分配存储空间,且编译器会自动对其初始化。

普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。

也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量)。

静态全局变量对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。

 

6.4.3static修饰函数

函数声明为static,除了对该函数声明所在的文件可见外,其他文件都无法访问。

static函数的作用域在本文件,在内存中只有一份,普通函数在每个被调用中维持一份拷贝。

函数的使用方式与全局变量类似,在函数的返回类型前加上static,就是静态函数。其特性如下:

  • 静态函数只能在声明它的文件中可见,其他文件不能引用该函数
  • 不同的文件可以使用相同名字的静态函数,互不影响

 

6.4.4在C++等面对对象编程语言中

静态数据成员和静态成员函数:https://blog.csdn.net/qiana_/article/details/82083313

静态数据成员
在类内数据成员的声明前加上static关键字,该数据成员就是类内的静态数据成员。其特点如下:

  1. 静态数据成员存储在全局数据区,静态数据成员在定义时分配存储空间,所以不能在类声明中定义(在类声明在声明,在全局作用域中定义中进行定义)
  2. 静态数据成员是类的成员,无论定义了多少个类的对象,静态数据成员的拷贝只有一个,且对该类的所有对象可见。也就是说任一对象都可以对静态数据成员进行操作。而对于非静态数据成员,每个对象都有自己的一份拷贝。
  3. 由于上面的原因,静态数据成员不属于任何对象,在没有类的实例时其作用域就可见,在没有任何对象时,就可以进行操作
  4. 和普通数据成员一样,静态数据成员也遵从public, protected, private访问规则
  5. 静态数据成员的初始化格式:<数据类型><类名>::<静态数据成员名>=<值>
  6. 类的静态数据成员有两种访问方式:<类对象名>.<静态数据成员名> 或 <类类型名>::<静态数据成员名>

同全局变量相比,使用静态数据成员有两个优势:

静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性
可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能


静态成员函数
与静态数据成员类似,静态成员函数属于整个类,而不是某一个对象,其特性如下:

静态成员函数没有this指针,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数
出现在类体外的函数定义不能指定关键字static
非静态成员函数可以任意地访问静态成员函数和静态数据成员


6.5 auto(自动变量)

参考:浅析C语言auto关键字和C++ 中的auto关键字https://blog.csdn.net/LiuBo_01/article/details/80752734

C语言中,auto关键字的作用:

  1. 声明一个变量为自动变量,存储于栈区,在函数内定义变量时,如果没有被声明为其他类型的变量都是自动变量,也就是说,省去类型说明符auto的都是自动变量。这里的其他类型指的是变量的存储类型即:静态类型变量(static )、寄存器类型变量(register)和外部类型变量(extern)
  2. 修饰变量可以不用初始化,但是在C++中不可以这么操作。
  3. C语言中,当省略了数据类型,则使用auto关键字修饰的变量默认为int 型数据。
//C
auto int val;//和const。static一样,都是修饰int类型的变量val的
auto val;//这就是没有给数据类型的情况,C会自动将其变成int类型

在C++中:auto关键字是一个类型说明符,通过变量的初始值或者表达式中参与运算的数据类型来推断变量的类型

由于,需要编译器推断变量或表达式的类型,所以,auto定义的变量必须初始化

使用auto也能在一条语句中声明多个变量。因为一条语句声明时只能有一种基本数据类型,所以该语句中的所有变量初始基本数据类型必须一样,例如:

auto i = 0, *p = &i;        //正确:i是整数、p是整形指针
auto sz = 0, pi = 3, 14;    //错误:sz和pi的类型不一致

同样。C++11中的关键字,也有相同的性质 decltype 

”如果我们仅仅是想根据初始值确定一个变量合适的数据类型,那么auto是最佳人选。而只有当我们需要推断某个表达式的数据类型,并将其作为一种新的数据类型重复使用(比如,定义多个相同类型变量)或者单独使用(比如,作为函数的返回值类型)时,我们才真正需要用到decltype“。

decltype和auto都可以用来推断类型,但是二者有几处明显的差异:

1.auto忽略顶层const,decltype保留顶层const;

2.对引用操作,auto推断出原有类型,decltype推断出引用;

3.对解引用操作,auto推断出原有类型,decltype推断出引用;

4.auto推断时会实际执行,decltype不会执行,只做分析。总之在使用中过程中和const、引用和指针结合时需要特别小心。

     

int i=0;
const int ci=i;
auto b=ci; //(1)
int *p=&i;
decltype(*p) c=i;//(2)

 以上(1)(2)中变量b,c类型为(int,int&)

 

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,除了对该函数声明所在的文件可见外,其他文件都无法访问。

                    C语言要点总结-关键字&保留字总结_第4张图片
常量区: 存放常量的区间,如字符串常量等,注意在常量区存放的数据一旦初始化后就不能被修改。 程序结束后由系统释放。

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 
#include 
 
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;
}

补充:C++保留字

C++保留了一些单词供自己和C++库使用。程序员不应该将保留字用作声明中的标识符。

保留字分为三类:关键字替代标记C++库保留名称 

以下图片来源:https://www.runoob.com/w3cnote/cpp-keyword-intro.html

C++关键字

C语言要点总结-关键字&保留字总结_第5张图片

C语言要点总结-关键字&保留字总结_第6张图片

(图片来源:C++ Primer Plus)  

替代标记

除关键字外,C++还有一些运算符的字母替代表示,它们被称为替代标记。

(图片来源:C++ Primer Plus) 

C++库保留名称

保留名称是给C++库使用的名称。

如果程序包含了某个头文件,则不应该将该头文件(以及头文件包含的头文件)中定义的宏名用作其他目的。

你可能感兴趣的:(C/C++/数据结构,C)