说明:本文为浙江大学翁恺老师C语言课程的学习笔记,仅供自身参考学习,如有纰漏,请多指教,如有帮助,不胜荣幸。
一~六 初步
格式
#include<studio.h>
int main()
{
printf(“hello world!\n”);
return
}
二
变量定义
变量定义的一般形式:(类型名称)(变量名称)=(赋值)
int price=0(初始值) int price=0,int amount=1;
变量名第一个字符不能是数字
所有的变量第一次出现先赋值
读整数,赋给price变量
scanf(“%d”,&price);
定义常量 const int AMOUNT=100
两个整数的运算结果只能是整数
浮点数和整数运算时,结果是浮点数,按浮点算
10和10.0不同
int变成double(浮点),此时输入用“%lf”,
数据类型
整数 int printf(“%d”,…)
scanf(“%d”,…)
浮点数 double printf(“%f”,…)输出 scanf(“%lf”,…)
表达式 运算符与算子
赋值是一种运算,优先级低
1.复合赋值(+-*\%与=复合)
total+=(sun+100)/2
total=total+(sum+100)/2
2.递增递减运算符“++”“--”算子必须是变量,作用是给算子加一或减一
count++
count+=1
count=count+1
前置和后置不一样int a=10
a++=10 a=11 a++这个表达式是运算前的值
++a=10 a=12 ++a是运算后的值
count++ 给count+1 表达式的值是count
++count 给count加一 表达式的值是count+1
ps:“!=”是不等于
三
判断
if(条件成立){
}; (大括号内的语句缩进一个tab距离)
关系运算符
相等==
不相等 !=
大于> 大于等于>=
关系成立,结果为1,不成立,结果为0,
所有关系运算的优先级低于算数运算,但高于赋值
以两个斜杠//开头的语句把程序分成几部分,这个叫做注释
延续数行的注释,以“/*”开始,以“*/”结束
else 否则的话,在if的};后面跟上else{
}
if语句也可以没有大括号,直接跟在后面,没有分号就不算结束。如:
if()
……;(没有分号,次行缩进)
但是当if嵌套时,不用大括号有分不清else匹配的风险,(一般是就近原则,但有时内层if无else)。不要冒险!
嵌套 if() {…if…}else
级联if else if相当于串联
多路分支:switch case(相当于多个选择,多个if)
switch(type){
case 1:
printf(“你好”);
break;
case 2:…
……
}
括号里叫控制表达式,只能是整数型
case后叫常量,可以是常数,也可以是常数计算的表达式
case 1就是括号中变量type=1,break代表switch结束(出口)
四
循环(每次会回到while,直到条件不成立)
while(判断){
……;
}
循环体{大括号里}一定要有条件不成立的可能
ps:验证代码:个位数,10,0,复数,边界数据,特殊的倍数,有效范围两端的数据
ps:调试:在适当位置插入printf输出该处变量内容
do-while循环(先执行,再检查条件是否满足,满足则继续下一轮,不满足则结束)
do
{
<循环语句>
}while(<循环条件>);(一定要有分号)
rand()召唤一个随机整数
五
for循环(设定一个计数器并初始化,在计数器到达某值前,重复执行循环体,在这个过程中,计数器调整)
for(i=0;i<5;i=i+1){
printf(“%d”,i);
}
翻译:对一开始的i=0,当i小于5时,重复做循环体,每一轮循环完后,令i加一
循环的次数是n,结束后i为n(5),
tip:求和,初始值0;求积,初始值一
tip:for中每一个表达式都是可以省略的,
for(;条件;)==while(条件)
for==while
for(int i=1;i<=n;i++){
fact *=i;
}
int i=1;
while(i<=n){
fact*=i;
i++;
}
TIPS FOR LOOPS
如果有固定次数,用for
如果必须执行一次,用do-while
其他情况用while
循环控制:素数的判断
1.
#include<studio.h>
int main()
{
scanf(“%d”,&x);
int i;
for(i=2;i<x;i++){
if(x%i==0){
printf(“不是素数\n”);
}
}
return0;
}
一个数输出好多次,
2.引入一个判断变量
int i;
int isprime=1;
for(i=2;i<x;i++){
if(x%i==0){
isprime=0;
}
}
if(isprime==1){
printf(“是素数\n”);
}else{
printf(“不是素数\n”);
}
但是每一个都需要跑完全程,这很浪费
3.引入出口(break)
if(x%i==0){
isprime=0;
break;
}
保证可以整除时结束
ps:break vs continue
break:跳出循环,continue:跳过这一轮剩下的语句,直接进入下一轮循环
break和continue只能对它所在的那一层循环做
循环嵌套(如何输出100以内的素数)
在判断素数前后加循环(循环里还是循环)
int x;
for(x=1;x<=100;x++){
判断
……
if(isprime==1){
printf(“%d”,x);
}
}
注意:1.每一层的循环变量(计数器)应不一样
2.控制变量是x,条件也可以用其他变量表示(限制)
ps:想换行?
if(cnt%5==0){
printf(“\n”);// cnt是计数的
}
对不齐?printf(“%d\t”,x)改变输出的
\n的意思其实是回车换行!!!
\t相当于tab(相当于八个空格)
多重嵌套循环,如何找到一个就结束?
1.每层加break,但外层要设置条件,在前面做一个判断变量,否则(外面不设条件)第一次直接结束,不管找到没有。
2.goto语句(传送锚点)
goto out;然后在最后一个大括号后放上out;
ps:最好不用goto
tips:
1.如何表示正负交错?
定义一个sign=1,每次令sign=-sign,每次乘上sign即可
2.注释多行Ctrl+/,先调试一半
六
数据类型
c语言的变量必须:在使用前定义,且确定类型。
整数:char、short、int、long、long long(c99)
浮点数:float、double、long double(c99)
逻辑 bool(c99)
指针
自定义类型
类型名称:int、long、double
输入输出:%d、%ld、%lf(输出时为%f)
所表示数的范围:char<short<int<float<double
sizeof 一个运算符,给出某个类型或变量在内存中占据的字节数。sizeif(int) sizeof(i)
整数类型:
char:1字节,8比特 short:2字节 int:一个字(取决于编译器) long:一个字 longlong:8字节
一个常量后面加u,就是unsigned,即该数没有负数,纯二进制
一个以0开始的数字字面量是8进制
一个以0x开始的数字字面量是16进制
%o用于八进制,%x用于十六进制
ps:虽然整数有很多,没什么特殊需要,就用int
浮点类型
float:字长32,有效数字7;scanf %f printf %f,%e
double:字长64,有效数字15;scanf%lf printf %f,%e
可以表示0,但在0的一个极小邻域内无法表示
科学计数法的输出 printf(“%e\n,ff”) 如-5.67e+16
输出精度
在%和f中间加上.n可以指定小数点后几位,它是四舍五入的
浮点数超过范围
printf输出inf表示超过范围的浮点数
printf输出nan表示不存在的浮点数
带小数点的字面量是double而非float
float需要用f后缀表明身份
选择浮点类型:没有特殊需要,用double
字符类型
char是一种整数,也是一种字符
用单引号表示字符‘a’,‘1’
printf和scanf里用%c输出字符
字符运算:一个字符加一个数字得到ASCII码表中那个数之后的字符
两个字符的减,得到他们在表中的距离
大小写转换:
ASCII表中,大小写分开排列,顺序排列。
a-A可以得到两段间距离,
a+‘a’-‘A’大写转小写,
a+‘A’-‘a’小写转大写
逃逸字符
\b 回退一格
覆盖,但之后输出为空则不覆盖,如123\b——123
123\bA——12A
\t 到下一个表格位
制表位 每行的固定位置,\t使上下两行对齐
\n 换行 \r 回车
类型转换
自动类型转换
运算符的两边出现不一样的类型,自动转换成较大的类型(表示范围更大)
char--short--int--long--longlong(向右转)
int--float--double
对printf,任何小于int的类型都转换成int,float转换成double
scanf不会,要输入short,需要%hd
强制类型转换:(类型)值。如(int)10.2
注:有时小的变量不能表示大的变量
强制类型转换优先级高于四则运算,应该是:
int i=(int)(a/b);
而不是:int i=(int)a/b;
逻辑类型 bool
需要加上#include<std bool.h>,之后可以用bool和true false,输出仍是整数
逻辑运算
! 逻辑非
&& 逻辑与
|| 逻辑或
判断c是否是大写字母:
c>=‘A’&&c<=‘Z’
优先级 !>&&>||
9.赋值
短路:
对于&&,左边是false就不做右边了
对||,左边是true就不做右边了
所以不要把赋值,包括复合赋值组合进表达式
条件运算符
count=(count>20)?count-10:count+10;
条件、条件满足时的值和不满足时的值
优先级高于赋值,低于其他所有
逗号运算
逗号连接两个表达式,以右边的值作为结果,优先级最低
在for中使用
for(i=0,j=10;i<j;i++,j--)
七 函数
是一块代码,接收零个或多个参数,做一件事情,返回零个或一个值
函数定义
void sum(int begin,int end) 头部,sum叫函数名,void叫返回类型,圆括号里是参数表
{
int i;
int sum=0;
for( i=begin;i<=end;i++){
sum+=i;
}
printf("%d到%d的和是%d\n",begin,end,sum);
}
大括号里叫函数体
调用函数:函数名(参数值)
()起到了表示函数调用的作用,没有参数也要有()
从函数中返回值: return
return停止函数的执行,并送回 一个值(交给调用这个函数的地方,作为这个函数运行的结果)
return; return表达式;
没有返回值的函数
void 函数名(参数表) 不能使用带值的return,可以没有return
此时不能使用返回值的赋值,输出什么就是什么
函数先后关系
自上而下(先写出函数,再用函数)
函数原型
可以把函数第一行(函数头)粘贴到调用的函数前面,以分号结尾,就是函数的原型
作为声明;告诉编译器这个函数什么样,检查是否与定义一致
调用函数
如果函数有参数,调用函数时必须传递给它数量、类型正确的值
什么可以传递给函数的值?是表达式的结果,如:字面量、变量、返回值、计算结果
ps:调用函数时类型不匹配,会自动转换
c语言在调用函数时,只能传值给函数,调用参数和定义参数没有关系。详细的解释一下:函数里变量的运算只是读入了调用时相应参数的值,对这个值运算,而与调用参数无关,所以永远无法做到对参数的运算
每个函数有自己的变量空间,参数也只能在这个空间中有效,与其他函数没有关系
本地变量
函数的每次运行都产生独立的变量空间,这里的变量叫本地变量
定义在函数内部的变量是本地变量,参数是本地变量
运行到哪里,就在哪一个变量空间中
本地变量定义在块(大括号)内,可以是函数的块内,也可以是语句的块内,运行在块内就存在,运行至块外时消亡
块外面定义的变量在里边仍然有效,但里面定义了同名的变量就掩盖了外面的
本地变量不会被默认初始化,但是参数会
tips:
1.没有参数时,用void f(void),而不是void f()
2.调用函数时逗号和逗号运算符:f(a,b)是逗号;f((a,b))是逗号运算符
3.c语言不允许函数的嵌套定义(在函数里定义函数)(可以放函数的声明,不能放定义)
4.int main()也是一个函数,是返回int类型的main函数,有返回值,程序总是从main函数开始,最后到main函数结束(结束时有return语句)
八 数组
int number[100];
定义一个变量number,number是一个数组,大小是100(可以有100个int类型的变量)
number[cnt]=x 每一个x放在数组number中cnt的位置上(赋值) cnt是count缩写
定义数组
<类型>变量名称[元素数量];
double weight[20];
放在[]中的数字叫做下标或索引,有效范围从0到数组大小减一
记录0-10数字出现的次数
初始化:初始化数组中每一个变量,所以循环
-1是出口,最后输入-1直接结束
数组的集成初始化
直接用大括号给出数组中所有元素的初始值,不用给数组的大小
int a[]={2,4,6,7}
数组的大小:用sizeof表示出整个数组的字节大小
sizeof(a) sizeof(a[0])相除
数组的赋值
数组变量本身不能赋值(int b[]=a;是错误的)
要把数组的所有元素交给另一个数组,必须采用遍历
for(i=0;i<length;i++){
b[i]=a[i];
}
tips:让i从零到<数组的长度,这样最大的i正好是数组最大的有效下标
常见错误:1.循环结束条件是<=数组长度
2.离开循环后,继续用i的值做元素的下标(此时i的值已经是无效的下标范围)
数组作为函数参数时,必须用另一个参数传入数组的大小 : 在外面用单独变量i,调用函数时空着
(int key,int a[],int length) a[i]==key
数组作为函数的参数时
不能在[]中给出数组的大小,不能用sizeof计算数组的元素个数
数组例子:素数
算法优化:
1.去掉偶数后,从3到x-1,每次加二
2.不用到x-1,到sqrt(x)
3.判断是否能被已知且小于x的素数整除
int prime[number]={2}是用另一个数组的赋值,代表第0个元素为2,后面没有写就是0
prime[count++]=i 将i的值赋给数组中cnt的位置,cnt移到下一位置(自加一)
跟踪检测运行状态:1.调试2.在中间写入printf语句,可以用大括号,如
为了更好看一点,再用一个大括号,输出表头
构造n以内素数表
令x=2;
将2x、3x、4x直至ax<n标记为非素数
令x为下一个没有标记的数,重复二
1.开辟prime[n],初始化所有元素为1,prime[x]为1表示x是素数
2.令x=2
3.若x是素数,则对(i=2;x*i<n;i++)令prime[i*x]=0
4.令x++,若x<n,重复3
二维数组
int a[3][5];a是一个三行五列的矩阵
二维数组的遍历
a[i][j]是一个int,表示第i行第j列的单元
初始化
int a[][5]={
{0,1,2,3,4},
{2,3,4,5,6},
};
列数必须给出,每行一个{},逗号分隔,
九 取地址运算
运算符&:获得变量的地址,它的操作数必须是变量 scanf(“%d,&i”);
地址的大小是否与int相同取决于编译器
输出地址? int i;printf(“%p”,&i);
指针:指针变量就是记录地址的变量
scanf(“%d”,&i);
将取得的变量的地址传递给一个函数,通过这个地址在那个函数内访问这个变量
我们需要一个参数能保存别的变量的地址!
指针!就是保存地址的变量
int* p= &i;*p是一个指针,带*是标识,p是新变量,类型是int,它指向i这个变量,将i的地址交给指针(p保存i的地址)
作为参数的指针
void f(int *p);调用时给它变量的地址f(&i)
在函数里面可以通过这个指针访问外面的i
访问那个地址上的变量*
*用来访问指针的值所对应地址上的变量,可以做左值或右值(int k=*p *p=k+1)
如:在函数内部时,*p=26,实际是对i作用,这时候*p就代表函数外的i
ps:左值之所以叫左值,是因为赋值号左边的不是变量,而是值,是表达式计算的结果。*p=3
&与*就像逆运算
指针的使用
1.交换两个变量的值
void swap(int *pa,int*pb)
{
int t=*pa;
*pa=*pb;
*pb=t;
}
2.函数返回多个值,某些值就只能通过指针返回
或:函数返回状态(对错,结果通过指针返回
常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错
tip:指针一定要指向变量!!
数组与指针
函数参数表中的数组实际是指针, a[]中[]是地址,也是数组的运算符
以下四种函数原型等价
int sum(int *ar,int n);
int sum(int*,int);
int sum(int ar[],int n);
int sum(int[],int)
数组变量是特殊的指针,它本身表示地址
1.int a[10];int*p=a;(无需用&取地址)
2.数组的单元表示的是变量,需要用&取地址
3.a==&a[0]a的地址等于a[0]的
[]运算符可以对数组做,也可以对指针做,如:int *p=&min;
printf(“*p=%d\n”,*p);
printf(“p[0]=%d\n,p[0]”);将p看作数组,p[0]就是数组中第一个数,就是min本身
*可以对指针做,也能对数组做,*a=25 (第一个元素)
数组变量是const的指针,所以不能赋值,int a[]即int* const a,
const做变量的修饰即该变量不能被改变,对a就是取得地址不能被改变
转换:总能把一个非const值转换为const的,void f(const int* x)传入一个指针
const数组
const int a[]={1,2,3,4,5,};
这里表示数组的每个单元都是const int,所以必须通过初始化赋值
保护数组
1.因为数组传入函数时是地址,所以函数内部可以修改数组的值
2.为了保护数组,可以设置参数为const,int sum(const int a[],int length);
指针运算
给一个指针加一,并不是地址加一,而是加一个sizeof(对应类型的一个单位,即移到下一个位置)
这些运算符可以对指针做
1.给指针加减一个整数(+、+=、-、-=)
2.递增递减(++\--)
3.两个指针相减:得到的是两个指针地址间的长度除以sizeof,即两个地址间有多少个单位
p++:取出p所指的那个数据,随即把p移到下一个位置(常用于数组类的连续空间操作)
*的优先级没有++高
指针比较
<,<=,==,>=,!=都可以对指针做,比较它们在内存中的地址
ps:数组中单元的地址线性递增
0地址:不能随便碰,Null表示0地址
不论什么类型,指针的大小都一样,因为都是地址。
但指向不同类型的指针不能直接互相赋值,这是为了避免用错指针
但也不是不可以,指针类型转换:int* p=&i;void*q=(void*)p;(并没有改变p所指的变量类型,而是从q的角度看i,q也指向i,通过p看i,i是int,通过q看i,i是void*)
void*表示不知道指向什么的指针,计算时与char*相同
动态内存分配 int a[number]
a=(int*)malloc(number*sizeof(int));前面是类型转换,
最后还要还: free(a);(大括号外,return 0前)只能还申请空间的首地址
malloc
#include<Stdlib.h>
void* malloc(size_t size);(参数是size_t,返回类型是void*,需要类型转换为需要的类型)
向malloc申请的空间是以字节为单位的(乘sizeof)
没空间了?申请失败就返回0,或Null
十、字符串
char word[]={'h','e','l','l','\0'};
字符串:以0结尾的一串字符
1. 0和'\0'一样,但与'0'不一样
2.0标志字符串结束,但0不是字符串的一部分,计算字符串长度时不包含0
3.字符串以数组的形式存在,以数组或指针的形式访问,不能用运算符运算,可以遍历
字符串常量 char* s=“Hello,world”;
s指针指向字符串常量,这个常量只能读不能写
要修改字符串,用数组:chars s[]=“Hello,world”;(重新定义)
写成指针形式还是数组形式?
数组:这个字符串在这,作为本地变量自动回收
指针:不知道在哪,处理参数,动态分配空间
字符串变量
char *str=“Hello”;(str指针指向一个数组,数组里内容是Hello)
char word[]=“Hello”;
字符串赋值
char* t=“title”;
char* s;
s=t;
并没有产生新的字符串,只是让指针s指向了t所指的字符串,
字符串输入输出
定义:char string[8];string初始化为0
scanf(“%s”,string);
printf(“%s”,string);
scanf读入一个“单词”(到空格,回车,tab为止)
不安全,因为可能越界(超过字符串大小)
安全:在%和s之间的数字表示最多允许读入的字符数,这个数字应该比数组的大小小一,
%7s 最多读七个字符,下面的内容交给下一个scanf或其他什么读入的语句去读
空字符串:char buffer[100]="";
buffer[0]=='\0'
若char buffer[]=“”;
这个数组长度一
字符串数组
char**a a是一个指针,指向另一个指针,那个指针指向一个字符(串)
char a[][10] a[0]即char[10](10是后面括号里的)a[1] a[2]都是,数组中每一个元素都是一个char[10]
char *a[] a[0] a[1]…是指针,指向外面某处的…
putchar函数
int putchar(int c);向标准输出一个字符(字节(不是四个)
输入是int类型,返回类型也是int,表示写出去几个字符,EOF(值是-1)表示写失败
getchar函数
int getchar(void);从标准输入读入一个字符
字符串函数
前面需要加 #include<string.h>
strlen说明字符串的长度
size_t strlen(const char *s);返回s的字符串长度,不包括结尾的0
strcmp
int strcmp(const char *s1,const char *2);
比较两个字符串,返回:0(s1==s2)、1(s1>s2)、-1(s1<s2)
strcpy
char *strcpy(char *restrict dst,const char *restrict src);
把src的字符串拷贝到dst,返回dst
复制一个字符串
char *dst=(char*)malloc(strlen(src)+1);
scrcpy(dst,src);
strcat
char *strcat(char *restrict s1,const char *restrict s2);
把s2拷贝到s1的后面,从/0(dst[strlen(dst)])开始
strcpy和strcat都可能没有足够的空间,安全版本
char *strncpy(char *restrict dst,const char *restrict src,size_t n);
char *strncat(char *s1,const char *restrict s2,size_t n);
还有一个
int strncmp(const char *s1,const char *s2,size_t n);只判断一个字符串开头3个字母是不是abc,不比较后面的,size写上3
字符串搜索函数 strchr
char *strchr(const char *s,int c);从左寻找c第一次出现的位置(返回的是指针)
strchr换成strrchr就是从右边数
返回NULL表示没有找到
1.
char s[]=“hello”;
char *p=strchr(s,'l');
//此时输出p,为llo,即找到第一个l,从此开始当成一个数组
//第二个呢
p=strchr(p+1,'l');
//此时结果lo
2.想输出he?
char s[]=“hello”;
char *p=strchr(s,'l');
char c=*p;
*p = '\0';
//令第一个l为0,copy一份s就是'he',再通过定义的c还原回去(*p=c)
char *t=(char*)malloc(strlen(s)+1);
strcpy(t,s);
字符串中找字符串
char *strstr(const char *s1,const char *s2);
把strstr换成strcasestr表示忽略大小写
十一、枚举
常量符号化
枚举语句:enum枚举类型名字{名字0,…,名字n};
大括号里的名字就是常量符号,类型是int,值依次从0到n,如:enum colors{red,yellow,green};创建了三个常量,red的值是0,yellow的值是1,green是2
当需要一些可以排列起来的常量值时,定义枚举的意义是给这些常量值名字
作为变量或函数调用时enum color作为一个整体
enum color{red}
小套路:自动计数的枚举
enum color{red,yellow,green,numcolors};这样numcolors就是个数
枚举量:声明枚举量的时候可以指定值
enum color{red=1,yellow,green=5};yellow跟在后面,就是2
结构类型
struct point {
int x;
int y;
};p1,p2;
或者不要大括号后的p1p2,在后面加上语句
struct point p1,p2;
结构的初始化
struct date today={07,31,2014};
数组用[]运算符和下标访问成员 a[0]=10;结构用.运算符和名字访问成员 today.day
结构运算
要访问结构,直接用结构变量的名字
对整个结构,可以赋值,取地址,传递给函数参数
p1=(struct point){5,10}; 强制令5,10转换为struct类型,pl.x=5;pl.y=10;
p1=p2;即pl.x=p2.x;pl.y=p2.y
结构指针
和数组不同,结构变量的名字不是结构变量的地址,必须使用&
结构作为函数参数
int numberodays(struct date d)
整个结构作为参数的值传入函数,此时是在函数内新建一个结构变量,复制调用者结构的值
输入结构
在输入函数中,创建一个临时结构变量,把这个结构返回
指向结构的指针
p->month = 12;
结构数组
struct date dates[100];
struct date dates[]={{4,5,2005},{2,4,2005}};
结构中的结构(结构中的变量可以是结构)
struct dateAndTime{
struct date sdate;
struct time stime;
};
r.pt1.x rp->pt1.x
十二、全局变量
定义在函数外的变量
全局变量没有初始化会得到0值
静态本地变量
在本地变量前加上static修饰符,实际上是特殊的全局变量
特性:函数离开后,静态本地变量继续存在保持其值,以后进入函数保持上次离开的值
返回指针的函数
返回本地变量地址危险,全局变量地址安全,最好返回传入的指针
tips:尽量避免使用全局变量,不要用全局变量在函数间传递参数和结果
编译预处理指令:以#开头的指令
#define定义一个宏
#define<名字><值> 没有分号
名字必须是一个单词,值可以是各种东西
在c语言的编译器编译前,预处理程序将程序中的名字换成值
宏
1.一个宏的值中有其他宏的名字,会被替换
2.一个宏的值超过一行,最后一行末加\
3.空格或其他符号都会被当做值,但//注释不会
像函数的宏(带参数)
#define cube(x)((x)*(x)*(x))
原则:一切都要有括号(整个值,每一个参数)
可以带多个参数 #define min(a,b)((a)>(b)?(b):(a))
可以嵌套宏
在devc++中新建一个项目,会把项目中所有文件编译后链接起来
头文件:把函数原型放到一个头文件(以.h结尾)中,在需要调用该函数的源代码文件(.c结尾)中#include这个头文件
#include是编译预处理指令,把相应文件的全部文本内容插入
#include有两种形式指出要插入的文件,“”要求编译器在.c文件所在的目录寻找,没有就在编译器指定目录找。<>只在指定目录找
int i;是变量的定义
extern int i;是变量的声明
只有声明可以放在头文件中,定义不能。
同一个编译单元,同名结构不能重复声明
为了避免重复,引入标准头文件结构:
#ifndef _LIST_HEAD_
#define_LIST_HEAD_
最终结构:
#include "node.h"
代码内容……
#endif
十三 格式化输入输出
printf
%[flags][width][.prec][hlL]type
1.flag:"-"(左对齐),"+"(前面加加号或减号),"(space)"(正数留空),“0”(0填充)
2.width或prec:“number”(输出要占据的位数)如9,*:下一个参数是字符数,.number:小数点后的位数,.*下一个参数是小数点后的位数
3.修饰符
hh:单个字节,h:short,l:long,ll:long long,L:long double
4.类型type
scanf%[flag]type
[^.]读在符号前的所有字符,如[^,]读逗号前的字符
scanf("%*[^,],%[^,]")
文件输入输出
打开文件的标准代码
FILE* fp=fopen("file","r"); fopen是函数,r表示读,对file进行打开读操作
if(fp){
fscanf(fp,…);
fclose(fp);
}else{…
}
if的作用是判断文件是否打开
十四 可变数组
Array array_create(int int_size);创建数组
void array_free(Array *a);释放空间
int array_size(const Array *a);大小
int* array_at(Array *a,int index);访问某个单元
void array_inflate(Array *a,int more_size);长大
typedef struct{
int*array;
int size;
}Array;
->意思是所指的
自动增长
每次申请一块(block)
每次都重新申请有许多弊端,所以要有链表,直接连接起来
链表的函数
链表的搜索:遍历并输出。用if函数找
链表的删除:前一个指向后一个,中间的free了
如果第一个就是呢?if判断一下,head指向后一个
链表的清除(整个)
for (p=head;p;p=q){
q=p—>next;
free (p);
}
后:学习还未结束,to be continue…