第一阶段 C基础.docx
《第一阶段 C基础.docx》由会员分享,可在线阅读,更多相关《第一阶段 C基础.docx(20页珍藏版)》请在冰豆网上搜索。
![第一阶段 C基础.docx](https://file1.bdocx.com/fileroot1/2023-1/26/6ae96318-5a69-49be-9ff1-f315a01c1b0a/6ae96318-5a69-49be-9ff1-f315a01c1b0a1.gif)
第一阶段C基础
第一天入学测试
1.“++”优先级大于“-”(负号)
eg:
inti=3;
printf("%d\n",-i++);
分析:
优先级++大于-,所以先是右结合,即i++
2.在C语言中,是以补码的方式来存储数据的。
原码反码补码
正数:
本身 按位取反 本身
负数:
本身按位取反反码+1
3.共用体
在不同的时间段使用不同的成员变量,即:
同一时间只能使用一个成员变量
4.指针数组,int*p[5];//arrayofpointers,即用于存储指针的数组,也就是数组元素都是指针
数组指针,int(*p)[5];//apointertoanarray,即指向数组的指针,也称为行指针
区别:
指针数组本质是数组,数组里边的元素是指针
int*a[4],指针数组
表示:
数组a中的元素都为int型指针
元素表示:
*a[i]*(a[i])是一样的,因为[]优先级高于*
int(*a)[4],数组指针
表示:
指向数组的指针,或者指向某一行的指针(行指针)
5.有关const的使用说明
const所放的位置决定内容不可变还是地址不可变。
eg:
inta=8;
constint*p=&a;
intconst*p=&a;//以上两句,const修饰的是*p这个整体,即:
对*p赋值出错
int*constp=&a;//这句修饰的是p这个指针变量,即:
对p赋值出错
6.在Ubuntu下打开键盘
终端:
ibus-setup-->yes-->inputmethod-->chinses-pinyin-->ok
汉字输入切换快捷键:
ctrl+空格
7.vi编辑器的使用
(1)vi文件名.c打开文件或者新建文件
使用vi打开文件后
分为3种模式:
命令模式命令行模式(底行模式)输入模式(插入模式)
(2)命令行模式下:
保存w退出q
q!
不保存退出
w+文件名将当前文件内容存放到新文件里面
vsp文件名同一个终端打开另一个文件
sp文件名
复制多行:
首行行号,尾行行号y
跳到836行:
836
取消高亮:
nohl
替换操作:
:
%s/将被替换字符/替换字符/g
:
首行号,尾行号s/将被替换字符/替换字符/g
注意:
不加/g表示替换每行收索到的第一个字符串
命令模式下:
shift+zz保存退出
复制1行:
将光标放在这一行任意位置yy
粘贴:
p
复制n行:
将光标放在首行,输入nyy
跳到首行gg
跳到尾行G
撤销u
在终端复制一个文件:
cp源文件目标文件
注意:
将以上情况所有的y换成d就是剪切
8.在vi中使所写程序对齐
第一步:
光标在首行,shift+v,跳到尾行shift+g完成全选
第二步:
按下“=”键
9.gcc的使用:
(1)-E选项(预处理阶段)
gcc-E-oconst.iconst.c//说明:
-E选项是编译器在预处理结束就结束,不进行接下来的编译处理
-o选项是指定输出文件的名字为const.i此文件是经过预处理之后的文件,可以vi查看该文件经预处理之后的结构
(2)-S选项(编译阶段)
gcc-S-oconst.sconst.i//说明:
-S选项是在使编译器进行完编译就结束,编译将预处理得到的文件转换成汇编文件
(3)-c选项(汇编阶段)
gcc-cconst.s-oconst.o//说明:
-c选项是把编译阶段生成的.s文件生成目标文件(二进制文件)
(4)连接阶段
gccconst.o-oconst//说明:
最后阶段是将目标文件连接得到可执行文件
10.关于函数库
函数库包括静态库和动态库两种;
静态库:
是代码在编译时就已经连接到应用程序中;
动态库(共享库):
是在程序运行时被加载,动态库必须存在应用程序所要运行的系统中。
第二天计算机基础
1.二进制八进制
eg:
1010转换成八进制
12
001,01012
2.8421码
3216841
补充1:
二进制小数和十进制小数的表示
十进制小数:
12.3456 小数部分:
3/10+4/100+5/1000+6/10000......
二进制小数:
11.011111 小数部分:
1/2+1/4+1/8+1/16......
二者表示方法不一样,所以小数在计算存储的时候是存在误差的,这些误差是会累积的,在涉及大量浮点数计算的时候,可能会出错
3.man命令
manascii//查看ascii表
man3库函数名//查找库函数
4.define不是关键字
sizeof是关键字,c中32个关键字
c语言中4中存储类型:
auto,extern,static,register
5.字符串常量
char*p="hello";
*(p+1)='w';//出错
charp[]="hello";
*(p+1)='w';//正确
6.++运算:
++i作为前缀:
i值先自增,在参与运算;
i++作为后缀:
先取出i值参与运算,待整个表达式运行完成之后,i自增
eg:
intk=9;
k=k+++++k;
分析:
由于++的优先级高于+,所以先算++k,即:
k=9+1=10
+是由左至右结合,所以,变成k=10+10
计算结果k=20;
最后整个表达式结束k再++;
所以,最终结果是21
7.intx=100;
if(x=0)//表达式值为假
8.ctrl+shift+n//在当前终端下,打开另一个终端,并且新终端的路径和当前终端路径一样
9.配置共享文件夹
(1)虚拟机VMware-->VM-->InstallVMwareTools
(2)终端下
cd/media
cd/VMwareTools\
cpVMwareTools-9.2.2-893683.tar.gz~
cd~
tar-zxvfVMwareTools-9.2.2-893683.tar.gz
cdvmware-tools-distrib/
sudo./vmware-install.pl
(3)在虚拟机VMware下VMware-->VM-->setting-->
Options-->SharedFolders-->Alwaysenabled-->add-->设置windows下共享文件夹
(4)cd/mnt/hgfs
ls看到共享的文件夹
linux下文件传到window下:
cp文件/mnt/hgfs/文件夹
cp目录/mnt/hgfs/文件夹-a
window下传到linux下:
直接复制到共享的文件夹下
10.标识符
(1)以数字字母或者下划线组成
(2)不能以数字开头
(3)不能与关键字重名
11.动态类型:
auto
寄存器存储类型变量
函数形参、
静态类型:
全局变量
静态局部变量
静态全局变量
12.查看一个可执行程序的运行时间
time./可执行文件
eg:
register提高程序的执行效率
第三天
1.深入理解数据类型
数据类型分为4种:
基本数据类型:
intcharfloatemun
构造数据类型:
数组、结构体、共用体
空类型:
指针类型:
指针
int[] //数组类型
int(*)[]//指针数组类型
int*[] //数组指针类型
int* //指针类型
int*() //指针函数
int(*)()//函数指针
2.运算符结合方式
左结合:
&&||先运算左边的表达式值,再运算右边表达式的值
右结合:
反之
3.sizeof(各种类型或者变量)
4.运算符优先级
!
>算术运算符>关系运算符>&&和||
5.终端下
vim-t数据类型eg:
vim-tdev_t
ctrl+]//跟踪宏定义
第四天
1.利用do~while来当做{}使用,避免在使用宏定义的时候出现问题
参考内核中优秀的代码,记录下来借鉴使用
eg:
#include
voidfun(void)
{
printf("hello\n");
}
#defineMAROC(y)do{if(y==0)fun();}while(0);//
//#defineMAROC(y)if(y==0)fun();//直接使用这个宏的话会出现问题
intmain(intargc,constchar*argv[])
{
intx=1;
inty=0;
if(x)
MAROC(y)
else
printf("world\n");
if(x)
do{
if(y==0)
fun();
}while(0);
else
printf("world\n");
return0;
}
2.对数组初始化可以使用memset函数
inta[10];
memset(a,0,sizeof(a));
或者
bzero(a,sizeof(a));
3.求数组中元素的个数
元素个数=sizeof(数组名)/sizeof(数据类型)
第五天
1.指针
int*p[2];//数组p本身就是一个地址量,现在又int*,所以p是指针的指针,属于二级指针
int**p1;
p1=p;
理解:
由于p是数组,所以p代表&p[0],又由于p[0]是int*,所以,&p[0]是int**,即:
p是二级指针
2.*p++和(*p)++是不同的
(*p)++是取出内容再自加;
3.数组下标越界出现死循环
第六天指针
1.int**p;
ints[10]={0};
p=&s;//这种写法是不正确的,&s+1则地址偏移10*4;p+1,偏移4个字节,所以寻址方式不一样
2.由字符型的指针来访问存储中的每一个字节
unsignedinta=0x12345678;
unsignedchar*p;
p=(unsignedchar*)&a;//注意:
p是指向unsignedchar类型的指针,unsignedint在内存中存储占据4个字节
//可以使用unsignedchar类型指针来操作unsignedint内存中的单个字节
printf("%x\n",*p);
printf("%x\n",*(p+1));
printf("%x\n",*(p+2));
printf("%x\n\n",*(p+3));
*(p)=0x55;
*(p+3)=0xfa;//可以使用unsignedchar类型指针来操作unsignedint内存中的单个字节
printf("%x\n",*p);
printf("%x\n",*(p+1));
printf("%x\n",*(p+2));
printf("%x\n",*(p+3));
3、const只存在在汇编之前,汇编之后const的修饰就不起作用了
4、void类型,是未定义类型;未确定用来做什么的类型
第七天指针
1.inta[3][3],int(*p)[3],int**pp的区分:
a是一个二级指针地址,但是a+1是跳转一个有三个元素的一维数组的大小;
pp是一个二级指针变量,里边存储着的是一个地址值,pp+1是跳转4个字节大小;
a和pp两者寻址方式不一样;
数组指针访问二维数组:
2.数组名和指针变量寻址方式:
inta[3];
int*p;
p=a;
*(a+1)=10;
*(p+1)=20;//两者寻址方式不一样
假设a=0x1234这个地址值,那么a+1是在0x1234的基础加上4等于0x1238,是属于直接寻址
而,*(p+1)则是取出p中存放的地址值,再在此地址值上加上偏移值
3.进程的内核结构
Linux中使用的是虚拟内存技术,使得每个进程都有自己独立的内存空间,该地址空间是4GB,其中用户空间能够访问0~3GB,内核空间3GB~4GB;通常情况下,应用程序只能访问用户空间的虚拟内存。
用户空间包括以下几个功能区域:
(1)只读段:
包括程序代码(.init和.text)和只读数据(.rodata)
(2)数据段:
存放全局变量和静态变量
(3)栈:
由系统自动分配,存放函数的参数值、局部变量值、返回地址等
(4)堆:
存放动态分配的数据,一般由程序员分配和释放
(5)共享库的内存映射区:
Linux动态链接器和其他共享库代码的映射区;
cat/proc/进程号/maps 查看某一个进程的内存分布情况
4.字符串常量赋值操作出现问题:
charstr[]="hello";
str中的内容是存在栈区,是由系统自动分配的,在栈区的内容是可以改变的;
而char*p="helloworld";中"helloworld"字符串常量是存储在静态只读区的(.text),是不可以改变的,指针变量p只是指向这块静态存储区,而不能改变其内容;可查看汇编语句来分析;
所以,*(str+3)='z';//是可以赋值的,栈区的内存是可读可写的
*(p+3)='z';//这样赋值是不可以的;
5、指针操作总结
int*p;
int*p=&a;
int*p=(int*)0x1234;//确定地址是什么
int*q=&a;
int*p=q;
int*p=NULL;
p=&a;
p=(int*)0x1234;
char*q;
p=(int*)q;
p=(int*)a;//要知道自己干嘛呢
*p++//指针运算符*和++的优先级相同,自右向左结合,所以,先是*p,然后再++
(*p)++
int*p=&a;
int**p=&p;
a=10==**p=10
inta[10];
p=a;
p=(int*)&a;//
*(p+1)==a[1]==p[1]
*(a+1)
constint*p;//*p只读
intconst*p;//
int*constp;//p只读
constint*constp;//p*p只读
//*((int*)&a)=10;
void和void*p:
是未确定用来做什么的类型
6、关于const
const只存在于汇编之前,预处理阶段,汇编之后const修饰只读的功能就不存在了
intconsta=9;//修饰变量a中值是不可改变的,但是想改变的话,使用下面的方法
*((int*)&a)=10;//使用指针来绕开const的修饰,但是不建议这么用
7、指针函数
指针函数不可以返回局部变量的地址
可返回的地址有三种:
(1)静态变量的地址
(2)字符串常量的地址
(3)堆上的地址,例如:
malloc分配的空间的地址可以返回
8、define和#define
define不是关键字
#define也不是关键字,是属于预处理器
9、函数指针和指针函数
10、signal函数分析
可以这么理解:
void(*)(int) signal(intsig, void(*)(int) handler)//是一个返回值是函数指针类型,参数也是带有函数指针类型
void(*signal(intsignum, void(*handler)(int) ) )(int);
void(*signal(intsignum, void(*handler)(int) ) )(int);
void(*signal(intsignum, void(*handler)(int) ) )(int);
void(*p)(int)//最外层函数是这种类型的
void(*handler)(int) //是函数指针类型,其所指向的内容为返回值为void,函数参数int,即:
voidhandler(int)
signal(intsignum,函数指针类型)//这个函数返回的是一个函数指针类型,此函数的参数是int和函数指针类型
返回函数指针,该函数怎么写?
?
?
答:
见专题二
第八天函数
1、函数的复制传参
在main函数中,定义两个变量a,b
intfun(inta,intb)
{
returna+b;
}
intmain(intargc,constchar**argv)
{
inta,b;
fun(a,b);
return0;
}
局部自动变量是存储在栈区的,函数刚开始执行时,sp指向main函数的栈区,当调用函数fun的时候,将传入的参数拷贝到fun函数的栈区,所以对fun函数的栈区操作,不会影响到主函数中的变量的值;
2、函数地址传递
如果函数fun定义如下:
intfun(inta,int*p)
{
*p=*p+a;
return*p;
}
main函数中调用如下
intmain(intargc,constchar**argv)
{
inta,b;
fun(a,&b);
return0;
}
程序栈区的存储分布情况:
3、由1、2的分析,结构体作为函数的参数传入到函数中时,如果结构体比较大的话,会占用大量的栈区,最后可能会栈溢出;
所以,在操作结构体时,尽量传入结构体的指针来操作;
4、malloc使用注意:
malloc分配的内存空间是在堆区,堆区的内存系统不会自动释放和回收,需要程序员手动分配和释放。
malloc使用模版:
unsignedchar*p=NULL;
if(NULL==(p=(unsignedchar*)malloc(szieof())))
{
分配失败;
}
.
.
.
free(p);
p=NULL;
第十天测试
1、鞍点,一个二维数组中,一行中的最大值,一列中的最小值
2、
///////////////////////////////////////////////////////////////////////////////////////
补充:
1、printf函数首先是将数据放到缓冲区中,不立刻打印出来,而是要遇到'\n'的时候才能够打印出缓冲区中的数据
#include
#include
intmain(intargc,constchar*argv[])
{
inti=0;
printf("helloworld"); //不立刻打印出来
sleep(3); //延时3秒之后
printf("\n"); //遇到\n才打印出来
return0;
}