编程是一种控制计算机的方式,和我们平时双击打开文件、关机、重启没有任何区别——闫学灿
我们先看看上边的那个代码的结构,我们用左边的行号来表示第几行
1、第1行:#include<stdio.h>
这一行称之为头文件,其结构形式为#include<中间填调用的头文件>
这里我们调用的头文件为stdio.h
,这个头文件中包含着最常用的函数
头文件中包含着特定的函数,函数调用的结构为函数名+(),这里调用的是printf()函数,作用是输出特定数值。
如果需要使用特定的函数,就需要加入特定的头文件,一个头文件中包含着多个函数
常见头文件: math.h
, algorithm
, stdio.h
2、第3行:int main()
这是主函数,所有的程序都是从主函数开始,也是从主函数结束,函数的内容使用大括号{}
包裹起来
每个c语言代码都要包含这个函数
3、第5行:printf("Hello world\n");
这是一个==语句==,调用了头文件stdio.h
中的printf函数。
printf函数在这里的使用:
输出字符串(一串字符),使用双引号扩起来。末尾的
\n
,表示换行符,下边我们会讲到。
比较两个代码输出的异同
#include<stdio.h>
int main()
{
printf("hello world");
printf("你好世界");
return 0;
}
#include<stdio.h>
int main()
{
printf("hello world\n");
printf("你好世界");
return 0;
}
==语句==的特点:要以;
结尾。注意:c语言中所有的符号均为英文符号,使用中文符号代码将无法运行
4、第6行:return 0;
函数的返回值,除了void的类型的函数(这里的main函数是int类型函数)其余函数都需要返回值,这个知识点我们会在==函数==一节中详细讲解
5、第8行://该程序用于输出字符串Hello World
这一行为==注释==,注释的内容将不会被编译器识别,也就是说,注释中写任何内容都不会影响程序的运行
注释的类型:
单行注释:
//注释内容
多行注释:
/*注释内容
注释内容
注释内容*/
如上:该代码的每一行均已讲解完成
(1)、C语言中的数据类型:
1、整型:表示整数的数据类型
数据类型 | 占用空间 | 取值范围 |
---|---|---|
short(短整型) | 2字节 | -2^15^~2^15^-1 |
int(整型) | 4字节 | -2^31^~2^31^-1 |
long(长整型) | 4字节 | -2^31^~2^31^-1 |
long long(长长整型) | 8字节 | -2^63^~2^63^-1 |
int是最常用的整型
2、实型(浮点型):表示小数的数据类型
数据类型 | 占用空间 | 有效数字范围 |
---|---|---|
float | 4字节 | 7位有效数字 |
double | 8字节 | 15~16位有效数字 |
double是最常用的整型
3、字符型:表示单个字符的数据类型
数据类型 | 占用空间 |
---|---|
char | 1字节 |
4、布尔类型:表示正确或错误的数据类型
数据类型 | 占用空间 |
---|---|
bool | 1字节 |
bool型存在两种,true(1)
和flase(0)
,分别表示正确和错误
其他相关:
转义字符:
转义字符 | 含义 | ASCII码值(十进制) |
---|---|---|
\a | 警报 | 007 |
\b | 退格(BS),将当前位置移到前一列 | 008 |
\f | 换页(FF),将当前位置移到下页开头 | 012 |
\n | 换行(LF),将当前位置移到下行开头 | 010 |
\r | 回车(CR),将当前位置移到本行开头 | 013 |
\t | 水平制表(HT)(跳到下一个TAB位置) | 009 |
\v | 垂直制表(VT) | 011 |
\ | 代表一个反斜字符“\” | 092 |
\’ | 代表一个单引号(撇号)字符 | 039 |
\” | 代表一个双引号字符 | 034 |
? | 代表一个问号 | 063 |
\0 | 数字0 | 000 |
\ddd | 8进制转义字符,d范围0~7 | 0位8进制 |
\xhh | 16进制转义字符,h范围09,af,A~F | 3位16进制 |
sizeof
关键字
如果忘了数据类型的大小,就可以使用sizeof(数据类型\变量)
来查看,显示的是字节数
#include<stdio.h>
int main()
{
int a;
printf("%d\n",sizeof(int));
printf("%d\n",sizeof(a));
return 0;
}
(2)、变量和常量
变量是可变的量,其创建语法为数据类型 变量名;
或者数据类型 变量名 = 变量初始值;
变量在创建之后可以再通过赋值改变其存储的值
#include<stdio.h>
int main()
{
int a;
int b = 10;
a = 5;
printf("%d\n",a);//先不用管括号中的含义,这里的意思是输出a,下边一行是输出b
printf("%d\n",b);
return 0;
}
常量是创建之后就不可更改的量,所以常量的创建一定要对其赋予初始值
常量创建有两种方式:
1、#define宏常量:#define 常量名 常量值
2、const修饰的变量:const 数据类型 常量名 = 常量值;
#include<stdio.h>
int main()
{
#define a 10
const int b = 20;
printf("%d\n",a);
printf("%d\n",b);
return 0;
}
(3)、标识符(变量和常量名和函数名等)的命名规则
-
标识符不能是关键字
-
标识符只能由字母、数字、下划线组成
-
第一个字符必须为字母或下划线
-
标识符中字母区分大小写
注意:命名争取做到顾名思义的效果,方便阅读。
c语言中的格式符:
%d整型输出,%ld长整型输出,
%o以八进制数形式输出整数,
%x以十六进制数形式输出整数,或输出字符串的地址。
%u以十进制数输出unsigned型数据(无符号数)。
%c用来输出一个字符,
%s用来输出一个字符串,
%f用来输出实数,以小数形式输出,默认情况下保留小数点6位。
%.100f用来输出实数,保留小数点100位。
%e以指数形式输出实数,
%g根据大小自动选f格式或e格式,且不输出无意义的零。
常用的就是%d,%lf,%c,%s
这四个
输入:scanf("格式符",&变量名);
#include<stdio.h>
int main()
{
int a;
double b;
scanf("%d%lf",&a,&b);
return 0;
}
输出:printf("格式符",变量名);
#include<stdio.h>
int main()
{
int a;
double b;
scanf("%d%lf",&a,&b);
printf("%d %f",a,b);//两数之间会有一个空格
return 0;
}
1.算数运算符
算法运算符包含以下符号:
运算符 术语 示例 结果 + 正号 +3 3 - 负号 -3 -3 + 加 10+5 15 - 减 15-5 5 * 乘 10*5 50 / 除 10/5 2 % 取模(取余) 10%3 1 ++ 前置递增 a=2;b=++a; a=3;b=3; ++ 后置递增 a=2;b=a++; a=3;b=2; -- 前置递减 a=2;b=--a; a=1;b=1; -- 后置递减 a=2;b=a--; a=1;b=2;
#include<stdio.n>
int main()
{
int a = 10,b = 5,c = 3;
double d = 3.14,e = 5.34;
int num1 = a - b;
double num2 = e - d;
double num3 = a / 3;
printf("%d %f %f\n",num1, num2, num3);
int num4 = a % 10;
printf("%d\n",num4);
return 0;
}
#include<stdio.h>
int main()
{
int a = 10;
printf("此时a的值为:%d\n",a);
a++;
printf("此时a的值为:%d\n",a);
a--;
printf("此时a的值为:%d\n",a);
++a;
printf("此时a的值为:%d\n",a);
--a;
printf("此时a的值为:%d\n",a);
return 0;
}
前置与后置的区别
#include<stdio.h>
int main()
{
int a = 10;
printf("此时a的值为:%d\n",a++);
printf("此时a的值为:%d\n",a);
printf("此时a的值为:%d\n",++a);
return 0;
}
2.赋值运算符
赋值运算符包括以下几个符号:
运算符 | 术语 | 示例 | 结果 |
---|---|---|---|
= | 赋值 | a=2;b=3; | a=2;b=3; |
+= | 加等于 | a=0;a+=2; | a=2; |
-= | 减等于 | a=2;a-=3; | a=-1; |
*= | 乘等于 | a=2;a*=2; | a=4; |
/= | 除等于 | a=4;a/=2; | a=2; |
%= | 模等于 | a=3;a%=2; | a=1; |
#include<stdio.h>
int main()
{
int a = 10;//赋值
printf("此时a的值为:%d\n",a);
a += 5;
printf("此时a的值为:%d\n",a);
a *= 3;
printf("此时a的值为:%d\n",a);
a /= 3;
printf("此时a的值为:%d\n",a);
a %= 2;
printf("此时a的值为:%d\n",a);
return 0;
}
3.比较运算符
比较运算符有以下符号:
运算符 | 术语 | 示例 | 结果 |
---|---|---|---|
== | 相等于 | 4 == 3 | 0 |
!= | 不等于 | 4 != 3 | 1 |
< | 小于 | 4 < 3 | 0 |
> | 大于 | 4 > 3 | 1 |
<= | 小于等于 | 4 <= 3 | 0 |
>= | 大于等于 | 4 >= 3 | 1 |
比较运算符返回的是bool型数据
#include<stdio.h>
int main()
{
int a = 10,b = 10,c = 5;
printf("a == b:%d\n",a == b);
printf("a != b:%d\n",a != b);
printf("a > b:%d\n",a > b);
printf("a < b:%d\n",a < b);
printf("a >= b:%d\n",a >= b);
printf("a <= b:%d\n",a <= b);
return 0;
}
4.逻辑运算符
逻辑运算符有以下符号:
运算符 | 术语 | 示例 | 结果 |
---|---|---|---|
! | 非 | !a | 如a为真,则!a为假;如a为假,则!a为真 |
&& | 与 | a && b | 如a和b都为真,则结果为真,否则为假 |
|| | 或 | a || b | 如果a和b有一个为真,则结果为真,二者都为假时,结果为假 |
#include<stdio.h>
int main()
{
bool a = true,b = false;
printf("!a=%d\n",!a);
printf("a&&b=%d\n",a&&b);
printf("a&&a=%d\n",a&&a);
printf("b&&b=%d\n",b&&b);
printf("a||a=%d\n",a||a);
printf("a||b=%d\n",a || b);
printf("b||b=%d\n",b||b);
return 0;
}
程序流程结构分为三种:选择结构、循环结构和跳转语句
if语句
if(条件)
{
代码
}
else if(条件)
{
代码
}
else
{
代码
}
#include<stdio.h>
int main()
{
int a = 2;
if(a == 1)
{
printf("a=%d\n",a);
}
else if(a == 2)
{
printf("a=%d\n",a);
}
else
{
printf("a即不等于1也不等于2\n");
}
return 0;
}
三目运算符
表达式1?表达式2:表达式3
#include<stdio.h>
int main()
{
int a = 3;
a==3?printf("a==3"):printf("a!=3");
return 0;
}
switch语句
switch(表达式)
{
case结果1:执行语句;break;
case结果2:执行语句;break;
……
default:执行语句;break;
}
#include<stdio.h>
int main()
{
int a = 3;
switch(a)
{
case a== 1:
printf("a==1\n");
break;
case a==2:
printf("a==2\n");
break;
case a==3:
printf("a==3\n");
break;
default:
printf("a的值未知\n");
break;
}
return 0;
}
while循环
while(循环条件){循环语句}
#include<stdio.h>
int main()
{
int n = 10;
while(n--)
{
printf("%d\n",n);
}//输出10~0
return 0;
}
do……while循环
do{循环语句}while(循环条件);
#include<stdio.h>
int main()
{
int n = 10;
do{
printf("%d\n",n);
}while(n--);//输出10~0
return 0;
}
while和do……while的区别
- while是先判断循环条件再执行循环语句;do……while是先执行循环语句再判断循环条件
- do……while至少会执行一次
for循环
for(起始表达式;条件表达式;末尾循环体){循环语句;}
#incldue<stdio.h>
int main()
{
for(int i = 0;i < 10;i++)
{
printf("%d\n",i);
}//输出0~9
return 0;
}
嵌套循环
嵌套循环就是在循环中再套一层循环,下边是冒泡排序
#include<stdio.h>
int main()
{
int lenth = 10;
int a[10] = {9,8,8,10,4,6,1,7,5,3};
for(int i = 0;i < lenth - 1;i++)
{
for(int j = 0;j < lenth - 1 - i;j++)
{
if(a[j] > a[j + 1])
{
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
for(int i = 0;i < lenth;i++)
{
printf("%d "a[i]);
}
return 0;
}
跳转语句的作用在于跳出循环。
break语句
break
语句用于跳出循环结构
#include<stdio.h>
int main()
{
for(int i = 2;i <= 100;i++)
{
bool flag = 1;
for(int j = 2;j < i;j++)
{
if(i % j == 0)
{
flag = 0;
break;
}
}
if(flag)
printf("%d\n",i);
}
return 0;
}//输出100以内的素数(质数)
continue语句
continue
语句用于跳过本次循环
#include<stdio.h>
int main()
{
int res = 0;
for(int i = 1;i < 10;i++)
{
if(i == 3)
continue;
res += i;
}
printf("%d\n",res);
return 0;
}//求1,2,4,5,6,7,8,9的和
goto语句
这个不讲,没用
数组是一个存放了相同类型元素的集合
**特点1:**数组中每个数据元素都是相同的数据类型
**特点2:**数组是由连续的内存位置组成的
**特点3:**数组的下标是从0开始的
一维数组的定义方式
数据类型 数组名[ 数组长度 ];
数据类型 数组名[ 数组长度 ] = {值1,值2…};
数据类型 数组名[ ] = {值1,值2…};
一维数组定义
#incldue<stdio.h>
int main()
{
int a[5];//没有初始值的数组会被随机赋值
int b[5]={1,2,3,4,5};
int c[]={1,2,3,4,5};
for(int i = 0;i < 5;i++)
{
printf("%d ",a[i]);
}
printf("\n");
for(int i = 0;i < 5;i++)
{
printf("%d ",b[i]);
}
printf("\n");
for(int i = 0;i < 5;i++)
{
printf("%d ",c[i]);
}
printf("\n");
return 0;
}
一维数组的赋值
#include<stdio.h>
int main()
{
int a[5];
for(int i = 0;i < 5;i++)
{
a[i] = i;
}
for(int i = 0;i < 5;i++)
{
printf("%d ",a[i]);
}
printf("\n");
for(int i = 0;i < 5;i++)
{
scanf("%d",&a[i]);
}
for(int i = 0;i < 5;i++)
{
printf("%d ",a[i]);
}
printf("\n");
return 0;
}
字符串初识
顾名思义,字符串就是一串字符。数组的一个特点就是由连续的内存地址组成,所有字符类型的数组可以用于存储字符串。
#include<stdio.h>
int main()
{
char str[10];//最长可以储存9位长度的字符串
scanf("%s",str);
printf("%s\n",str);
return 0;
}
字符串输入的特殊之处:
其他数据类型 | 字符串类型 |
---|---|
scanf("%d",&a); | scanf("%s",a); |
在变量名前少了&
。
关于字符
&
:该字符用于取变量的内存地址,输入的时候是将值放入一个内存地址当中,所以在输入的时候需要加上&(关于这个知识点详细的可以去学学指针)
这就引出数组的一个特性,==数组命名代表着数组在内存中的首地址,故输入字符串不需要取值符&==。
关于字符串,我们会单独开一节来讲,这里知识简单的提及一下。了解了不用加&和想要存储n位长度的字符串就要开至少n+1长度的数组即可,后边会讲原因。
二维数组的创建就是数组名后两个中括号即可
#inlcude<stdio.h>
int main()
{
int a[5][5];
for(int i = 0;i < 5;i++)
{
for(int j = 0;j < 5;j++)
{
scanf("%d",&a[i][j]);
}
}
for(int i = 0;i < 5;i++)
{
for(int j = 0;j < 5;j++)
{
printf("%d ",a[i][j]);
}
printf("\n");
}
}
多加几个中括号就可以了
字符串就是字符的数组
字符串在字符数组中的储存方式:
例如,我们将Hello这个词存入到字符串数组str中,那么就会有:
下标 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
str | H | e | l | l | 0 | \0 |
我们在上边提到过,==想要存储n位长度的字符串就要开至少n+1长度的数组==,原因就在这里,字符串的最后一位存储着转义字符\0
,作为字符串的结尾。在字符串输出的时候,\0
不会输出。
字符串的创建与初始化:
char str[6] = "Hello";
(一定要用双引号)char str[] = "hello";
char str[6];
(在不进行初始化时,一定要规定长度)
字符串的输入:
scanf("%s",str);
字符串处理函数:
加入头文件string.h
==字符串连接函数==:strcat(字符串1,const 字符串2);
//将字符串2连接到字符串1的后边
==字符串复制函数==:strcpy(字符串1,cosnt 字符串2);
//将字符串2赋值给字符串1
==字符串比较函数==:strcmp(const 字符串1,const 字符串2);
//如果字符串1等于字符串2,函数值为0;如果字符串1大于字符串2, 函数值为一正整数;如果字符串1小于字符串2,函数值为一负整数。(比较字典序列大小)
==字符串长度函数==:strlen(const 字符串);
//函数值为字符串的实际长度,不包含转义字符\0
在内。
函数就是将一段代码封装起来,通常是封装一个程序中会重复使用的较长的代码,合理使用函数可以提高代码的精简度和可读性
返回值类型 函数名 (参数列表)
{
函数体语句;
return表达式;
}
返回值类型即为数据类型和一个空类型void(无返回值)
函数名(参数)
#inlcude<stdio.h>
int add(int num1,int num2)//两数之和
{
int num = num1 + num2;
return num;
}
int main()
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d",add(a,b));
return 0;
}
由于编译器在编译的时候是从上往下一行一行编译的,所以==函数必须要写在其调用前边==。
如果想要把函数写在后边就得先进行声明
#include<stdio.h>
int add(int num1,int num2);//函数声明
int main()
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d",add(a,b));
return 0;
}
int add(int num1,int num2)//两数只和
{
int num = num1 + num2;
return num;
}
值传递
调用形式参数(简称形参)的为值传递,只传递该参数的值,故在函数中调用形式参数时不会影响实际参数
#include<stdio.h>
int add(int a,int b)
{
a--,b--;
printf("传入参数的值为:a=%d b=%d\n",a,b);
return a + b;
}
int main()
{
int a = 5,b = 10;
printf("函数的返回值:%d\n"add(a,b));
printf("实际参数的值: a=%d b=%d\n",a,b);
}
地址传递
调用**实际参数(简称实参)**的为地址传递,地址传递会改变实参的值
#include<stdio.h>
int add(int &a,int &b)
{
a--,b--;
printf("传入参数的值为:a=%d b=%d\n",a,b);
return a + b;
}
int main()
{
int a = 5,b = 10;
printf("函数的返回值:%d\n"add(a,b));
printf("实际参数的值: a=%d b=%d\n",a,b);
}
为什么地址传递会改变实际参数的值?(指针初识)
当值传递时:
另外开辟了一个内存用于存储值,故形式参数改变时不会影响实际参数
当地址传递时:
地址传递时,参数直接指向了同样一个地址,故参数改变时,实际参数也会跟着改变
这个了解一下就可以
函数分文件编写一般有4个步骤
-
创建后缀名为.h的头文件
-
创建后缀名为.cpp的源文件
-
在头文件中写函数的声明
-
在源文件中写函数的定义
#include<stdio.h>
void sort(int a[],int lenth)
{
for(int i = 0;i < lenth - 1;i++)
{
for(int j = 0;j < lenth - 1 - i;j++)
{
if(a[j] > a[j + 1])
{
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
}
int main()
{
int a[10] = {9,8,8,10,4,6,1,7,5,3};
sort(a,10);
for(int i = 0;i < 10;i++)
printf("%d ",a[i]);
return 0;
}
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型
结构体定义:struct 结构体名 {结构体成员列表};
#include<stdio.h>
struct People
{
char name[10];
int age;
};
int main()
{
People p;
scanf("%s",p.name);
scanf("%d",&p.age);
printf("姓名为:%s\n年龄为:%d",p.name,p.age);
return 0;
}
结构体数组的定义:结构体名 变量名[ ];
#include<stdio.h>
struct People
{
char name[10];
int age;
};
int main()
{
People p[4];
for(int i = 1;i <= 3;i++)
{
scanf("输入第%d个人的名字:%s",p[i].name);
scanf("输入第%d个人的年龄:%d",&p[i].age);
}
for(int i = 1;i <= 3;i++)
{
printf("第%d个人的名字:%s\n",p[i].name);
printf("第%d个人的年龄:%d\n\n",p[i].age);
}
return 0;
}
指针是c语言直接访问内存的一种方式
- 内存编号是从0开始记录的,一般用十六进制数字表示
- 可以指针变量保存地址
指针变量的定义:数据类型 * 变量名 = &另一个变量名
指针变量的变量名中存储的是地址,在变量名之前加上*解引用,则是地址
一般变量和指针变量的区别:就像一枚硬笔的两面
变量 | 地址 | 值 |
---|---|---|
一般变量 | &a | a |
指针变量 | a | *a |
#include<stdio.h>
int main()
{
int a = 10;
int *p = a;
printf("a=%d &a=%d\n p=%d *p=%d",a,&a,p,*p);
return 0;
}
**空指针:**指针变量指向内存中编号为0的空间。空指针指向的空间是用户不可访问的,其作用是初始化指针。
**野指针:**指针指向非法的内存空间。
1.常量指针:const 数据类型 * 变量名 = &另一个变量名
特点:指针的==指向是可以改,但是指向的值不可以改==。
2.指针常量:数据类型 * const 变量名 = &另一个变量名
特点:指针的==指向不可以改,但指针指向的值可以改==。
3.const即修饰指针,又修饰常量:const 数据类型 * const 变量名 = &另一个变量名
特点:指针的==指向和指向的值都不可以改==。
指针访问一维数组:
#include<stdio.h>
int main()
{
int a[5] = {1,2,3,4,5};
int *p = a;
for(int i = 0;i < 10;i++)
{
printf("%d ",*(p + i));
}
return 0;
}
指针的加减:
如上程序,int类型的指针+1之后,内存地址编号增加了4,即向后移动了4个字节
我们知道,数组的空间是连续的,向后移动四个字节就到达了数组的下一个元素
指针访问二维数组:
#include<stdio.h>
int main()
{
int a[5][5] = {{0,1,2,3,4},
{5,6,7,8,9},
{0,1,2,3,4},
{5,6,7,8,9},
{0,1,2,3,4}};
int (*p)[5] = a;//指针声明
for(int i = 0;i < 5;i++)
{
for(int j = 0;j < 5;j++)
{
printf("%d ",*(*(p + i) + j));
}
printf("\n");
}
return 0;
}
指针p的指向是数组a的开头地址,即a[0] [0],p + 1指向的是a[1] [0],即第二行的开头地址
如果你想访问a[0] [1]则是:
*(p) + 1
访问a[1] [1]:
*(p + 1) + 1
不要问我为什么,要问就去问Dennis MacAlistair Ritchie老爷子,他就是这么规定的
用指针最为函数参数,传递实参的地址,会修改实参
#include<stdio.h>
void sort(int *a, int lenth)
{
for(int i = 0;i < lenth - 1;i++)
{
for(int j = 0;j < lenth - 1 - i;j++)
{
if(a[j] > a[j + 1])
{
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
}
int main()
{
int a[10] = {9,8,8,10,4,6,1,7,5,3};
int lenth = 10;
sort(a,lenth);
for(int i = 0;i < lenth;i++)
{
printf("%d ",a[i]);
}
return 0;
}