文章目录
基础
- const是一个修饰符,加在int前。表示这个变量的值一旦被初始化则不能再被改变。const的变量一般为了强调全大写。
- 两个整数的计算结果也只能是整数,%f是浮点数的占位符。
- double的输入占位符必须为%lf,输出可以用%f。int的为%d
表达式 | 运算 | 表达式的值 | 含义 |
---|---|---|---|
a++ | 给a+1 | a原来的值 | 先输出a的值再进行自加 |
++a | 给a+1 | a+1以后的值 | 先自加再输出a的值 |
goto语句,用法形式
goto 标志;
标志:#程序某处设置标志,跳到标志所在的地方去,
//
在循环最内层要跳到最外层用,其他情况尽量不用。
运算符
- 逗号,也是运算符(3,5)结果是5
- 先#include <stdbool.h>才可以使用bool类型
- !age<20,!age会先结合,因为单目优先级!会高于双目优先级<;
- !>&&>||
- 对于&&,左边是false时就不做右边了。对于||,左边是true时就不做右边了
- 条件运算符自右向左结合,逗号表达式一般只在for循环条件中
级联的if-else if
if(exp1)
stl;
else if(exp2)
st2;
else
st3;
- 数组初次使用也需要初始化。(利用循环遍历数组,对数组每一个元素赋值)
函数
- c语言不允许函数的嵌套定义
- int main()函数中return 0是有必要的,一般也会表示程序正常运行。
- void类型的函数不能做返回值的赋值,即调用后赋值给另一个变量
- 可以仅将函数头放在main函数前面,称为函数的原型声明,声明时可以不写参数名,但要写参数类型。main函数后面为具体函数(函数头+函数体);
- 也可以将函数体整个放在main前,而不需要声明。但这样并不清楚美观。
- 参数的传递仅是值得传递
#include <stdio.h>
void sum(int begin,int end);//函数声明,也可以void sum(int,int)但对于人类来说这样不是很容易理解。
int main()
{
sum(1,10);
return 0
}
void sum(int begin,int end)//函数的定义
{
int i;
int sum=0;
for (i=begin;i<=end;i++){
sum+=i;
}
printf("%d到%d的和是%d\n",begin,end,sum);
}
- 若实参(值)与形参(参数)类型不匹配,c编译器会悄悄进行类型装换,这样其实并不好。但java等语言对这方面却是很严格的。
- c语言在调用函数时,只能传值给函数,而不是这个变量,因此做简单交换函数,并不能真正的交换,除非在函数中引入指针。
/*错误写法,并不能真正做到交换
int swap(int x,int y)
{
int t;
t=x;
x=y;
y=c;
printf("%d,%d",x,y)
}
*/
int swap(int *x,int *y)//正确写法
{
int t;
t=*x;
*x=*y;
*y=c;
printf("%d,%d",*x,*y)
}
本地变量
- 本地变量定义在块内(即{ }内)
- 函数块内,语句块内(for语句等),甚至随便拉的一个大括号内
- 而它的作用域与生存期都在定义的这个块内,即程序未进入这个块或已经离开这个块后,这个变量就消失了。
- 但在块外定义的变量在块内仍然有效。
- 块里定义了和外面同名的变量则掩盖了外面的,java不同
- 不能在一个块内定义同名的变量
- 本地变量不会被默认初始化,而参数在进入函数的时候被初始化了
参数
- void f(void);表示不接收任何参数
- void f();表示函数参数表未知,并不表示没有参数,这种写法并不好,但确定没有任何参数时,用第一种方式。
二维数组
- 列数必须给出,行数编译器可以帮你数
- 初始化时,每行一个{ },逗号分隔,最后的逗号可以存在,如果省略表示补0.
- 数组读入
scanf("%d",&board[i][j])
int a[] [5]={//也可以不带大括号当一维数组初始化,但这种方式使人类更便于阅读
{0,1,2,3,4},
{2,3,4,5,6},
};
board[i][size-i-1]
反对角线上的元素- 集成初始化定位,指定元素初始化:int a[10]={[0]=2,[2]=3,6,},未定位的部分接在前面的位置后面,其余位置补0,适合初始数据稀疏的数组
- sizeof()给出整个数组所占据的内容的大小,单位是字节;则
sizeof(a)/sizeof(a[0])
为数组元素数量。 - 通常遍历数组都是i从0到<数组长度
- 数组作为函数参数时,往往必须再用另一个参数来传入数组的大小,此时不能用sizeof计算元素个数
int serach(int key,int a[ ],int length)
{
//具体内容
}
- 函数有多个出口、一个变量承担了多个功能都不是好的代码形式
搜索的一种结构
//使用两个数组的方式,输入一定金额,查找并输出对应单位
//但这种方式并不好,割裂的两个数组对于catch是不友好的
int amount[]={1,5,10,25,50};
char *name[]={"penny","nickel","dime","quarter","half-dollar"};//指针形式的数组
int serach(int key,int a[],int len)
{
int ret=-1;
for(int i=0;i<len;i++)
{
if(key==a[i])
{
ret=i;
break;
}
}
return ret;
}
int main()
{
int k=10;
int r=serach(10,amount,sizeof(amount)/sizeof(amount[0]));
if(r>-1)
{
printf("%s\n",name[r]);
}
return 0;
}
//输入一定金额,查找并输出对应单位,使用结构的方式
int amount[]={1,5,10,25,50};
char *name[]={"penny","nickel","dime","quarter","half-dollar"};//指针形式的数组
struct{
int amount;
char *name;
}coins[]={
{1,"penny"},
{5,"nickel"},
{10,"dime"},
{25,"quarter"},
{50,"half-dollar"}
};
int serach(int key,int a[],int len)
{
int ret=-1;
for(int i=0;i<len;i++)
{
if(key==a[i])
{
ret=i;
break;
}
}
return ret;
}
int main()
{
int k=10;
for(int i=0;i<sizeof(coins)/sizeof(coins[0]);i++)
{
if(k==coins[i].amount){
printf("%s\n",coins[i].name);
break;
}
}
return 0;
}
数组运算
二分搜索
- 效率为log2N
//一定要是有序的,才能进行二分查找
//本段代码演示从小到大排序的情况
int serach(int key,int a[],int len)
{
int left=0;
int right=len-1;
int ret=-1;//表示没找到
while (right>left)//当不满足时,表示全都找了
{
int mid=(left+right)/2;
if(a[mid]==k)
{
ret=mid;//找到的就在ret下标处
break;
}
else if(a[mid]>k)
{
right=mid-1;
}
else
{
left=mid+1;
}
}
return ret;
}
选择排序
- 每次选出一个最大的,将最大的与最后一个做交换,从而放到应该的位置
//找出最大的值的下标
int max(int a[],int len)
{
int maxid=0;
for(int i=1;i<len;i++)
{
if(a[i]>a[maxid])
{
maxid=i;
}
}
return maxid;//返回最大值下标
}
int main()
{
int a[]={2,45,6,12,87,34,90,24,23,11,65};
int len=sizeof(a)/sizeof(a[0]);
//做交换,将最大的与最后一位交换
for(int i=len-1; i>0;i--)
{
int maxid=max(a,i+1);
//swap a[maxid],a[len-1]
int t=a[maxid];
a[maxid]=a[i];
a[i]=t;
}
//遍历输出数组
for (int i=0;i<len;i++)
{
printf("%d ",a[i]);
}
return 0;
}
指针
-
指针中的值放的都是别的变量的地址
-
&是取地址运算符,作用是获得变量的地址
,它的操作数必须是变量,不能对没有地址的取地址 -
int i=0;printf("%p\n",&i)
得到变量i的地址,以对应正确格式输出 -
地址的大小是否与int相同取决于编译器是32位架构还是64位
-
c中内存是以堆栈stack存储,自顶向下形式,先定义的在高位。
-
相邻地址间差距为4,取数组a地址时,a=&a=a[0],这几种形式取出的都是a[0]的地址
-
将变量的地址传给函数,能否通过地址访问变量?–可以,指针
-
用参数表达别的参数的地址,如何表达能保存地址的变量?----指针
-
*单目运算符,用来访问指针的值所表示的在那个地址上的变量,即用来取出这个指针所指地址处的值
int i=5;
int*p=&i;//*表示p是指针(本质还是变量),指向的是一个int,将i的地址交给指针p
printf("*p=%d\n",*p);//输出*p=5
//*p是个int
//p指向i的意思是,即p指向i变量的地址,*P的值就是i地址所对应的值即i的值
int *p,q;//p是指针,而q只是普通变量,
int *p,*q;//p,q都是指针
void f(int*p);//函数的参数是指针
int i=0;
f(&i);//调用时的参数应该是取得的某个变量的地址
//在函数里可以通过这个指针访问外面的这个i
int i=6;
void g(int k)//函数里拿到的仅是值
g(i);//仅得到值6,与外面的变量i没有关系
int j=*p;//p所表示的地址上的变量值赋给j
int i=6;
f(&i);
void f(int *p)//也是值的传递,只是这种指针方式传入了地址
{
printf("*p=%d\n",k)//此步输出*p=6
*p=28//若此时再输出i的值,发现i的值也变为28
//此时函数内的*p就代表了i,改*p即改了i
//若是没有*将地址传入则仅值传递,发生的仅此处值改变,而外面的变量值仍不变
}
int i=2;
int*p=&i;
printf("i的地址&i=%p\n",&i);//输出结果0060FF0C
printf("p的值=%p\n",p);//输出结果0060FF0C
printf("p的地址&p=%p\n",&p);//输出结果0060FF08,这个地址是指针的地址
*p=28;
printf("更改p后i的值i=%d",i);//值为28,此时修改指针的值也就修改了i的值
return 0;
数组变量与指针
- void minmax(int a[ ],int len,int *min,int *max)
- 传入函数的数组不仅是数组的值,而是其数组本身,也是就是个指针
函数参数表中的数组实际上是指针
- 故其上函数的参数表中数组可以直接写为void minmax(int *a,int len,int *min,int *max)
- 故,
在函数里用sizeof(a),计算的不是数组a的长度
,而是指针的长度即sizeof(int*) - 数组变量是特殊的指针
数组变量本身表达地址,所以无需&
即可取其地址- int a[10];int*p=a;//无需&取地址
- 但是
数组的单元表达的是变量,需要&取地址
- a的地址就是a[0]的地址,a==&a[0]
- [ ]运算符可以对数组做,也可以对指针做
- p[0]<==>a[0]//指针p[0]相当于a[0],
- int min=2;int *p=&min;此时输出p[0]等于2
- 对于指针变量可以将其当做是只有一个元素的数组
- *运算符可以对指针做,也可以对数组做,此处的*a==a[0]
数组变量是const的指针,所以不能被赋值
//a是一个数组,且在数组变量与指针这一节中都这么表示
int b[]=a;//错误,不可以这样,此处int b[]即int*const b
int *q=a;//可以这样写
字符类型
- %c来做字符的输入输出,且单引号表示字符
- 两个字符的减,得到它们在表中的距离
- ascall表中大写在前,数字小;小写在后,数字大
- 字符加一个数字得到这个字符的ascll码加上数字后所表示的字符
- 因为’a’-‘A’可以得到两段之间的距离,于是’A‘+(‘a’-‘A’)可以把一个大写字母变成小写字母,而’a’+(‘a’-‘A’)可以把小写字母变成大写字母
printf("小写转大写%c\n",'a'-('a'-'A'));
printf("大写转小写%c\n",'A'+('a'-'A'));
逃逸字符
- 用来表达无法打印出来的控制字符或特殊字符,由一个反斜杠“\”开头后跟另一个字符
\b退格,\t到下一个表格位,每一行固定的一个位置,\r回车
字符串
- char word[ ]={‘H’,‘e’,‘l’,‘l’,‘o’,’!’,’\0’};//0编译器会帮忙生成
- 以0为结尾的一串字符
- 0或’\0‘是一样的,但是和’0‘不同
- 0标志字符串的结束,但它不是字符串的一部分
- 字符串以数组的形式存在,以数组或指针的形式访问,指针形式较多
- string.h里有很多处理字符串的函数
- 双引号形式
三种定义形式
- char *str=“Hello”;一个叫str的指针指向了一个字符数组,这个里面的内容是Hello
- char word[ ]=“Hello”;这里有一个字符数组,它里面的内容是Hello
- char line[10]=“Hello”;有一个叫line的字符数组,这个数组有10个字节大,里面放了Hello,加上末尾的0,它只占了6个字节的位置
- c语言中两个相邻的字符串会自动将其连接
- 输出的双引号中若一行语句可能要写的太长可以在本行加一个反斜杠\,然后在下一行从头继续写
- c语言的字符串以字符数组的形态存在,通过数组的方式可以遍历字符串
- 字符串字面量可以用来初始化字符数组
- char *s=“Hello world!”;
- s是一个指针,初始化为指向一个字符串常量
- 由于这个常量所在的地方,所以实际s是const char *s,因此无法修改只能读
- 若要修改字符串,应该用数组char s[]=“Hello,world!”;
- 当程序中有两处相同的字符串,分别定义给两个指针。则它们会指向同一个地方
什么时候定义为数组形式,什么时候定义为指针形式
- 数组:这个字符串就在这里
- 作为本地变量空间自动被回收
- 指针:这个字符串不知道在哪
不需要修改字符串的情况下,指针定义的无法修改
- 处理参数时,作为参数时
- 动态分配空间
- 即
要构造一个字符串用数组,要处理一个字符串用指针
- 字符串可以表达为char的形式,但char不一定为字符串。本意为指向字符的指针,可能指向的是字符的数组(像int*)。只有它所指的字符数组有结尾的0,才能说它所指的是字符串。
字符串的计算
字符串的输入输出
char string[8];
scanf("%s",string);//不安全,可能超过8
scanf("%7s",string);//安全,告诉函数最多只能读7个字符,超过7个就不要了
//这样就不是根据空格来划分单词而是根据个数来。若定义了两个字符串,两次读入,则多余的给下一个scanf阅读
printf("%s",string);
- scanf读入一个单词(到空格、tab或回车为止)
- 定义char *string;指针变量没有初始化没有让这个指针指向一个实际有效的地址,不能直接使用比如读一个输入放入,可能会随意指向一个地方,可能会出错
- char buffer[100]="";这是空的字符串,且buffer[0]==’\0’
- char buffer[]="";这个数组的长度只有1,即buffer[0]==’\0’且只有buffer[0]有效,再放不下其他字符串
字符串函数
- string,h文件中
- size_t strlen(const char*s);返回s的字符串长度(不包括结尾的0)
- int strcmp(const char*s1,const char *s2);
比较
- 比较两个字符串
返回0(相等)、1(s1>s2)、-1(s1<s2).
- 不相等的情况下会返回其中不相等部分的差值
- 若仅用==来比,比的结果永远是False,包括地址要比较是否相同
- int strncmp(const char*s1,const char *s2,size_t n);只比较前n个
- 比较两个字符串
- char strcpy(charrestrict dst,const char *restrict src);
拷贝
存在安全问题可能越界- 将src的字符拷贝到dst
- restrict表明src和dst不重叠
- 返回dst,为了能链起代码来
- char strncpy(charrestrict dst,const char *restrict src,size_t n);安全的,有最多可以再拷贝n个字符的限制
- char strcat(charrestrict s1,const char *restrict s2);
链接
,存在安全问题可能越界- 把s2拷贝到s1后面,接成一个长的字符串
- 返回s1
- s1必须具有足够的空间
- char strcat(charrestrict s1,const char *restrict s2,size_t n);安全的,有个最多可以再链接n个字符的限制
- char *strchr(const char *s,int c);从左边找c第一次出现的位置,返回指针。字符串中找字符,返回NULL没找到
- char *strrchr(const char *s,int c);从右边找
- char * strstr(const char *s1,const char *s2)
字符串中找字符串
- char * strcasestr(const char *s1,const char *s2)
字符串中找字符串过程中忽略大小写
- getchar()输入字符
- 相当于一次性把键盘输入的一行都放在缓冲区,
- 直到输入回车,然后逐个读取,
- 前面没读走的仍在缓存队列中,被下一个函数读取。
- 每调用一次,仅读取一个字符
- 若按回车再输入a再按回车,则分别做两次输出10,a。(回车的ASCALL是10)
-
- char * strcasestr(const char *s1,const char *s2)