C语言笔记.docx
《C语言笔记.docx》由会员分享,可在线阅读,更多相关《C语言笔记.docx(44页珍藏版)》请在冰豆网上搜索。
C语言笔记
C语言的基本概念
编写一个简单的C程序,后缀名保存为c(本次文件名为a.c)
gcc:
对c程序进行编译和连接;gcca.c
./a.out:
运行程序,输出程序的结果;其中a是c程序的文件名
说明:
其实并不是简单的在a.c文本中输入几行代码就能运行的,其内部实现步骤如下:
1:
gcc-Ea.c在c程序中有个#include的头文件,其中#开始的命令都叫做预处理命令,这里是把文件通过预处理器进行处理
2:
gcc-ca.c对文件进行编译成机器认识的二进制的格式的目标代码,然后就出现一个a.o文件
3:
gcca.o把目标代码和其他的附加代码整合在一起,这样就有一个可以执行的程序a.out
4:
执行a.out就能够得到程序的结果
注意:
上面的例子中只使用了gcca.c对c程序进行编译和连接;然后./a.out来输出,并没有使用1,2,3步骤;事实上gcca.c这一表达式已经完成编译和连接两个步骤,也就是说执行完gcca.c之后,就已经生成out文件了,下面来了解gcc的命令参数就知道了;
gcc命令的参数讲解
-c:
只编译,不连接成为可执行的文件,编译器只是由输入的.c等源代码文件生成.o为后缀的目标文件,通常用于编译不包含主程序的子程序文件
-ooutput_filename:
确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。
如果不给出这个选项,gcc就给出预设的可执行文件a.out,所以上边例子的gcca.c执行后就直接生成了out后缀文件,所以就省略了1-2-3步骤(gcc-ob.oa.c//这里是把a.c文件生成b.o文件,如果不为b文件指定o为后缀,那就只生成b文件)
-g:
产生符号调试工具(GNU的gdb)所必要的符号资讯,要想对源代码进行调试,我们就必须加入这个选项,这个参数一样可以生成out的后缀文件
-O:
对程序进行优化编译、连接,采用这个选项,整个源代码会在编译、连接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是,编译、连接的速度就相应地要慢一些;这个参数一样可以生成out的后缀文件
-O2:
比-O更好的优化编译、连接,当然整个编译、连接过程会更慢;这个参数一样可以生成out的后缀文件
-std:
选择编译程序所使用的c语言的标准,可以使用-std=c89或者-std=c99;这个参数一样可以生成out的后缀文件
注释
/**/:
多行注释,一般用来对描述参数,程序信息
//:
单行注释,一般用来描述语法的讲解,每行语法对应一行单行注释
例如:
/*
文件名:
×××
作者:
×××
年份:
×××
版本信息:
×××
*/
#include//头文件
___________________________________________________________________________________________________________________________________________________________
基本类型
char:
字符类型
charc1="a";//输出a
charc2="97";//97是ASCII转码代表a
charc3="3";//输出3
charc4="/"//printf("%d",c4);//输出这个/号的ASCII转码
关键字sizeof:
在printf中用来测量变量和类型在内存中所占的字节数
sizeof(c);//变量
sizeof(char);//类型
sizeof(表达式);//他只关心括号里的类型占的字节大小,不会对表达式进行运算:
inta
shorto=9;
a=sizeof(o=10);//只输出o类型占内存的大小,不会输出o=10;所以a等于o占内存字节的大小,而不是10;
输出的是字节数值
无符号整型:
就是没有负号的整型数,其永远只有正数
___________________________________________________________________________________________________________________________________________________________
二进制
负十进制转换为二进制
按位取反--加1
负二进制转换为十进制
减1--按位取反
___________________________________________________________________________________________________________________________________________________________
判断运算符
!
=//表示不等于
逻辑运算符
&&:
与
||:
或
!
:
非
短路特性:
inti=0,j=0;
if(++i||++j);//当运算++i得出结果时,就不会再去运算++j,所以这里++j无效,j还是等于0
if(--i&&++j);//当运算--i得出结果时,就不会再去运算++j,所以这里++j无效,j还是等于0
printf("%d%d",i,j);//经过两次运算,i最后还是0,j还是0
___________________________________________________________________________________________________________________________________________________________
位运算
~:
按位取反;如果是整数,运算后就是负数,同样负数运算后得到的是正数;~3=-4;~4=-5;~(n+1)
&:
按位与;两个二进制数进行与运算时,如果两个数都为真,其结果为真,如果两位数中有一位为假,其结果为假
|:
按位或:
两个二进制数进行与运算时,只要其中一个数为正,其对应的位就为真
^:
按位异或;一真一假为真,另外如果两个都是真或都是假,其结果都为假
<<:
左移
>>:
右移
左移一位相当于乘以2
右移一位相当于除以2
逗号运算符
在C语言中,多个表达式可以用逗号分开,其中用逗号分开的表达式的值分别结算,但整个表达式的值是最后一个表达式的值
假设b=2,c=7,d=5,
a1=(++b,c--,d+3);//有三个表达式,用逗号分开,所以最终的值应该是最后一个表达式的值,也就是d+3,为8,所以a1=8
a2=++b,c--,d+3;//这时的三个表达式为a2=++b、c--、d+3,(这是因为赋值运算符比逗号运算符优先级高)所以最终表达式的值虽然也为8,但a2=3。
注意:
上面两个算法没有连续关系,所以第一次的++b的值不能作为第二次的++b值继续运算,因此第二次的++b结果是3
还有一种情况:
x=3,y=5;
a=(x+3,y++,x++);//x+3的结果没有指定赋值,会被丢掉,这里的a=3;x=4;y=5
a=(x++,x+3,x+7);//x++的结果保存下来,x+3的结果丢掉,a=11;x=4
___________________________________________________________________________________________________________________________________________________________
缓冲区
scanf:
#include
main()
{
intdata1,data2;
printf("请输入两个数字\n");
scanf("%d",&data1);
scanf("%d",&data2);
printf("%d%d",data1,data2);
}
说明:
如果输入13得到的结果是正确的,但是如果输入的1r得到的是1和0,如果输入的是r1那么两个输出都是0
因为,只有当第一个数据读走时,才会清除这个数据,当第一个是0时,0就不会被清除,导致第二个还是0
这里的0是指错误码。
也是变量的初始值
解决方法:
scanf("%*[^\n]");//*忽略读到的内容,[^\n]任何非\n的字符
将\n之前的所有字符读走到缓冲区,这样你输入的路七八糟的字符都会被读入到缓冲区
scanf("%*c");//从缓冲区读取一个字符,这样就把乱码读走了,不会影响第二个数据的正常输入
实例:
#include
main()
{
intdata1=0,data2=0;
printf("请输入两个数字\n");
if(scanf("%d",&data1)==0)//如果=0.就执行上面的解决方法,就能把字符读走了
{
scanf("%*[^\n]");//把\n之前的所有字符读走,包含\n,之后就不能通过\n来完成输入,每输入一个数字都要按回车键了
scanf("%*c");//读取所有字符忽略掉
}
if(scanf("%d",&data2)==0)
{
scanf("%*[^\n]");
scanf("%*c");
}
printf("%d%d",data1,data2);
}
人工刷新输出:
fflush(stdin)刷新标准输入缓冲区,把输入缓冲区里的东西丢弃[非标准]
fflush(stdout)刷新标准输出缓冲区,把输出缓冲区里的东西打印到标准输出设备上
printf("......");后面加fflush(stdout);可提高打印效率
___________________________________________________________________________________________________________________________________________________________
数组
数组初始化
inta[5]={0};//这样就全部都初始化了
inta[30]={[10]=4,[9]=8,[23]=67};//这样就对指定的元素初始化,并且没有初始化的都默认为0,这样也不存在顺序问题
sizeof:
测试数组的大小
sizeof(数组字节)=sizeof(数组元素字节)*元素的个数如:
inta[10];sizeof(a)=sizeof(a[0])*10
printf("%d",sizeof(a[0]))//求数组元素的字节
printf("%d",sizeof(a))//求数组的字节
如:
inta[10]//int占4个字节,a就是40字节,在除以每个元素的字节a[0]=4,就是数组的长度
二维数组
inta[3][4]={{0}};//对所有的元素进行初始化为0
___________________________________________________________________________________________________________________________________________________________
函数
#include
#include
boolprime(intdata)
{
inti=2;
for(;i{
if(data%i==0)
returnfalse;//假
}
returntrue;//真
}
main()
{
intd=0;
printf("输入一个数");
scanf("%d",&d);
printf("%s\n",prime(d)?
"素数":
"合数");//为真选择素数,为假选择合数,输出是字符串
}
exit:
函数名:
exit()
所在头文件:
#include
功能:
关闭所有文件,终止正在执行的程序。
exit
(1)表示异常退出.这个1是返回给操作系统的不过在DOS好像不需要这个返回值
exit(x)(x不为0)都表示异常退出
exit(0)表示正常退出
用法:
voidexit(intstatus);
exit()和return的区别:
按照ANSIC,在最初调用的main()中使用return和exit()的效果相同。
但要注意这里所说的是“最初调用”。
如果main()在一个递归程序中,exit()仍然会终止程序;但return将控制权移交给递归的前一级,直到最初的那一级,此时return才会终止程序。
return和exit()的另一个区别在于,即使在除main()之外的函数中调用exit(),它也将终止程序。
#include
#include
#include
intmain(void)
{
charstatus;
printf("Entereither1or2\n");
status=getch();//获取一个字符
exit(status-'0');//status减去字符0的ASCII码
printf("%d\n",status);//字符以%d形式输出
return0;
}
___________________________________________________________________________________________________________________________________________________________
变量的类别
局部变量:
在函数体内定义的变量
生命周期:
从定义这个局部变量的地方开始到函数的结束
作用范围:
在定义这个局部变量的函数内
static:
静态局部变量,其数值就是上一次函数调用结束之后的数值,还可以修饰函数:
staticinta(inta);
生命周期:
整个程序
作用范围:
和普通局部变量一样
全局变量:
定义在整个程序中的变量称为全局变量
生命周期:
整个程序的生命周期之内
作用范围:
整个程序的范围内都可以访问
全局变量在定义之后自动初始化为0
块变量:
定义在程序块里面的变量叫做块变量
程序块:
使用{}括起来的一组语句,比如if语句里的或者是循环语句等使用到{}的地方
生命周期:
定义变量的地方开始,程序块结束的地方消失
作用范围:
程序块内
rand();:
随机生成一个数
#include
#include
#include
main()
{
intdata=0;
srand(time(0));//随机数发生器的初始化函数,为了防止随机数每次重复常常使用系统时间来初始化;time(0)取得当前时间(秒的总和)
data=rand()%100;//初始化随机数,取100内的数赋值给data
while(t==time(0));//延迟让程序等待这一秒过去
printf("%d\n",data);
}
time(0)%100//当前秒总和
intt;
t=((time(0)%100)+5);//当前时间+5赋值给t
printf("%d\n",t);//打印t的值
while(t>(time(0)%100));//延迟5秒。
当表达式为真,执行循环,为假退出循环,刚好5秒
register:
寄存器,registerinta;
告诉编译器这个变量会被频繁的使用,请保存到寄存器中
不能对寄存器变量取地址&
有些系统不会把register修饰的变量放到寄存器中,由编译器决定
const:
只读变量修饰符,变量的数值是不能被改变的
volatile:
一个类型修饰符(typespecifier)。
它是被设计用来修饰被不同线程访问和修改的变量。
如果没有volatile,基本上会导致这样的结果:
要么无法编写多线程程序,要么编译器失去大量优化的机会;确保本条指令不会因编译器的优化而省略,且要求每次直接读值
XBYTE[2]=0x55;
XBYTE[2]=0x56;
XBYTE[2]=0x57;
XBYTE[2]=0x58;
对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,但是编译器就不能像对待纯粹的程序那样对上述四条语句进行优化,只认为XBYTE[2]=0x58(即忽略前三条语句,只产生一条机器代码)。
如果键入volatile,则编译器会逐一的进行编译并产生相应的机器代码(四条).
定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
下面是volatile变量的几个例子:
1).并行设备的硬件寄存器(如:
状态寄存器)
2).一个中断服务子程序中会访问到的非自动变量
3).多线程应用中被几个任务共享的变量
___________________________________________________________________________________________________________________________________________________________
指针
int*q=NULL;//指针初始化
注意:
不要返回一个局部变量的指针
chara[10];a="add";//这种赋值是错的
chara[10];a[0]='b';//这种是对的,意思为第一个赋值一个字符b
___________________________________________________________________________________________________________________________________________________________
程序段:
就是一段程序(可以是一个子过程SUB,一个函数FUNCTION(用面向对象的观点或称为方法))
程序都是从上到下施行的,那应该什么时候用到程序段
数据段:
在采用段式内存管理的架构中,数据段(datasegment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。
数据段属于静态内存分配。
bss段:
通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域
___________________________________________________________________________________________________________________________________________________________
堆和栈
堆栈二者特性不同,各有适用场合。
首先,最重要的一点,对象生存期不同。
栈上的空间,是自动回收的,虽然省事,但如果不想让它自动回收,就要使用堆来创建对象;在一个函数内部创建一个对象,然后把它的地址传给函数外层用,就不能在栈上创建这个对象,因为当函数一结束,此对象就被销毁了,外面访问它会出错。
而堆的话由于是完全手工创建手工回收,在碰到delete之前这个对象是不会被销毁的,就可以随意传递。
堆上申请空间可以很大,但是栈的空间却很有限,根据操作系统不同而不同,一般只有1~4MB的大小,如果在栈上申请过大的空间就会出错。
最后,栈上申请空间的速度比堆上快得多,所以如果是函数内部临时使用的小对象,一般用栈来分配。
栈:
它是一种具有后进先出性质的数据结构,也就是说后存放的先取,先存放的后取
堆:
堆的存取是随意,如同我们在图书馆的书架上取书
char*p1;//全局未初始化区
main()
{
intb;//栈
chars[]="abc";//栈
char*p2;//栈
char*p3="123456";//123456\0在常量区,p3在栈上。
p1=(char*)malloc(10);堆
p2=(char*)malloc(20);堆
}
栈是系统自动分配空间的,栈上的数据的生存周期只是在函数的运行过程中,运行后就释放掉,不可以再访问。
堆则是程序员根据需要自己申请的空间,例如malloc(10);开辟十个字节的空间。
而堆上的数据只要程序员不释放空间,就一直可以访问到,不过缺点是一旦忘记释放会造成内存泄露。
堆内存申请:
new:
申请堆内存
malloc:
申请堆内存:
a=(int*)malloc(100*sizeof(int));
delete[]:
释放堆内存空间,指向堆内存首元素的指针;如果申请的是一个堆内存变量,则delete后的[]可以省略;如果申请的是一个堆内存数组,则该[]不能省略
#include"iostream.h"
intmain()
{
intsize;
floatsum=0;
int*heapArray;
cout<<"请输入元素个数:
";
cin>>size;
heapArray=newint[size];//申请堆内存
cout<<"请输入各元素:
"<for(inti=0;i{
cin>>heapArray[i];
sum=sum+heapArray[i];
}
cout<<"这些数的平均值为"<delete[]heapArray;//释放堆内存
return0;
}
注意:
这里使用到了两个输入输出关键字:
cout,cin
输出输入
#include"iostream.h"//使用头文件
cout<<"result="<<"result"<cin>>size;
___________________________________________________________________________________________________________________________________________________________
读写一个字符串
puts:
输出一个字符串,可以指定一个文件指针,把字符串输出到文件中,也可以输出到显示设备上
fputs
#include
main()
{
chara[]="1a3456";/*字符数组也可以定义数字,但输出的是ascll,并非数字*/
printf("%d\n",++a[0]);/*输出的是字符1的ASCII码,1的ASCII码为49,++a[0]后等于50*/
printf("