C语言复习(1):
-
1.数据类型:
char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整形
float //单精度浮点数
double //双精度浮点数 -
2.变量:
变量的作用域和生命周期
(1)作用域
作用域(scope),程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有
效可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
(a)局部变量的作用域是变量所在的局部范围。
(b)全局变量的作用域是整个工程。
(2)生命周期
变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段
(a)局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。
(b) 全局变量的生命周期是:整个程序的生命周期。
© 局部变量,全局变量:当局部变量和全局变量同名的时候,局部变量优先使用。 -
3.常量:
字面常量
const 修饰的常变量
#define 定义的标识符常量
枚举常量 -
4.数组:
C语言中给了数组的定义:一组相同类型元素的集合
数组定义
int arr[10] = {1,2,3,4,5,6,7,8,9,10}//定义一个整型数组,最多存放10个元素。 -
5.常见关键字
(1)关键字typedef:
typedef顾名思义是类型定义,这里应该理解为类型重命名。
例://将unsigned int重命名为uint_32所以uint_32也是一个类型名
typedef unsigned int uint_32;
int main(){
//观察num1和num2,这两个变量的类型是一样的
unsigned int num1 = 0;
uint_32 num2 = 0;
return 0;
}
(2)关键字static
在C语言中:
static是用来修饰变量和函数的 -
修饰局部变量
-
修饰全局变量
-
修饰函数
(3)define 定义常量和宏
//define定义标识符常量
#define MAX 1000
//define定义宏
#define ADD(x, y) ((x)+(y))
#include <stdio.h>
int main()
{
int sum = ADD(2, 3);
printf("sum = %d\n", sum);
sum = 10*ADD(2, 3);
printf("sum = %d\n", sum);
return 0;
}
6.指针
(1)内存
内存是电脑上特别重要的存储器,计算机中所有程序的运行都是在内存中进行的 。
所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。
为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。
// 以整形指针举例,可以推广到其他类型,如:
#include <stdio.h>
int main()
{
char ch = 'w';
char* pc = &ch; *pc = 'q';
printf("%c\n", ch);
return 0; }
//指针变量的大小
#include <stdio.h>
int main()
{
printf("%d\n", sizeof(char *));
printf("%d\n", sizeof(short *));
printf("%d\n", sizeof(int *));
printf("%d\n", sizeof(double *));
return 0; }
结论:指针大小在32位平台是4个字节,64位平台是8个字节。
(2)指针类型的意义:
指针±整数,
指针的类型决定了指针向前或者向后走一步有多大距离。即对指针解引用的时候有多大的权限(能操作几个字节)。比如:char* 的指针解
引用就只能访问一个字节,而int*的指针的解引用就能访问四个字节。
(3)指针和数组名:
数组名表示的是数组首元素的地址。
(4)指针运算:
指针± 整数
指针-指针
指针的关系运算
- 在进行数组内指针的比较时:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一
个元素之前的那个内存位置的指针进行比较。
(5)指针数组:
int* arr1[10]; //整形指针的数组
指针数组是一个存放指针的数组。
(6)数组指针:
int(*p)[10];
p先和* 结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。 所以p是一个指针,指向一个数组,叫数组指针。[]的优先级要高于 * 号的,所以必须加上()来保证p先和*结合。
&p表示的是数组的地址,而不是数组首元素的地址。数组的地址+1,跳过整个数组的大小。
7.结构体
-
结构体变量访问成员 结构变量的成员是通过点操作符(.)访问的。点操作符接受两个操作数。
-
结构体传参的时候,要传结构体的地址。
-
内存对齐
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默
认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8 Linux中的默认值为4
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍
处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。`为什么需要内存对齐:
平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址
处取某些特定类型的数据,否则抛出硬件异常。
性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理
器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
注意事项:
(1) 结构体的内存对齐是拿空间来换取时间的做法。
(2)让占用空间小的成员尽量集中在一起。
8.分支语句和循环语句
9.函数
(1)函数的参数
实际参数(实参):
真实传给函数的参数,叫实参。实参可以是:常量、变量、表达式、函数等。无论实参是何种类型的量,在
进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。
形式参数(形参):
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单
元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。
(2)函数的调用:
传值调用:
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
传址调用:
传值调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
这种传参方式可以让函数和函数外边的变量建立起正真的联系,也就是函数内部可以直接操作函数外部
的变量。
(3)函数递归:
递归的两个必要条件:
- 存在限制条件,当满足这个条件的时候,递归便不再继续(递归结束条件)。
- 每次递归调用之后越来越接近这个限制条件。
如果递归调用次数超过一定限制或产生死循环,系统分配的栈空间不足会产生栈空间耗尽的情况,称为栈溢出。可通过将递归改为非递归的形式解决。
10.数组
(1)数组创建:
- 数组创建,[]要给一个常量,不能使用变量。
- 随着数组下标的增长,元素的地址,也在有规律的递增。数组在内存中是连续存放的。
(2)数组使用:
- 数组是数组名后使用[]运算符中的下标访问的,下标是从0开始的。
- 数组大小可通过类似
int sz=sizeof(arr)/sizeof(arr[0]);
形式计算。 - 数组传参,如果函数内部需要知道数组元素个数应该在函数外部算出元素个数,以参数的形式传递给函数。
11.操作符
操作符(C语言)
12.数据的存储
(1)整形在内存中的存储:
- 原码、反码、补码:
计算机中的符号数有三种表示方法,即原码、反码和补码。三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位,三种表示方法各不相同。
原码:直接将二进制按照正负数的形式翻译成二进制就可以。
反码:将原码的符号位不变,其他位依次按位取反就可以得到了。
补码:反码+1就得到补码。正数的原、反、补码都相同。 - 对于整形来说:数据存放内存中其实存放的是补码。
(2)浮点型在内存中的存储:
根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数V可以表示成下面的形式:
V=(-1)^S*M*2^E
- (-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。
- M表示有效数字,大于等于1,小于2。
- 2^E表示指数位。
- IEEE754规定:对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。
- 对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。
- 在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部
分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效
数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。 - E为一个无符号整数(unsigned int):这意味着,如果E为8位,它的取值范围为0~ 255;如果E为11位,它的取值范围为0~2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE754规定,存入内存时E 的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。比如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。
- E全为0:这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
- E全为1:这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);
**13.**动态内存管理
- 需要动态内存管理的原因:
(1) 在栈空间上空间开辟大小是固定的。
(2) 在栈空间上开辟数组空间时必须指定数组大小,而有时候我们需要的空间大小在程序运行时才能知道。 - 动态内存函数:
void* malloc(size_T size);
//这个函数在堆上开辟空间:成功返回开辟好空间的指针;失败返回NULL,需做好返回值得检查
void free (void* ptr)
//free函数用来释放动态开辟的内存:如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的;如果参数 ptr 是NULL指针,则函数什么事都不做。
void* calloc(size_T num,size_T size);
//开辟num个初始为0的地址的空间:与malloc的区别是把开辟的地址的每个字节全初始化为0
void* ralloc(void* ptr,size_T size);
//将原地址空间大小调整为size,如后面空间不足则开辟新的空间拷贝原数据