C语言学习(1)

C

一,对编程以及计算机实现的过程,方法;
计算机实现的功能: 简单的说就是 input [算法] output;
计算机是怎么实现的?
二进制,只有 0 和 1 构成;
计算机使用大量的位来表示信息;
8bit=1kb;1024kb=1M;1024M=1GB;
0 和 1可以理解为开关;

计算机不仅能够存储数字,还能够存储字母,图像,视频,声音等等;
那么是如何表示的?
数字,10 进制和 2进制的转换;
字符,ASCII码;
图像,RGB;
视频,图像的快速移动,转换;
音乐,将声音的大小,长短量化;

**程序设计过程:**分析问题,设计算法,编写程序,运行分析调试,编写程序文档;

算法的特点:
有穷性(步骤有限),
确定性(含义确定),
可行性(实现),
有零个或多个输入(数据输入),
有一个或多个输出(数据输出);

面向过程的结构化程序设计三种基本结构:
顺序结构,选择结构,循环结构;

**原则:**自顶向下,逐步求精,模块化;

**C程序运行过程:**源程序(.c) — 目标程序(.obj) — 可执行程序(.exe) — 计算机运行;

系统位数与数据内存单元所占用字节的关系?
**32为操作系统:**cpu内部寄存器和寻址空间为32位,指令集可以运行32位数据指令,即处理器一次可处理32位数据;cpu的寻址能力以字节位单位,则32位寻址的cpu可以寻址2^32次方大小的地址(这里就不要理解成4字节,32bit=4byte),若超过cpu能力范围,cpu就无法找到数据,因此32位系统最大搭配4G内存;
另外: 32条地址总线并不是完全用来控制cpu和内存之间的通讯,还要控制其他设备之间的通讯,因此cpu可以与内存之间的地址线不足32条,这就是为什么系统无法识别到4G内存的原因;
64位系统同上理解;
2^32 = 4*1024(M)*1024(B)*1024(b)=4GB;
2^64 = 2^32 * 2^10 * 2^10;//理论上支持上亿GB内存;

内存的基本结构: 二进制数;1Byte=8Bit,
*cpu能够直接访问内存(主存储器)里的数据;
*像硬盘之类的不能直接访问,需要将硬盘里的数据读入内存中;
*cpu每次只能访问一个byte,即1byte就是内存的最小 IO单位;
注意: 内存地址仅仅是个编号,代表一个空间;内存地址所执行的内存单元大小是一个字节,跟内存地址的位数无关;

不同数据类型进行计算,一般转换形式:
short/char->int->unsigned->long->double<-float;
//箭头表示可转换方向;

对变量的认识:
变量以标识符为名,其值可以改变的量;
在定义时不允许连续赋值,*右值可以是赋值表达式,前提是变量在使用前被定义;
每个变量都有特定的类型,类型决定变量存储空间,
定义变量时,会申请一个内存单元,并将值存储进去;
*因此可以认为,一个内存单元有两个属性:值和地址;
//值和地址的关系,可以认为你家为什么要有门牌号一样;

运算符优先级:
1,数组下标 圆括号 成员选择(对象\指针) 左结合性(左到右)
2,单目运算符:所需变量为一个的运算符; 右结合性(右到左)
*(解引用),&(取地址),sizeof运算符,!,负号,强制类型转换等;
3,双目运算符:算术运算符 左移 右移运算符 左结合性
4,双目运算符:关系运算符 左结合性
5,双目运算符:位运算符 逻辑运算符 左结合性
6,三目运算符:条件运算符 (表达式1?表达式2:表达式3;) 右结合性
7,赋值运算符: 右结合性
8,逗号运算符: 左结合性

逗号和赋值运算符优先级最低,括号优先级最高;

位运算符: 位运算都是通过二进制进行计算;
1,按位与 & 1&1=1,1&0=0,0&1=0,0&0=0;
2,按位或 | 1|1=1,1|0=1,0|1=0,0|0=0;
3,异或 ^ 当两个值不同时等于1;
4,取反 ~ 二进制0变1,1变0; 单目运算符
5,左移 << 向左移一位,相当于原有数值乘2(符号位不变)
6,右移 >> 向右移一位,相当于原有数值除2(符号位不变)

二进制的符号位:
一个字节类型的数据类型在内存中占8位,
无符号: 0-255,即2^8;没有符号位,全部二进制代表数值;
有符号: -128-127;最高位表示符号位;
可以理解为 ’ -128 ’ 表示 ’ - ', ’ 0 ’ 表示 ’ + ';

二进制中的原码,反码,补码:
对于有符号而言:
1,二进制最高位是符号位: 0表示正数,1表示负数;
2,正数的原码,反码,补码都一样;
3,负数的反码=它的原码符号位不变,其它位取反
4,负数的补码=它的反码+1;(二进制逢2进1)
5,数值0的反码,补码都是0;
注意: 计算机运算时,都是以补码的方式来运算;(不论正负数)

逻辑判断0与1:
C语言里,0表示假,!0表示真,即可理解位所有非0值都为真;
不论是数值还是逻辑值,在需要条件,判断,运算表达式里,都可以按上面加粗字体来理解;

例,if(0),表达式为假,不永远不执行,
若在if-else语句里,就会执行else语句;
while(0),永远不执行;for语句里的条件也是如此;
注意: switch(0),这里同样是数值,但switch语法关系,可以执行,有(case 0: ;)时就执行这一语句,没有就执行default;

逻辑与(&&),双目运算符: 当左右值有一个为假时,结果为假,返回值0;左值一为假就不用对右值进行判断(即短路现象);当佐治右值都为真时,结果为真,返回值1;
注意: 左右值可以是表达式,而且受运算符优先级影响;

逻辑或(||), 当左右值有一个为真,返回值为1,当左右值两个都为假,返回值才为0;若返回值第一个为真,发生短路,返回值为1,且不对右值进行判断;
注意: 有表达式就有赋值,故短路现象需要注意;

if(表达式):表达式为真,执行语句块;否则不执行;
注意: if语句表达式中 ’ = ‘和’ == '的区别,前者是赋值,后者是比较;且if语句里的赋值语句,是将赋值语句的最后结果作为判断条件,不是必须的情况下,尽量避免使用赋值表达式;

条件运算符, 三元运算符(表达式1?表达式2:表达式3)
对表达式1进行判断,若为真执行表达式2,并返回该值;否者执行表达式3,返回该值;
对以上内容都能够综合使用,不要限制于基本语句语法;

标识符: 预定义标识符,可以作为用户标识符,但不要在程序中作为用户标识符使用;
例,printf scanf define

数据输出:
scanf函数: 务必使用&运算符,除非变量本身是地址变量;
输入 double类型 时候一定要使用%lf;
务必将格式字符数量和输入数量对应;

数据输入:
printf函数: 格式控制符有几项,后面输出项就有几项,类型必须匹配,%多余输出项,会乱码;%少于输出项,多余的不输出;
printf函数是右运算,故注意变量运算;
常用输出格式:以%号开头,一个格式字符结束,中间可以插入宽度说明,左对齐(-),带前缀(通过在%和格式字符中间加入#显示);
strlen函数: 计算实际长度,’\0’不会计入;
sizeof函数: 计算占用字节长度;
putchar(字符); 每次只输入一个字符,不会自动换行;
puts(s); 自动在串尾输出换行符;
gets(s); 读取字符串直到换行符或文件末尾,结束;
变量=getchar();
//从键盘接收,回车符也放在缓冲区,直达用户按回车健时,每次从键盘缓冲区读入一个字符;
可以通过,((变量=getchar())!=" ")来判断是否结束输入;

常见数学函数:
头文件:math.h
pow(底数,指数);
sqrt(数值),开平方;
abs(整数值),取整数的绝对值;
fabs(实数型),取实数型值的绝对值;
sin(),cos(),exp()以自然对数e为底的幂;

函数返回值说明, 除了为int和char等基础类型以外,都需要在程序之前说明;说明形式后面要加分号;
值传递,单项传递(实参不做改变), 即使指针变量也是按值传递;
地址传递(实参被改变);

在调用一个函数的过程,又出现间接或直接的调用该函数本身,成为函数的递归调用;

指针变量赋值:
除了可以给指针变量赋地址值之外,还可以给变量赋空值;
int *p=NULL; 还可以是 0 或 ‘\0’ ;但对空地址赋值会报错;
指针赋值和不赋值时不同的,指针变量为赋值,可以是任意值;
赋予了0值后,表示一个空值,它不指向具体的变量;

指针变量在定义或初始化赋值时和赋值语句是不同含义的, 也可以说指针运算符* 和指针变量说明中的指针说明符* 不是一回事;
初始化赋值 int a,*p=&a;
赋值语句 int a,*p;p=&a;
(为什么没有加 * 了?)
区别:在指针变量说明中’ * '是类型说明符,表示其后的是指针类型;
而表达式中出现的 ’ * '则是一个运算符用以表示指针变量所指的变量;

二维数组与指针:
定义了一个二维数组int a[3][4],那么就可以将这个二维数组分布,想象成一个矩阵,3行4列;
但在内存中,a的分布是一维线性的,整个数组占用一块连续的内存;先存放a[0]行,a[1]行…,a[3]行依次向后存放;

每行首地址地址位数由数组基类型和每行元素决定的;
定义一个指向a的指针变量p;int (p)[4]=a;
p指向含有4个int型元素的数组;
1,p指向a的开头,也即第0行;p+1,指向第一行
2, * (p+1) 表示取地址里的数据,也就是整个第一行的数据;
可以通过sizeof(
(P+1))运算;得出16的结果;
3, * (p+1)+1 表示第一行第一个元素;
这里有个疑问点,(p+1)的表达方式是不是变了?
没错,
(p+1)除单独使用时,表示整行的数据,而放在表达式里,将会被转化为这一行的首地址 (因为这里使用整行的数据没有实际含义,编译器遇到这种情况都会转化为指向该行的第0个元素的指针, 就如同一维数组的名字,在这个表达式后+1,就可以看作一维数组的首地址+1,即指向下一个元素),故可以表示为第一行第一列;

4, * (* (p+1)+1) 由上理解:表示取a[1][1]的地址的值;
* (* p+1),表示的是第零行第一个元素的值;
* (p+0)和* p和* (p+n)同上理解;

注意:[]的优先级高于*的,所有()是必须加的,而int p[n];p就成了一个指针数组,即p的每一个数据类型都是一个int类型的指针;
也可以这样理解 * (p+1);
p是指向二维数组的指针,加上
取一维数组的指针,即第一行;(指向二维数组首地址的地址)
p是指针指向包含4个元素的一维数组的指针变量;

函数指针:指向函数的指针变量,因此我们能够知道每个函数都有一个入口地址;

调用函数和函数的参数;
声明方法:返回类型(*指针变量名)(形参列表);
int fun(int x); //声明一个函数;
int (*f)(int x); //声明一个函数指针;
f=fun; //将fun函数的首地址赋给了地址,即fun代表函数首地址;或者通过这种形式:f=&fun; 调用:(*p)();
指针函数: 返回值是指针的函数;
指针数组: 数组元素都是相同类型的指针;在内存中占有多个指针的存储空间;(适用于指向若干字符串?)
//char a[4]; 在某一个34的空间里,a分配的空间会取决与具体字符串长度,不会限制在相同的字符长度里(不会取决与初始化的长度),且分配空间不一定是连续的;另外通过指针间接进行访问效率要比下标方式要高;但是通过下标方式方便修改某一元素的值,而指针无法做到;

数组指针:指向数组首元素的地址的指针;在C语言里专门指向二维数组的,在内存中占有一个指针的存储空间;
//char (*a)[4];数组指针的元素个数和二维数组列长度一致;
//(这个指针存放的是数组首地址的地址,相当于二级地址)
//函数指针数组: 函数返回值类型 (*数组名[])(),每一个元素是指指向函数入口的指针; 调用:数组名n;

局部变量,全局变量和存储变量;
形参变量只有在被调用期间才分配内存单元,调用结束立即释放;既可以认为形参变量只有在函数内才是有效的;即存在作用域;不仅对于形参变量,C语言所有的量都存在作用域,变量的说明方式不同,其作用域也不同;

按作用域可分为:局部变量和全局变量;

1,局部变量(内部变量)
作用域仅限于函数内,离开该函数后再使用这种变量是非法的;

注意:主函数中定义的变量也只能在主函数中使用,不能在其它函数中使用;

形参变量是属于被调用函数的局部变量,实参变量是属于主调函数的局部变量;
允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰;
在复合语句中也可以定义变量,其作用域只在复合语句范围内;

2,全局变量(外部变量)
它是在函数外部定义的变量,不属于哪一个函数,属于一个源程序文件,其作用域是整个源程序;
注意:只有函数内经过说明的全局变量才能使用;
全局变量的说明符为extern;
在函数之前定义的全局变量,在该函数使用时可以不再加以说明;

变量的存储类别:动态存储方式与静态存储方式(以生存期角度分);
静态存储方式:是指在程序运行期间分配固定的存储空间方式;
动态存储方式:是在程序运行期间根据需要进行动态的分配存储空间的方式;

用户存储空间可以分为三个部分:
1)程序区; 2)静态存储区; 3)动态存储区;

全局变量全部存放在静态存储区,在程序开始执行时给全局变量分配存储区,程序完毕就释放;在程序执行过程中它们占据固定的存储单元,而不动态地进行分配和释放;

动态存储区存放一下数据:

  1. 函数形式参数;2)自动变量;3)函数调用返回地址等;
    以上数据,在函数开始调用时分配动态存储空间,函数结束时释放这些空间;

//每个变量和函数有两个属性:数据类型和数据的存储类别;

auto变量:函数中的局部变量,如不专门声明为static存储类别,都是的动态的分配存储空间,数据存储在动态存储区中;
函数中的形参和函数中定义的变量(包括在复合语句中定义的变量),都属于此类;关键字auto可省略;

用static声明局部变量:
有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,这时就可以指定局部变量为静态局部变量,用关键字static进行声明;

对静态局部变量的说明:

  1. 静态局部变量属于静态存储类别,在静态存储区分配存储单元;在程序整个运行期间都不释放;
  2. 静态局部变量在编译时赋初值,即只赋初值一次;而对自动变量赋初值是在函数调用时进行,每调用一次函数重新赋一次值;
  3. 如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符变量);而对自动变量来说,如果不赋初值则它的值是一个不确定的值;

register变量:
为提高效率,C语言允许将局部变量的值放在cpu中的寄存器中这种变量叫”寄存器变量”,用关键字register作声明;

注意:只有局部变量和形式参数可以作为寄存器变量;
一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量;
局部静态变量不能定义为寄存器变量;

extern声明外部变量:
在函数外部定义的,它的作用域范围只限于定义处开始,到程序文件的末尾;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值