C语言是一门高级语言,是一门结构化语言,是面向过程的。
下面的试验均基于Dev-C++
面向过程:分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一再依次调用,面向过程以步骤划分问题
面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。面向对象以功能划分问题
变量本质上是内存中的一段存储空间
定义一个变量,相当于请求操作系统将内存中某一块空间的使用权分配给该变量
变量如果不初始化使用,将会保留原来该区域中遗留的“垃圾数据”
常量以二进制代码存储在计算机中
#incude
int main()
{
float i = 99.9;
printf("%f",i);
return 0;
}
字符本质上与整数的存方式相同(ASCII)
#include
int main()
{
int i;//定义一个变量
//printf可以直接输出字符串
printf("请输入一个数:");
//scanf是C语言中与用户交互的函数,会等待用户输入,具体用法:
scanf("%d",&i);
/*
“”号中的%d是输出控制符指以十进制类型输入
&是取地址符,意为将输入的值赋给上面定义的变量i
“”中不要加其他字符,否则输入时,要原样输入
*/
//printf也可以格式化输出
printf("这里将原样输出----%d",i);//以十进制方式输出i
return 0;
}
控制符 | 类型 |
---|---|
%d | int |
%ld | long int |
%c | char |
%f | float |
%lf | double |
%x或%X或%#x或#X | 16进制输出 |
%o | 8进制输出 |
%s | 字符串 |
test
#include
int main(void){
int i = 46;
printf("%x\n",i) ;
printf("%X\n",i) ;
printf("%#x\n",i) ;
printf("%#X\n",i) ;//?
int j = 10;
printf("%o\n",j);
char str[4] = "ABC";
printf("%s\n",str);
return 0;
}
为了提升效率
&&左边为假,右边的表达是不会执行
||左边为真,右边的表达式不会执行
除法/的运算结果和运算对象的类型有关,int/int的结果还是int,换句话说,小数部分被舍弃
要想保留小数部分,需要让除数或被除数为浮点型
取余%的运算对象必须是整数,结果是整除后的余数,其余数的符号和被除数相同
——任何复杂的算法,都可以由顺序结构、选择(分支)结构和循环结构三种基本结构组成
顺序:程序中的各个操作是按照它们在源代码中的排列顺序依次执行的;
选择:根据条件,选择一些代码执行,另外的代码不执行
#include
int main(void){
float s;
printf("请输入分数:");
scanf("%f",&s);
if(s>=90)
printf("%c",'A');
else if(s >= 80)
printf("%c",'B');
else if(s >= 70)
printf("%c",'C');
else if(s >= 60)
printf("%c",'D');
else
printf("%c",'E');
return 0;
}
#include
int main()
{
float s;
printf("请输入分数:");
scanf("%f",&s);
switch((int)(s/10))
{
case 10://这里没有break,就会一直执行到下一个break,这里要注意,之前有错过
case 9:
printf("%c",'A');
break;//出口
case 8:
printf("%c",'B');
break;//出口
case 7:
printf("%c",'C');
break;//出口
case 6:
printf("%c",'D');
break;//出口
default:
printf("%c",'E');
break;//出口
}
return 0;
}
/*
switch语句规则:
1、switch语句非常有用,但在使用时必须谨慎。所写的任何switch语句都必须遵循以下规则:
2、只能针对基本数据类型中的整型类型使用switch,这些类型包括int、char等。对于其他类型,则必须使用if语句。
3、switch()的参数类型不能为实型 。
4、case标签必须是常量表达式(constantExpression),如42或者'4'。
5、case标签必须是唯一性的表达式;也就是说,不允许两个case具有相同的值。
*/
单层循环:求1-100的和
#include
int main()
{
int sum = 0;
for(int i = 1;i<=100;i++)
{
sum += i;
}
printf("%d",sum);
return 0;
}
/*
result:5050
*/
嵌套循环:输出:
*
***
*****
#include
int main()
{
for(int i = 0;i < 3;i++)
{
if(i!=0)
{
printf("\n");
}
for(int k = 0;k<3-1-i;k++)
{
printf(" ");
}
for(int j = 0;j<2*(i+1)-1;j++)
{
printf("*");
}
}
return 0;
}
while循环和for循环可以互相转换
1;
while(2)
{
A;
3;
}
for(1;2;3)
{
A;
}
/*
1一般只执行一次
2成立执行A
A执行后执行3
3成执行后执行2
2不成立循环结束
*/
/*
do while 和for、while不一样,他至少执行一次
*/
do{
...
}while(表达式)
break如果用于循环是用来终止循环,在多层循环中, break只能终止最里面包裹它的那个循环
break如果用于 switch,则是用于终止 switch
break不能直接用于if,除非if属于循环内部的一个子句
continue用于跳过本次循环余下的语句,转去判断是否需要执行下次循环
while((ch = getchar())!='\n')
continue;
/*
该语句为了屏蔽用户的某些非法输入
比如在前面输入了111abd
在执行了111还有残留的abd,可能对下一次的输入产生影响,该语句可屏蔽该影响
*/
——为了解决大量同类型数据的存储和使用问题
/*
一维数组
为n个变量连续分配存储空间
所有变量的数据类型必须相同
所有变量所占字节大小必须相同
数组名表示数组第一个元素的地址
构建静态数组时,[]中不能写变量,也就是说不能通过用户输入的值来确定数组长度
*/
int a[5];//声名数组,不初始化,所有元素都是垃圾值
int a[5] = {1,2,3}//不完全初始化,其他变量是0
int a[5] = {1,2,3,4,5};//声名并完全初始化
int a[5] = {0};//清零
/*
二维数组
int a[3][4];//可以看作3行4列
*/
/*
多维数组
计算机的内存是线性一维的
严格意义上不存在多维数组
n维数组可以当做每个元素是n-1维数组的一维数组
*/
#include
#include
int main()
{
int len;
int * pArr;
int i;
//静态构造一维数组
int a[20];//如果int占4个字节,则本数组共包含有20个字节,每四个字节当作一个int变量使用
//动态的构造一维数组
printf("请输入要存放元素的个数:");
scanf("%d",&len);
pArr = (int *)malloc( sizeof(int) * len); //等同 int pArr[len] 本行 动态的构造了一个一维数组
for(i = 0;i
——为了避免重复性的操作,有利于程序的模块化
/*
函数的使用,输出1-100内的素数
*/
#include
#include
//定义判断素数的函数
bool JudgePrime(int num)
{
for(int i = 2;i<=sqrt(num);i++)
{
if(num%i==0)
{
return 0;
}
}
return 1;
}
//给定一个整数,输出1-该整数中所有的素数
void Traverse(int N)
{
for(int i = 2;i
——指针可以用于表示一些复杂的数据结构(树、图)
——快速的传递数据,减少内存的耗用
——使函数放回一个以上的值
——能直接访问硬件
——能够方便的处理字符串(\0休止符的存在)
——理解面向对象语言中引用的基础
指针就是地址,所谓地址是内存单元的编号,范围[0,内存大小(比如8G)-1]
指针变量和指针不一样,指针变量是存放指针(地址)的变量,指针变量的占字节的大小与类型无关,与操作系统位数和编译器环境相关,32位4个字节,64位8个字节。
指针的本质是一个操作受限的非负整数
#include
#include
int main(void)
{
int * p;// 定义指针变量p,指针变量p存放的是----int类型----的----地址---现在p放的是垃圾值
int i = 5;//将5赋给系统分配的名称为i的内存空间
p = &i; //这里是第8行,将i的地址赋给指针变量i &是取地址符,能够获得变量的内存单元的编号
*p = i;
/*
若第8行被注释掉,这里的写法是危险的,因为将i赋给了未经系统分配的内存空间
若第8行没被注释,这里指将i赋给以p的内容为地址的变量即i
*/
/*
p保存了i的地址,所以p指向i
p和i不同,p相当于复制的i的地址,修改p的值不影响i的值,修改i的值不影响p的值
如果一个指针变量指向一个普通变量 则
*指针变量 与该普通变量等价
eg:
如果p是个指针变量,并且p保存了普通变量i的地址,则
p指向了普通变量i
*p 完全等同于 i
或者说:
在所有出现*p的地方可以替换成i
同理
在所有出现i的地方可以替换成*p
*p中的*相当于取地址符的逆操作,换句话说就是以p的内容为地址的变量
指针VS指针变量
指针:地址【内存单元中具体的(编号)值】
指针变量 :存放指针的变量
*/
printf("%d",*p);//若第八行没有被注释,这里将输出5
return 0;
}
指针和一维数组
数组下标和指针的关系
如果p为指针变量,则p[i]等价于*(p+i)
确定一个一维数组需要两个参数
#include
int main()
{
int a[5] = {1,2,3,4,5};//定义一个数组,并初始化
int * p = a;//将a赋给p,如果a是地址的话,这里才不会报错,相同类型的变量才能互相赋值
//如果,上面没有问题,这里验证*(p+i)与a[i]是否等价
for(int i = 0;i<5;i++)
{
printf("a[%d]:%d\n",i,a[i]);
printf("*(p+%d):%d\n",i,*(p+i));
printf("-----------\n");//无意义,为了看的清楚点
}
return 0;
}
#include
/*
以下函数不能完成互换功能
R:虽然都叫变量a,和变量b
但是函数里的a,b与主函数的不同
形参和实参是不一样的!!!!!!!
*/
void SwitchAB_1(int a,int b)
{
int t;
t = a;
a = b;
b = t;
return;
}
/*
以下函数不能完成互换功能
只是互换p,q的内容,对a,b没有影响
要根据p,q的 内容,找到它们对对应的变量,并互换变量内的值
*/
void SwitchAB_2(int * p,int * q)
{
int * t;
t = p;
p = q;
q = t;
return;
}
//可以完成互换
void SwitchAB_3(int * p,int * q)
{
int t;
t = *p;
*p = *q;
*q = t;
return;
}
//可以完成互换
void SwitchAB_4(int &a,int &b)
{
int t;
t = a;
a = b;
b = t;
return;
}
int main(void)
{
int a=5,b=6;
int t;
//下面3行是主调函数内完成的互换
t=a;
a=b;
b=t;
printf("正常的互换:a:%d b:%d\n",a,b);//a:6 b:5
SwitchAB_1(a,b);
printf("later1 a:%d b:%d\n",a,b);//a:6 b:5 失败
SwitchAB_2(&a,&b);//SwitchAB_2(a,b)是错误的
printf("later2 a:%d b:%d\n",a,b);//a:6 b:5 失败
SwitchAB_3(&a,&b);
printf("later3 a:%d b:%d\n",a,b);//a:5 b:6 成功
SwitchAB_4(a,b);
printf("later4 a:%d b:%d\n",a,b);//a:6 b:5 成功
return 0;
}
int test(int * p, int * q){
int t;
t = *p;
*p = *q;
*q = t;
return;
}
指针变量的加法,乘法,除法没有意义
如果两个指针变量指向的是同一块连续空间中的不同存储单元,那么它们相减意味着隔了”多远“
注意:
#include
int main()
{
int i = 6;
int * p = &i;//p只能存放int类型变量的地址
int ** q = &p;//q只能存放int * 类型变量的地址 那么*q == p **q == i 地址的地址
int *** r = &q;//r只能存放int ** 类型变量的地址 地址的地址的地址 //禁止套娃 ε(┬┬﹏┬┬)3
printf("***r:%d **q:%d *p:%d",***r , **q, *p);
return 0;
}
malloc函数的用法
#include
#include
/*
malloc (memory allocate 内存分配)函数的用法
int * p = (int *)malloc(int len); //事实上 ,int len为保证安全,更常写为:sizeof(int)*len
malloc 函数的功能是请求系统分配len个字节的内存空间,
成功则返回第一个字节的地址,失败则返回NULL
由于为了保证变量的完整性,需要强制类型转换,前面加(int *),以具体划分
比如 double * p = (double *)malloc(80);
这里向系统请求分配80个字节的内存空间,并将第一个字节的地址返回给p,
同时又指定了double * 类型,系统就知道从第一个地址开始后续的8个字节作为一个整体
即p 指向了第一个 8个字节,p+1指向了第二个 8个字节...
*/
int main(){
int 5;//静态分配了5个字节
int * p = (int *)malloc(4);//这里共分配了8(p本身 64位环境)+4 = 12个字节
/*
1.要使用malloc函数,必须添加malloc.h头文件
2.malloc只有一个形参,并且形参为整形
3.4表示请求系统为该函数分配4个字节
4.malloc函数只能返回第一个字节得地址
5.所以要通过指定类型来指定第一个字节得地址是什么类型的地址
6.p本身所占得内存是静态分配得,而p指向的内存是动态分配的
*/
*p = 5;
//动态分配的可以手动释放
free(p);//表示把p指向的内存给释放掉 p本身的内存是静态的,不能由程序员释放,只能在函数执行完后,自动释放
return 0;
}
静态变量不能跨函数使用内存
#include
void f(int ** q)//q是个指针变量,无论是 什么类型的指针变量,都只占8个字节(64 位环境)
{
int i = 5;//静态变量不能跨函数使用,因为它们存储在栈中,出栈意味着,资源被释放,也就没有读写权限
//*q等价于p
*q = &i;//p = &i;
}
int main()
{
int * p;
f(&p);
printf("%d\n",*p);//这里逻辑上存在问题,i在f函数执行完后就已经释放了,*p访问了一个没有权限的变量
return 0;
}
动态变量跨函数使用内存
#include
#include
void f(int ** q)
{
*q = (int *)malloc(sizeof(int)*1);//动态分配的变量在堆内,函数执行后,不会被释放
**q = 5;
}
int main()
{
int * p;
f(&p);
printf("%d\n",*p); //输出为5
return 0;
}
总结
——为了表示基本数据类型无法表示的事物,比如学生,他可以有姓名,性别,分数等,需要多个变量综合起来才能描述的事物
——结构体,是一些基本类型数据组合起来形成的新的复合数据类型
#include
//define 结构体的第一种方式 推荐 仅仅定义了一个新的数据类型,并没有实例化
struct Student
{
int age;
float score;
char sex;
};
//define 2
struct Student2
{
int age;
float score;
char sex;
}st2 ;
//define 3
struct
{
int age;
float score;
char sex;
}st3;
int main()
{
struct Student st1 = {80,66.6,'F'};//结构体的赋值和初始化(定义的同时赋初值)
struct Student st2 ;
//结构体访问成员变量 1 结构体变量名.成员名
st2.age = 66;
st2.score= 90;
st2.sex = 'M';
printf("%d %f %c\n",st1.age,st1.score,st1.sex);
printf("%d %f %c\n",st2.age,st2.score,st2.sex);
//结构体访问 2 指针变量名->成员名 用的多
struct Student * pst = &st1;//因为st1 是Student类型则,指向它的指针也是该类型
pst->score = 66.7f;//pst->age在计算机内部会被转换成(*pst).age pst->age = st.age 66.7在C语言中默认是double类型
printf("%f ",st1.score) ;
//pst->age 含义:pst所指向的那个结构体变量中的age成员
return 0;
}
#include
#include
//定义Student结构体,由age,score,name 属性
struct Student
{
int age;
float score;
char name[100];
};
//输入学生信息
void InStInfo(struct Student * p,int i)
{
printf("请输入第%d个学生的信息:\n",i+1);
printf("age : ") ;
scanf("%d",&p[i].age);
printf("name : ") ;
scanf("%s",p[i].name);//name 本身是数组名,所以在scanf 函数里不需要加取地址符
printf("score : ") ;
scanf("%f",&p[i].score);
}
//输出学生信息
void OutStInfo(struct Student * p,int i)
{
printf("第%d个学生的信息:\n",i+1);
printf("age : %d\n",p[i].age) ;
printf("name : %s\n",p[i].name) ;
printf("score : %f\n",p[i].score) ;
printf("\n");
}
//通过冒泡排序按学生升序排
void sortUp(struct Student * p,int * len)
{
for(int i = 0;i<*len;i++)
{
for(int j = 0;j<*len-1-i;j++)
{
if(p[j].score>p[j+1].score)//根据学生成绩高低,给学生整体排序,而不是单纯的换成绩本身,那没有意义
{
struct Student t;//换的是学生,所以变量类型是struct Student类型
t = p[j];
p[j] = p[j+1];
p[j+1] = t;
}
}
}
}
int main()
{
int len;
struct Student * pArr;
printf("请输入学生的个数:\n");
printf("len = ");
scanf("%d",&len);
pArr = (Student *)malloc( len * sizeof(Student) );
for(int i = 0;i < len;i++)
{
InStInfo(pArr,i);
}
//排序
sortUp(pArr,&len);
//输出
printf("\n\n学生的信息如下:\n\n");
for(int i = 0;i
——把所有事物的取值意一一列举出来