一、C 的关键字共有32个(关键字,又称保留字,是编译器能识别的特殊单词)
-
数值类型的关键字
char 字符型 short短整型 int整型 long长整型 float浮点型 double双精度浮点型
unsigned无符号 signed有符号 struct 结构体 union 共用体 enum枚举 void
-
控制语句的关键字
if else switch case default for do while break continue goto return
-
存储类关键字
auto extern register static const
-
其他关键字
sizeof typedef volatile
1、const用法
-
用const修饰变量:定义时就必须初始化,以后不能再更改;
-
用const修饰形参:该形参在函数里不能被改变;
-
用const修饰类成员函数:该函数对成员变量只能进行只读操作,就是const类成员函数是不能修改成员变量的数值的;
-
const修饰变量const int a:使变量相当于“常量”;
-
const修饰指针类型const int *p:不能改变指针变量指向的内存地址的值,但可以改变指向的地址;
-
const修饰指针变量 int *const p:不能改变指针变量指向的内存地址,但可以改变指针指向地址的值;
-
const int *const p:不能改变指针指向的地址,也不能改变指针指向地址的值;
-
修饰谁谁不变;
2、static用法
- static修饰局部变量:使其变为静态存储方式(静态数据区),那么这个局部变量在函数执行完成之后不会被释放,而是继续保留在内存中;
- static修饰全局变量:当源程序定义了全局变量时,其他外部程序可以通过extern关键字引用此变量,如果加了static关键字,则其他文件不可连接或引用该变量。即全局变量则对当前源程序有效;
static1.c文件
#include"static1.h"
#include<stdio.h>
int value = 1;
void funtion()
{
printf("hello world!");
}
static1.h文件
#ifndef __STATIC1_H
#define __STATIC1_H
void funtion();
#endif
main()文件
#include<stdio.h>
#include"static1.h"
extern int value;
int main()
{
value = 12;
printf("value的值:%d", value);
}
最后输出的结果value等于12,当把int value = 1改为static int value = 1,运行之后会报错,无法解析到外部符号value.
- static函数与普通函数的作用域不同:被static修饰的函数,只能在当前的源文件中使用,可以简称为内部函数,对于当前源文件以外使用的函数,应该在头文件中进行声明,要使用这些函数的源文件要包含这个头文件,但不能访问到static修饰的函数;
3、volatile作用
当一个值可能随时会发生改变时,编译器就不能去假设这个变量,优化器每次用到这个变量都必须去内存重新读取,而不是使用保存在寄存器里边的值。
4、typedef作用
通过typedef 可以为已有类型取一个新的名字。
二、数据类型所占的字节数(64位系统)
数据类型 | 字节数 |
---|---|
char | 1 |
short | 2 |
int | 4 |
float | 4 |
double | 8 |
long | 8 |
long long | 8 |
溢出:在数据进行操作的时候导致超出数据类型大小,会向前位进1,多于原始数据大小,会被系统舍弃,保留后面数据大小
- 整型数据在内存中所占的字节数与所选择的操作系统有关,long的数据类型长度不能比int的小,int不能比short小
- 当一个小的数据类型赋值给一个大的,系统会自动转换,不会报错,当一个大的数据类型赋值给小的,很有可能会丢失高位
三、C代码编译成可执行文件4步骤
-
预处理:宏定义展开,头文件展开,条件编译等,同时将代码中的注释删除,这里不会检查语法
-
编译:检查语法,将预处理后文件编译生成汇编文件
-
汇编:将汇编文件生成目标文件(二进制文件)
-
链接:C语言写的程序是需要依赖各种库的,所以编译之后还需要把库链接到最终的可执行文件程序中去
linux编译gcc常用选项说明:
参数 | 作用 |
---|---|
-o file | 指定生成输出的文件名为file |
-E | 只进行预处理 |
-S(大写) | 只进行预处理和编译 |
-c(小写) | 只进行预处理、编译和汇编 |
四、C语言的内存分区
可参考C语言内存分区
-
栈区(stack):存放函数的参数值、返回值、局部变量等
-
堆区(heap):用于动态内存分配
-
全局区/静态区
- 未初始化数据(bss):全局未初始化、静态未初始化数据
- 初始化数据(data):全局初始化、静态初始化数据
- 文字常量区(data):字符串常量
-
代码区(text):可执行文件的二进制代码(函数)
栈是向下生长,堆是向上生长,具体可以参考堆和栈的区别
具体实例:
#include<stdio.h>
int a0 = 0; //全局初始化变量;生命周期是整个程序运行期间;存储在data区;作用域是所有文件
static int a1;//全局未初始化变量;生命周期是整个程序运行期间;存储在bss区;作用域只限本文件
onst static a2 = 2;//全局静态变量,相当于常量;
extern int a3;//全局初始化变量;同a1,但它会到其他文件找变量
int main(void)
{
int a4;//局部变量,生命周期调用函数开始到结束,跟函数共存亡,作用域只限在本函数,存储位置是栈
volatile int a5;//局部易变的变量,其他同a4
return 0;
}
五、计算机字符存储形式
-
在计算机中,数值一律用补码来存储
-
正数的原码,反码,补码,都一样
-
负数的反码符号位不动,其他位求反
-
负数的补码为它的反码加1
-
补码符号位不动,其他位求反,最后整个数加1,得到原码
六、指针分类
地址0-255都是系统保留的,不能读,也不能写
野指针:是指向一个未知的内存空间,可能在读写的过程中出现错误
空指针:是指向内存编号为0(NULL)的空间,操作该内存空间会报错,一般用在程序条件判断
万能指针: void *p=&a, 赋值的时候需要强制转换成a的类型
七、函数的声明,定义,调用
extern:引用同一文件或不同文件的变量或者函数,变量必须是全局变量。
#include<stdio.h>
int main()
{
//引用
extern int add(int a,int b);
//调用
int a = add(10,20);
return 0;
}
//定义
int add(int a,int b){
return a+b;
}
在定义函数时指定的形参,在未出现函数调用时,它们并不占内存中的存储单元,所以它们本身实际意义上存在的,因此,形参不能提前赋值。
- 形参出现在函数的定义中,在整个函数体内都可以使用,离开函数则不能使用
- 实参出现在主调函数中,不一定是主函数,进入被调函数之后,实参则不能使用,即形参的改变不会改变实参
- 实参变量对形参变量的数据传递的“值的传递”,即单向传递,只由实参传给形参,而不能形参传回来实参
- 当实参变量对形参变量的传递是“地址传递”时,形参所做的所有改变都会改变实参的值。
- 在调用函数时,编译系统临时给形参分配存储单元。在调用结束后,形参单元才被释放
- 实参单元与形参单元是不同的单元。调用结束后,形参单元被释放,函数调用结束返回主调函数后则不能再使用该形参变量。实参单元仍保留并维持原值。因此,在执行一个被调函数时,形参的值如果发生改变,并不会改变主调函数中实参的值。
exit()函数:关闭所有文件,终止正在执行的文件
所在头文件stdlib.h
exit(1) 表示异常退出,这个1是返回操作系统的
exit(x)(x不为0)都表示异常退出
exit(0)表示正常退出
引用(&取别名)
- &可以是取地址符,将一个变量取地址赋值给指针
- 也可以作为变量的引用,如int &a = arr; 在声明引用时,必须同时对其初始化,引用声明之后,就相当于给变量arr取了一个别名a。
- 引用本身不是一种数据类型,因此引用本身不占用存储单元,对引用求地址就相当于对目标变量求地址
- 不能建立数组的引用,因为数组是一个由若干个元素所组成的集合,所以无法建立引用
!!!引用最大的作用是作为函数的参数
- 以前的函数参数的传递是值传递,在被调函数对形参的操作不会影响到实参,而引用的效果跟传递指针的效果一样,即被调函数的形参操作会改变实参。
- 使用引用传递函数的参数时,在内存空间并没有产生实参的副本,它是直接对实参进行操作;而使用值的传递,需要在内存中申请出同样大小的空间,对实参进行一份拷贝,即形参相当于实参的一个副本;如果传递的是对象等大块数据时,使用引用比一般变量传递参数的效率和所占的空间小。
!!!使用常引用,保护传递给函数的数据不在函数中被改变
-
const 类型标识符 &引用名 = 目标变量名
-
通过这种方式传递给被调函数,被调函数不能对目标变量进行修改,只有只读的权利