------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
C语言学习过程中的重点与难点:
(1)运算符:自增、自减运算符。
(2)进制:各种进制转换、原码、反码、补码。
(3)数组:一维数组、二维数组、多维数组。
(4)循环:多重循环的嵌套、排序、查找。?
(5)函数:递归函数、递归调用。
(6)指针:一级指针、多级指针、指针和数组、函数、结构体之间的关系。
(7)内存管理:C语言的内存管理问题、内存泄露、野指针。
(8)有参宏及条件编译。
(9)多文件开发:多文件编译、多文件开发。
以下的内容主要是我在学习C语言的过程中的一些心得体会与总结。
一、运算符(自增、自减)
(1)前缀表达式:++x, --x; 其中x表示变量名,先完成变量的自增自减1运算,再用x的值作为表达式的值;即“先变后用”,也就是变量的值先变,再用变量的值参与运算。
(2)后缀表达式:x++, x--; 先用x的当前值作为表达式的值,再进行自增自减1运算。即“先用后变”,也就是先用变量的值参与运算,变量的值再进行自增自减变化。
计算技巧:在变量的上方写两个数字,用逗号隔开,表示它的表达式的值和变量的值,使用的时候,使用前面的表达式的值。
例子:
输出结果:
二、进制:各种进制转换以及原码、反码、补码
1、进制的转换
2进制:逢二进1 0 1
8进制:逢八进1 01 234567
10进制:逢十进1 (默认的进制) 0 – 9
16进制:逢十六进1 0 - 9、 A B C D E F (内存的地址格式)
2、源码、反码、补码
(1)源码:
正数:就是其二进制表示 +1 00000001
负数:二进制的最高位是1 -1 10000001
(2)反码:
正数:就是其二进制表示 +1 00000001
负数:符号位不变,其他位逐位取反 -1 11111110
(3)补码:?
正数:就是其二进制表示 +1 00000001
负数:反码 +1 11111111
注意:补码的真值不等于原码的真值的
三、一维数组
1、一维数组的定义:
数组的使用的流程:先定义 ---> 初始化 --->使用
数组定义完成后,如果不初始化里面存放的是系统随机的数字。
格式:int arr[10] 即定义长度为10的整型数组,每个都是Int型的,数组的名称 arr
(1)定义变量的同时,可以定义数组 int a[10],b,c;
(2) int n=5; int a2[n]; //其他编译器数组定义的时候,数组长度不能使用变量,但是 xcode做了优化是可以的。
(4)#define N 5 int a3[N+3];?
(5)int a4; int a4[10]; 数组的名称不能和变量名重名。
(6)数组名的命名原则严格遵守标识符命名原则。
(1)一维数组的存储
存储方式:?1)计算机会给数组分配一块连续的存储空间
2)数组名代表数组的首地址,从首地址位置,依次存入数组的第1个、第2个....、第n个元素 3)每个元素占用相同的字节数(取决于数组类型)
4)并且数组中元素之间的地址是连续。
模拟该数组的内存存储细节如下: int x[2]={1,2};?int ca[5]={‘a‘,‘A‘,‘B‘,‘C‘,‘D‘};
例子:
(2)一维数组遇到的疑问:数组如果没有初始化,数组中到底有没有值(以整型数组为例)
例子:
(3)一维数组的遍历
例子:
输出结果:
(4)实例:彩票(实现选5注,21选5):
思路:1.随机产生1-21之间的数字
2.选取5个
3.排重
4.产生5注
输出结果:
结论:如果定义数组后,没有初始化,数组中是有值的,是随机的垃圾数,所以如果想要正确使用数组应该要进行初始化。
四、指针与结构体
1、指针
注:严格的说指针是一个地址,是一个常量。
(1)定义一个指针变量
对指针变量的定义包括三个内容:
1)指针类型说明,即定义变量为一个指针变量;
2)指针变量名;?
3)变量值(指针)
其一般形式为: 类型说明符 *变量名;
其中,*表示这是一个指针变量,变量名即为定义的指针变量名,类型说明符表示本指针变量所指 向的变量的数据类型。
(2) 用函数实现两变量值交换
int a = 3,b = 5;
int temp; temp = a; a = b;?b = temp;
常规的做法不能实现变量值的交换,指针的地址交换也不能实现。
但是指针指向的值交换可以实现,如下所示:
(3)利用函数指针调用函数
例子:
2、结构体
(1)定义一个结构的一般形式为:
struct 结构名{
成员表列 };
(2)例如定义一个学生的结构如下:
struct stu
{ int num;
char name[20]; //char *name; char sex;?float score;
};
即成员表列由若干个成员组成,每个成员都是该结构的一个组成部分。对每个成员也必须作类 型说明,其形式为:类型说明符 成员名;
注意:成员名的命名应符合标识符的书写规定。
(3)实例:先定义结构体变量,在初始化
从上述程序可以看出用,函数指针变量形式调用函数的步骤如下:
1)先定义函数指针变量,如 int (*fun)(int *p,int len); 定义 fun为函数指针变量。
2)把被调函数的入口地址(函数名)赋予该函数指针变量,如程序中 fun=getMax;
3)用函数指针变量形式调用函数,如程序 int max=fun(a,5);
使用函数指针变量还应注意以下两点:
1)函数指针变量不能进行算术运算,这是与数组指针变量不同的。数组指针变量加减一个整数可 使指针移动指向后面或前面的数组元素,而函数指针的移动是毫无意义的。
2)函数调用中"(*指针变量名)"的两边的括号不可少,其中的*不应该理解为求值运算,在此处它 只是一种表示符号。
五、内存管理、野指针、内存泄露
1、内存
(1)堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或 缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张); 当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减) 。
(2)栈 (stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}” 中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变 量)。除此以外, 在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值 也会被存放回栈中。由于栈的先进先出特点,所以 栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
(3)内存分区
(4)内存分布图:(自上而下地址从低到高) 。
(5)
(6)
(7)堆的地址分配方式是从高到低
2、野指针
内存好比作尺子,尺子上的0 毫米处就是内存的0 地址处,也就是NULL 地址处。这条栓“野指 针”的链子就是这个“NULL”。
定义指针变量的同时最好初始化为NULL,用完指针之后也将指针变量的值设置为NULL。也就是说 除了在使用时,别的时间都把指针“栓”到0 地址处。这样它就老实了。
int *p;?p =(int *) malloc(100); //释放指针的操作 *(p+1) = 100;
3、内存泄露
int main(int argc, const char * argv[])
{ //该句话会造成内存泄露
int *p = malloc(4*sizeof(int));?return 0;
}
解决办法: 在p被释放之前,先要释放堆区中的内存空间。
free函数使用定义函数:void free(void *ptr);
六、 宏定义
被定义为“宏”的标识符 称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。
(1) 无参宏的宏名后不带参数。其定义的一般形式为:
#define 标识符(宏名) 字符串
其中的“#”表示这是一条预处理命令。凡是以“#”开头的均为预处理命令。“define”为 宏定义命令。“标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。
(2) C语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。
带参宏定义的一般形式为:
#define 宏名(形参表) 字符串