C语言要点总结-关键字总结

别来无恙 提交于 2020-02-25 16:11:57

目录

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关键字总结:

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. 以上情况,在遇到++或--时,又会是不一样的情况

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关键字,该数据成员就是类内的静态数据成员。其特点如下:

  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关键字的作用:声明一个变量为自动变量,存储于栈区

在函数内定义变量时,如果没有被声明为其他类型的变量都是自动变量,也就是说,省去类型说明符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;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!