Men的博客

欢迎光临!

0%

一 数组及其特性

1)数组的3要素
类型名 数组名[数组元素个数]
(1)类型名:char ,int ,float, double,char * ,int *,float *,double 等等
(2)数组名:只要符合c语言标识符规范即可。
(3)数组元素个数:可以使常量,比如4,5等只要能够有确定值的表达式就可以
2)声明一个有10个整型元素的数组
int arr[10]; <==> int arr[2
5];
声明一个有10个字符元素的数组
char str[10];
其他类型,如float,double,char *,int *,float *,double *以此类推
3)整型数组初始化
(1)完全初始化(2)部分初始化
除了被初始化的元素是有确定的值,未被初始化的元素被系统设置为数值0
例如:int arr[10]={1,2,3,4};
这时该数组的10 个元素依次为:1,2,3,4,0,0,0,0,0,0
特例,极端的部分初始化,如int arr[10]={};这种用法很常见,数组所有元素
被系统设置为0 7
指定初始化,如int arr[10]={[1]=4,[2]=5};相当于把下标为1的元素初始化为4
把下标为2的元素初始化为5,未被初始化的元素被系统自动设为0
(3)其他类型的初始化也都遵循上述原则
注意:数组只有在初始化的时候可以整体赋值,单就赋值而言数组是不能被整体赋
值的
4)整型数组元素的引用
数组下标是从0开始的,数组名字[n],这个相当于数组第n+1个元素
如int arr[10]={1,2,3,4,5,6,7,8,9,0};
arr[0]=1
arr[2]=3
arr[9]=0
数组下标是有范围的,还用上面的例子,显然数组的下标是0~9
int arr[100]; 下标是0~99
int arr[4]; 下标范围是0~3
如果引用数组元素时,下标超过了其表达范围,编译器在编译的时候是不会报错的,
但是执行结果时不确定,即有可能符合正确结果,或者直接报内存错误,总之结果
都是错误的,或者说是程序都是错误的。
一般习惯管超过下标所能表达范围时叫做越界,越界的程序结果是不可预测的。保
证程序不越界是程序员的责任。
5)数组名字:数组的名字是一个常量,即首元素的地址。
6)数组存储特性:
数组元素的地址在内存存储时是连续的。

二 指针(间接访问)

指针即内存地址,硬件厂商在做内存的时候,都是按照一定规律来制作内存的内存可以抽象为一排即有门牌号,又满足一个门牌号对应一个大小确定的存储空间。而作为门牌号的地址都是特殊的常量(4G大小内存可以表示的范围是0x0~0xffff ffff ffff ffff),而专门存储这些常量的变量被称为指针变量
注意:指针变量与普通变量的区别是指针变量存储的值只能是地址常量
1)type * 指针变量名(声明了type类型的一个指针);
(1)类型:可以是基本数据类型,例如,char ,int ,float,double
也可以是构造类型,例如:struct student ,union mydir等等
(2)指针变量名:符合c语言标识符规范
(3)指针变量所占内存大小
2)声明一个整型指针
int *p;
int * 是一个类型,即整型指针类型。
p是一个变量,对应的有它自己所占用内存。而p既然是一个指针变量,那么
该指针变量所存储的值是一个地址,而且只能是一个地址。
例如:
int n=10;
int *p=&n; //与一个数据类型共同构成指针类型,仅仅只是一个标识符号
p=5; //在这里是一个间接取值运算符
//n=5;
通过指针途径修改一个变量的值,效率不如直接通过变量名修改。因为前者还牵涉到一个间接取值的运算。这里仅仅只是为了讲解指针的用法,仅仅修改变量的值显然不是指针的主要用法,指针的主要用法是可以指向内存地址或者说是在允许范围内指针可以自由的操作自己的内存空间。
注意:在声明一个指针变量的过程中所用的
与通过指针间接取用数值时候的
意义是不同的。后者是一个间接取值运算符,而前者充其量仅仅只是一个类型说明符的一部分。

三 结构体

1)结构体声明(结构体模板)
#define LENGTH 20
struct book
{
char name[LENGTH] ;
char author[LENGTH];
float price;
int edition;
};
注意:程序在运行的时候,结构体模板是不占内存的
2)声明结构体变量:结构体关键字+结构体名字=结构体类型名字
struct book 是一个结构体类型,那么声明一个结构体变量如下:
struct book my_book;
my_book就是一个结构体变量,他是有对应的内存空间的,其实这个结构体是关于一本书的信息描述,并且有自己的内存空间,可以把这个变量当成一个对象。结构体变量所占内存大小并非是各个成员类型所占内存大小的和,需要通过sizeof运算符来进行测量。
struct book *book_pointer;
book_pointer是一个结构体指针变量,该指针变量只有存储一个内存地址
大小的空间
3)结构体成员访问
结构体成员访问方法与数组引用元素有些类似。数组是通过下标来引用对应的元素,而结构体是通过模板中的成员名字来引用。
引用的时候可以如下:
结构体变量引用成员和(.)运算符
struct book my_book;
my_book.name
my_book.author
my_book.price
my_book.edition
结构体指针引用成员和(->)运算符
struct book *book_pointer;
book_pointer->name
book_pointer->author
book_pointer->price
book_pointer->edition
数组是同种类型数据的集合,而结构体是各种类型数据的集合

一 隐式类型转换,强制类型转换

1>根据不同类型在内存中所占内存大小而进行默认的一个转换
int short int long int long long int float (double)
系统默认的把一个所占内存空间小的类型的数据拷贝给了所占内存空间大的类型的数据
2>有时需要将所占内存大的类型的数据赋值给所占内存小的类型的变量。这就需要用到强制类型转换。例如:
double num=4.5;
(int)num; //这个结果是整数类型,舍掉小数点后的部分。
注意:虽然(int)num是一个整型,但是num还是原来的类型和原来的数据
即强制类型转换不会改变被强制类型转换的数据。

二 排序算法

1)冒泡排序法:相邻的两个元素进行比较,若前者比后者大则交换这两个数的位置,利用上述比较方法先找到所有数中最大的数放在最后一个元素的位置。然后再从剩余的元素当中选出来最大的数放到倒数第二个元素的位置。以此类推直到把所有元素拍完序。

2)选择排序法:第一次找出所有数中最大的数并记录下标,如果下标与刚开始的时候不一样则拿这个最大的数与第一个位置的元素交换。然后在剩余的元素中再找出最大的数并记录下标,若下标变动则与第二个位置的元素交换位置。以此类推直至把整个数组所有元素排完序为止。

3)插入排序法:把一个数插入到一个有序数组的合适位置,刚开始把第一个元素当作一个有序的数组,然后把第二个数插入到这个有序数组中。然后把第三个数插入到前两个数构成的有序数组当中适当的位置。依此类推,直至全部拍完序为止。

三 函数

1)函数的三个要素:
返回值 函数名 函数参数列表
int main (int argc, char argv[])
(1)函数名:遵守标识符规范
(2)函数参数列表:就是函数从它的调用函数获得该函数所需要的信息
(3)函数的返回值:就是该函数把函数执行的结果返回给调用它的函数
2)函数在调用之前必须声明,函数声明之前需要定义函数,然后才可以调用该函数
#include<stdio.h>
void my_print(void); //函数的声明
int main(void)
{
my_print(); //函数的调用
return 0;
}
void my_print(void) //函数的定义
{
int i,j;
for(i=0; i<5; i++) {
for(j=0 ; j<=i; j++) {
putchar(‘
’);
}
putchar(‘\n’);
}
}
(1)函数的声明当然跟变量声明有些类似,不过函数的声明一般放在main函数前面, 而不是放在main函数里面调用语句之前。void my_print(void);
注意:函数的声明比较特殊,在有参数的情况下,在声明的时候,参数变量名字随便写,因为它既没有分配空间也没有其它用处,也完全可以不写。函数在声明的时候参数列表的作用就是告诉编译器有几个参数,每一个参数都是什么类型。
(2)函数的定义:函数肯定会具有一定的功能,而这些功能的实现离不开具体的代码
实现,而这个代码的具体实现就是函数实体
void my_print(void)
{
函数实体(或者叫函数功能的具体代码实现)
}
这就是一个函数的定义
(3)函数的调用:就是上面例子中main函数中那一条语句:
my_print();

注意:如果函数有参数,那么当该函数在调用时才会有函数栈空间,而函数栈空间包含参数变量所占内存空间。所以说只有函数在调用时,参数变量才分配了内存空间。
3)一般函数形式 :
(1)最简单的一种函数类型
void my_function(void)
(2)有参数,无返回值的函数
void my_function(int n)
(3)有参数有返回值的函数
int my_function(int a, int b)
4)形式参数:在函数定义的时候,参数列表里的参数变量叫做形参
实际参数:在调用函数的时候,在参数列表位置所写的参数表达式叫做实参
5)变量的作用域:
当一个函数在调用的时候会单独的开辟一块新的内存空间,这个内存空间被叫做这个函数的一个栈。而当函数调用完成后,该栈内存控制权交给操作系统,调用函数已经无权管理这块内存空间了。
对于一个自定义函数,其变量仅仅只在被调用的时候在内存中占有空间,被调用前或者被调用后都不占有空间。函数仅仅只在被调用的那一个时间段,是该函数变量的生存周期该函数的作用域是该函数存在的时候整个函数内部。
6)返回值关键字return
(1)把有用的信息返回给当前函数的调用函数
(2)表示当前程序结束,把程序的控制权从当前函数转换到当前函数的调用函数。
7)递归函数:自己调用自己的函数

一 表达式和语句,关系运算符和逻辑运算符

1)表达式是由运算符和操作符组合构成的,简单表达式能够构成复杂表达式 下面列举的都是表达式
常量表达式:5 该表达式值为5
加法表达式:5+4 该表达式值为9
赋值表达式:n=4+5 该表达式值为9
关系表达式:9>5 该表达式值为1
逻辑表达式:5>4&&4<3 该表达式值为0
注意:每一个表达式肯定有一个确定的值
2)语句:表达式后面加一个分号就构成了语句,不是所有语句都合法 一个语句是一条完整的计算机指令
注意: 语句有单条语句和复合语句之分,复合语句是多条语句用花括号括起来,功能相当于单条语句。
3)关系运算符:
关系表达式为真,值为1;关系表达式为假,值为0. 即表达式的值非0即1,非1即0。
4)逻辑运算符
逻辑运算符的值可以为0或者非0
(1) &&
有一个表达式为假,整个复合表达式的为假,即值为0。
所有表达式都为真,整个复合表达式的为真,即值为非0.
(2) ||
有一个表达式为真,整个复合表达式就为真,即值为非0
所有表达式都为假,整个复合表达式才为假,即值为0
(3) ! 逻辑非
非假即为真
非真即为假

二 分支结构

程序在执行的过程中所有语句都遵循一个原则:
源程序自上而下自动执行每一条语句,这是计算机设计的时候就决定的。顺序结构是程序执行的基础,分支结构与循环结构是建立在顺序执行结构之上的。
1)if(表达式)
{
statements;
}
表达式为真,则执行statements,执行完statements,即结束判断语句,
继续执行下面的语句。
若表达式为假,不执行statements而继续执行if判断语句下面的语句
2)if(表达式)
{
statements1;
}
else
{
statements2;
}
表达式为真则执行statements1,否则执行statements2,而且只能执行
两者其中之一,绝不可能两条语句都执行。
statements1或者statements2执行完其中之一后还要继续执行if判断语
句的下一条语句。
4)switch(表达式) 表达式是一个可变的
{
case 常量表达式1:
statements1;
break;
case 常量表达式2:
statements2;
break;
:
:
:
default:
statements_default;
}
扫描与表达式匹配的项,一旦匹配执行后面对应语句,执行完直接退出
switch结构,如果没有找到与表达式匹配的项,就执行default那个标
签对应的语句,之后退出switch结构。
注意:
(1)表达式必须是可变的
(2)case后面的标签必须是常量
(3)每一个常量标签后面都有一个冒号(:)
(4)每一个标签后面语句都要有一句break;
不用break;有时会很有用,看情况要不要break;
一般情况下执行完要退出switch的只能用break来退出。如果没有break,那么switch会先查找与表达式匹配的标签之后不再检 查标签是否与表达式匹配而顺次执行下去,直到执行完整个switch剩下的部分。有时我们也可以利用switch这个特性来实现我们的代码功能。
(5)default语句就相当于if…else if…else结构里的最后一个else,对选项的逻辑完整性有一定的作用,一般写上为好。

三 循环控制结构:已明确知道执行循环次数和不确定具体执行循环次数

1)for(表达式1;表达式2;表达式3)
{
statements;//循环体
}
for循环的有点:不容易漏掉一些必须的条件。
表达式1:仅仅只在进入循环的时候执行一次,给计数器赋一个初值
表达式2:每一次进入循环体执行代码的之前,都要判断该表达式是否为真
若为真则进循环,否则结束循环
表达式3: 每成功执行一次循环体,循环计数器都要递变以趋近于循环结束
注意:步长的增加和循环结束判断条件都可以很灵活的使用。

2)while(表达式)

{
statements;//循环体
}
如果表达式为真,则进入循环执行循环体,否则结束循环
注意:
(1)循环在开始前,要给循环作个铺垫。例如,确定循环的次数,我还需要知道循环计数器是从那个数值开始的
(2)循环体内必须要包含让while循环趋近于结束循环的递变

3)do

{
statements;//循环体
}while(表达式);
do…while()语句与while()循环功能差不多,的不同是:do…while()是一个出口条件判断循环,这就导致do…while结构在第一次执行循环体的时候是没有经过任何条件判断的,即第一次循环肯定执行一次。而while()循环是一个入口条件判断循环,即在每一次执行循环体之前都要做一次判断。
注意:do…while()循环后有分号,这点跟while()和for循环都是不一样的。

4)break和continue

(1)break;可以跳出(结束)switch()或者循环控制结构,下面看一个break退出循环

经验之谈:要以计算机的思维模式去思考怎么写出符合需求的代码