c2
存储类,链接,内存管理
存储类
作用域
block scope: 代码块作用域
c99允许在代码块中变量使用之前的任何位置声明变量;'for(int i=0;;)'
function prototype scope: 函数原型作用域
files cope: 文件作用域,全局变量
链接
external linkage || internal linkage || no linkage
block scope 和function prototype scope只有空链接;file scope默认外部链接,即可以被其他文件使用;若全局变量由static关键字声明则该变量是该文件私有变量,不能被其它文件使用。
存储时期
static storage duration, 静态存储时期
automatic storage duration, 动态存储时期
file scope: 静态存储时期;
block scope:动态存储时期,即程序进入定义这些变量的代码块时将为变量分配内存,退出代码块则内存释放;
存储类
存储类 | 时期 | 作用域 | 链接 | 声明 |
---|---|---|---|---|
自动 | 自动 | block | 空 | auto,可省略 |
寄存器 | 自动 | block | 空 | register |
外部链接静态 | 静态 | file | 外部 | —— |
内部链接静态 | 静态 | file | 内部 | static |
空连接静态 | 静态 | block | 空 | static |
自动变量
- 相同变量名:内部定义覆盖外部定义
寄存器变量
- 获得较快的访问速度
- 不能再使用地址运算符
代码块作用域的静态变量
void trystat(void)
{
int fade=1;
static int stay=1;
printf("fade=%d and stay=%d",fade++,stay++);
}
//循环运行后fade每次都会初始化,而stay只初始化一次。
//对于函数内部'static int stay=1'可以被当作不是在运行时执行的语句,只是告诉编译器它的作用域在这个函数块里。
外部链接的静态变量
-
定义外部变量省略'extern'
-
若引用在其他文件定义的外部变量,则本文件中必须加关键字'extern'
extern的存在标志着这是一个引用声明,而非定义变量
-
若在main函数中不加关键字地声明一个与全局变量同名地变量,则该变量将覆盖全局变量
-
外部变量若不作初始化则被赋值为0
-
外部变量已用表达式初始化但只限于常量表达式,即表达式里没有变量
函数存储类
static声明使函数私有,不能被外部访问
extern可省略
随机数函数和静态变量
//o-version rand
static unsigned long int next = 1;
int rand0(void) {
next = next * 1103515245 + 12345;
return (unsigned int)(next / 65536) % 32768;
}
//这解释了为什么每次运行结果都是一样的,因为next初始值是固定的
//解决的方案是产生“新种子”,这也是next声明为静态的原因
//1-version rand
static unsigned long int next = 1;
int rand1(void) {
next = next * 1103515245 + 12345;
return (unsigned int)(next / 65536) % 32768;
}
void srand1(unsigned int seed) {
next = seed;
}
标准库
#include<stdlib.h>
#include<time.h>
int main(){
srand((unsigned int)time(NULL));
for (int i=0; i<10; i++)
{
printf("%d ", rand()%10);
}
}
// 要取[a,b)之间的随机整数(包括a,但不包括b),使用:
(rand() % (b - a)) + a
分配内存
标准存储类自动决定作用域和存储时期。更灵活的指定内存管理规则则使用库函数:'malloc()''free()';
malloc指定的内存是匿名的,返回指向void的指针,如此方便将返回值(首地址)赋值给一个相对应的指针变量进行内存管理。
#include<stdlib.h>
double *ptr;
ptr=(double *)malloc(10*sizeof(double));
//与数组名作为首地址指针由相似的功能
//指定可变数组
int n;
ptr=(double *)malloc(n*sizeof(double));//legal
free(ptr);
//对应一个malloc()调用,应该调用一次free();free参数是先前malloc返回的地址。一般在程序终止后,内存会自动释放,故调用此函数不是必须的。但建议。
if(ptr==NULL){
exit(EXIT_FAILURE);
}
//若malloc无法返回所需数量内存则返回空指针
//EXIT_FAILURE指示程序异常终止
//EXIT_SUCCESS类似于0,指示正常终止。
long *newmen;
newmen=(long *)calloc(30,sizeof(double));
//calloc将全部位置0
以下的两种方法都可以建立动态的二维空间数组。
方法一:
int i,j;
int r,c;
int **a; //创建二维指针来指向数组
scanf("%d%d",&r,&c);
a = (int **) malloc(sizeof(int *) * r);//注意申请的指针格式
for (j=0;j<r;j++){
a[j] = (int *) malloc(sizeof(int) * c);
……
……
}
释放为:
for (j=0;j<r;j++)
free(a[j]);//先释放一维指针
free(a);//最后释放我二维指针
类型限定词
const
变量声明中带有const,则不能改变变量的值。
//指针
const float *ptr;//ptr指向常量,数据不可变
float * const ptr;//常量指针,指向同一地址,但地址中数据可变
const float * const prt;//地址数据都不可变
float const *ptr;//==const float *ptr
//多用在函数参数中
void dispay(const int array[]);
void dispay(const int * array);//则数组指向的数据不可变
//全局数据使用const
//在一个文件中定义
const double PI=3.1415;
//另一个文件引用
extern const double PI;
//包含头文件的方法
//无需重复声明。但是每一个文件都对其进行了一次数据备份
volatile
该变量除了可被程序改变以外还可以被其他代理改变
多个关键字的顺序不重要
restrict
int *restrict ptr=(int *)malloc(10*sizeof(int));
//只能用于指针
//表示该指针是访问该数据的唯一入口,以方便编译器优化代码
//可以作为指针形函数参量的限定词,如此假定函数体内没有其它标识符修改指针指向的数据
文件读写
何为文件
文本视图/二进制视图
读写
FILE *ptr;
char ch;
ptr=fopen(,"r");//失败返回空指针NULL
fclose(ptr);//成功返回0,失败返回EOF
//写入文件
ch=getc(ptr);//
putc(ptw,ch);
if ((ptw = fopen(fileW, "w"))==NULL) {
printf("fail to open %s", fileW);
exit(3);
}
while (ch=getc(ptr) != EOF) {
putc(ch,ptw);
}
文件打开模式
“r”,"w" | 只读/只写,不存在创建,存在删除原内容 |
---|---|
“a” | 追加内容,不存在则创建 |
“r+” | 可读可写 |
“w+” | 可读可写,破环式 |
“a+” | 可读可写,最佳式,不存在创建,存在追加 |
“wb”,"rb","ab" | 二进制 |
"ab+"=="ab+" | 二进制 |
fprintf/scanf/fgets/fputs
fprintf()/fscanf()
/*file.c */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main() {
FILE * pta;
char words[40];
//create file
if ((pta = fopen("addword.txt", "a+")) == NULL) {
fprintf(stdout, "cna't open this file\n");
exit(1);
}
//write
puts("enter some words and a blank line to end:");
while (gets(words) != NULL && words[0] != '\0') {
fprintf(pta, "%s\n", words);//第一个参数
}
rewind(pta);//移动指针
puts("file contents:");
while (fscanf(pta,"%s", words) == 1) {
printf("%s\n", words);//fscanf 第一个参数
}
//close
if (fclose(pta) != 0) {
puts("fail to close this file!");
}
return 0;
}
fgets()/fputs()
fgets(buf,Max,pta);//若有空余,则添加换行符
//遇结尾,返回eof
fputs(buf,pta); //不添加换行符
while(fgets(line,Maxline,stdin)!=NULL && line[0]!='\n'){
fputs(line,stdout);
}
随机存取
fseek()/ftell()
fseek(pta,0L,SEEK_END);//正常返回值0
fseek(pta,10L,SEEK_SET);//文件第10个字符
fseek(pta,2L,SEEK_CUR);//当前位置向前移动两个字节
fseek(pta,-10L,SEEK_END);//文件末尾回退10个字节
long last;
last=ftell(pta);//long 类型,返回据文件开始处字节
fgetpos()/fsetpos()
int fgetpos(FILE *restrict stream,fpos_t * restrict pos);
/*success->0; fail->not 0*/
int fsetpos(FILE *stream,const fpos_t *pos);
结构体
结构体
//声明,在main前
struct book {
char title[MAXTITL];
char author[MAXAUTL];
float value;
}; //struct definition end and 注意双引号
//definition
struct book sjtu,asee,*pta;//definition
struct book {
char title[MAXTITL];
char author[MAXAUTL];
float value;
} sjtu; //在声明之后直接定义
//初始化
struct book sjtu={
"love",
"Charles",
12.34
};
//指定初始化
struct book sjtu={
.title="love"
}
结构数组
struct book sjtu[100];
sjtu[2].value;//访问成员
指向结构的指针
struct book *pta,sjtu,lib[10];
pta=&sjtu; //与数组不同,结构体的名字不是该结构的地址
pta=&lib[0];
//pta+1:则pta指向lib[1]
//访问成员
pta->.title; //is sjtu.title if pta =&sjtu
//新的运算符
(*pta).title;//必须带圆括号,‘.’比‘*’运算级高
结构体作函数参数
int add(int ,int);
add(sjtu.num,situ.value);//函数不关心是否是结构体成员,只要参数类型符合
//结构地址
double sum(const struct funds *);
sum(&sjtu);
//结构作为返回值
struct book makeinfo(struct book sjtu);
字符指针和内存动态分配
struct name{
char *fname;
char *lname;
int letters;
}
void getinfo(struct name *pst){
char temp[8];
puts("enter your first name:");
gets(temp);
pst->fname=(char *)malloc(strlen(temp)+1);
strcpy(pst->fname,temp);
/*
两个字符串被保存在由malloc管理的内存中,结构体中只存放了地址
*/
...
}
//do not forget free them
void cleanup(struct name *pst){
free(pst->fname);
free(pst->lname);
}
复合文字和结构体
(struct book){"love","charles",2.33}
伸缩型数组成员
- 伸缩型数组成员必须是最后一个数组成员
- 结构中必须有一个其它成员
struct flex{
int count;
double average;
double scores[];//vla
}
struct flex *pf1,*pf2;
//分配空间
pf1=malloc(sizeof(struct flex)+5*sizeof(double));
pf1=malloc(sizeof(struct flex)+9*sizeof(double));
free(pf1);
free(pf2);
结构内容保存到文件
fread(&sjtu[count], size, 1, pb)
fwrite(&sjtu[filecount], size, count - filecount, pb);//写入多个,是开始地址位
高级数据处理
联合
枚举
enmerated type
enum spextrum{red,orange,yellow,green,blue,violet};//definition
enum spectrum color;//create entity
//访问
int c;
color=blue;
if(color==blue){}
for(color=red;color<=violet;color++){}//枚举常量是int型
//enum常量默认按序列0,1,2……赋值
enum levels{low=1000,medium=2000,high=3000};
typedef
位操作
二进制
字节位
一个字节(byte)8位(bit)。从左到右字节为标识为7-0.其中位7为高位(high-order-bit),0位为低位(low-order-bit)。
补码(two's-complement)
正0负1,取反加1.
二进制小数
位运算符
逻辑运算符
~; //取反
|; //或
&; //与
^; //异或
//赋值运算符
~=;
|=;
&=;
^=;
& //掩码,打开通道
| //打开位,其它位不变
&= ~//关闭位
^ //转置位
//查看
if((flag & mask)==mask)
移位运算符
(10001010) << 2//左移个数由右操作数决定,移入位由0补充
stonk =stonk<<2;
stonk<<=2;//乘以2的n次幂
sweet>>3;//右移。除以2的n次幂
位字段
struct box{
unsigned int opaque :1;
unsigned int fill_color:3;
unsigned int :4;
unsigned int show_border :1
};
预处理器
#define
符号常量
macro: 宏
Object-like macro: 类对象宏
macro expansion: 宏展开
预处理器发现程序中的宏后,用它的等价文本展开。若字符串中仍包括宏,则继续展开。例外情况是双引号中的宏。
语言符号,token.
使用参数
function-like macro
macro only replace text instead of calculating.
#define addnum(x) x*x
#define adddnum(x) (x)*(x)
//'#'创建字符串
#define psqr(x) printf("the square of "#x" is %d.\n",((x)*(x)))
//'##'粘合操作
#define XNAME(n) x ##n
//用圆括号括住每一恶搞参数,并括住宏的整体定义以保证正确分组
可变宏
#define PR(...) printf(__VA_ARGS__)
//省略号只能代替最后的宏参数
#include
格式
#include
#include"hot.H" 当前工作目录搜索
更多指令
#undef LIMIT //delete macro
//conditional def
#ifdef MAVIS
#include "horse.h"
#else
#include"cow.h"
#endif
#ifndef SIZE //是否为未定义的
#if
#elif
#else
#endif
预定义宏
macro | mean |
---|---|
__FILE__ | file name |
__DATE__\__TIME__ | date/time |
inline function
inline double square(int a){
}
c库
math.h
stdlib.h
通用工具库
atexit();//以函数名,即函数指针作为参数,先进后出
exit();//0成功终止
链表
create linkeed list
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define TSIZE 45
struct film {
char title[TSIZE];
int rating;
struct film *next;
};
int main() {
struct film *head = NULL;
struct film * prev, *current;
char input[TSIZE];
/*add list*/
puts("enter first move title:");
while (gets(input) != NULL && input[0] != '\0') {
current = (struct film *) malloc(sizeof(struct film));
if (head == NULL)
head = current;
else
prev->next = current;
current->next = NULL;
strcpy(current->title, input);
puts("enter the rating:<0-10>");
scanf("%d", ¤t->rating);
while (getchar() != '\n')
continue;
puts("enter next movie title:");
prev = current;
}
/*display list*/
if (head == NULL)
printf("No data entered!");
else
printf("Here is the movie list:\n");
current = head;
while (current != NULL) {
printf("Movie:%s Rating: %d\n", current->title, current->rating);
current = current->next;
}
current = head;
while (current != NULL) {
head = current->next;
free(current);
//内存释放之后,结构信息也不存在,故先用head保存next属性值
current = head;
}
printf("\nBye\n");
return 0;
}
ADT
构造接口
二叉搜索树
binary search tree