对象
被存储的每一个值都占用一定的物理内存,这样的内存称为对象。
int a = 73;
a
是一个标志符。指定了硬件内存中的对象的方式,并提供了存储在对象中的值。
int * p = &a;
p
是一个标志符,指定了储存地址的对象。但是 *p
不是标志符,是个表达式, 它指向了一个对象。
作用域
作用域描述程序中可访问标识符的区域。 一个C变量的作用域可以是块作用域、函数作用域、函数原型作用域或文件作用域。块是用一对花括号括起来的代码区域。
函数原型作用域: int a(int a, double b)
小括号扩起来的就是函数原型作用域。
文件作用域即声明的变量在所在文件从定义处到文件末尾都可用,这个变量也称为全局变量。
int a = 10; // 本文件和同程序的其他文件都可以使用 a变量
static b = 22; // 只有本文件可以访问 b
int main(){
}
如果文件要使用其他文件中的外部变量,则必须用 extern
声明该变量。
extern char Name;
存储期
静态存储期
存储在静态内存中存储地址不变但值可以变,在程序的执行期间一直存在,关键字 static
声明的变量变量具有静态存储期。
线程存储期
自动变量
默认情况下,声明在块或函数头中的任何变量都属于自动存储类别。可以使用 auto
关键字来显示的定义它。
变量具有自动存储期意味着,程序在进入该变量声明所在的块时变量存在,程序在退出该块时变量消失。原来该变量占用的内存位置现在可做他用。
自动变量不会初始化,除非显式初始化它。
int a; // a 变量的值是之前占用分配给 a 的空间中的任意值(如果有的话),它不会是0
int b = 10;
寄存器变量
变量通常储存在计算机内存中。如果幸运的话,寄存器变量储存在CPU的寄存器中,或者概括地说,储存在最快的可用内存中。
由于寄存器变量储存在寄存器而非内存中,所以无法获取寄存器变量的地址。
用 register
声明寄存器变量,编译器必须根据寄存器或最快可用内存的数量衡量你的请求,或者直接忽略你的请求,这个时候寄存器变量会成为普通变量,不管怎样都不能使用地址运算符。
register int a;
存储类别说明符
- auto说明符表明变量是自动存储期,只能用于块作用域的变量声明中;
- register说明符也只用于块作用域的变量,它把变量归为寄存器存储类别,请求最快速度访问该变量。同时,还保护了该变量的地址不被获取;
- 用static说明符创建的对象具有静态存储期,载入程序时创建对象,当程序结束时对象消失;
- extern说明符表明声明的变量定义在别处;
注意:外部函数可以被其他文件的函数访问,但是静态函数只能用于其定义所在的文件。
doubleg amma(double);/*该函数默认为外部函数*/
static double beta(int, int);
extern double delta(double, int);
在同一个程序中,其他文件中的函数可以调用gamma()和delta(),但是不能调用beta(),因为以static存储类别说明符创建的函数属于特定模块私有。
随机数
rand()
函数生成随机数字。这个函数取值范围是0
到 RNAD_MAX
之间,RAND_MAX被定义在stdlib.h中,其值通常是INT_MAX。
下面实现一个自己的随机数程序:
myrand.c
文件:
static unsigned long int next = 1;
unsigned int myrand(void) {
// 生成伪随机数的魔法公示
next = next * 1103515245 + 12345;
return (unsigned int)(next / 65536) % 32768;
}
main.c
文件:
#include <stdio.h>
#include "myrand.c"
extern unsigned int myrand(void);
int main() {
for (int i = 0; i < 10; ++i) {
printf("%d\n", myrand());
}
}
分配内存
malloc()
malloc()
函数可以在程序运行时分配更多的内存,该函数接受一个参数:所需的内存字节数。malloc()函数会找到合适的空闲内存块,这样的内存是匿名的,它会返回动态分配内存块的首字节地址。
如果malloc()分配内存失败,将返回空指针。
double * ptd;
ptd = (double *) malloc(30*sizeof(double));
上面代码为30个double类型的值请求内存空间,并设置ptd指向该位置。注意,指针ptd被声明为指向一个double类型,而不是指向内含30个double类型值的块。
动态分配的内存数量只会增加,除非用free()
进行释放。
free()
函数的参数是指向malloc()
分配的内存的指针,用来释放这块内存。
calloc()
long * newmem;
newmem = (long *)calloc(100, sizeof(long));
calloc()
和malloc()
类似, 返回指向void的指针。calloc()
函数接受两个无符号整数作为参数(ANSI规定是size_t类型)。第1个参数是所需的存储单元数量,第2个参数是存储单元的大小(以字节为单位)。它会把分配的块所有位都设为0.
也需要用 free()
函数进行释放。
存储类别和动态内存分配有何联系?
静态存储类别所用的内存数量在编译时确定,该类别的变量在程序开始执行时被创建,在程序结束时被销毁。
自动存储类别的变量在程序进入变量定义所在块时存在,在程序离开块时消失。
动态分配的内存在调用malloc()或相关函数时存在,在调用free()后释放。使用动态内存通常比使用栈内存慢。
ANSIC类型限定符
const
声明对象的值可以初始化但是不能被修改。
volatile
volatile限定符告知计算机,代理(而不是变量所在的程序)可以改变该变量的值。通常,它被用于硬件地址以及在其他程序或同时运行的线程中共享数据。例如,一个地址上可能储存着当前的时钟时间,无论程序做什么,地址上的值都随时间的变化而改变。
restrict
restrict
关键字允许编译器优化某部分代码以更好地支持计算。它只能用于指针,表明该指针是访问数据对象的唯一且初始的方式。
_Atomic
并发程序设计把程序执行分成可以同时执行的多个线程,。当一个线程对一个原子类型的对象执行原子操作时,其他线程不能访问该对象。_Atomic
用来声明原子类型的变量。