一、基础知识
第一个C语言程序,打印helloworld
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("Helllo world!\n");
system("pause");
return 0;
}
制作程序的流程:
- 编写源代码
- 编译前,进行预处理
- 编译,生成
.obj
文件 - 链接,生成可执行文件(如
.exe
) - 生成目标程序,可以运行
一段代码基本构成:
- main函数
- 预处理
- 库函数
- 关键字
- 注释
预处理:以#
号开头的命令
库函数:如printf/system
等已经实现具体功能的函数,要使用库函数,必须包含头文件
printf
函数–<stdio.h>
system
函数–<stdlib.h>
- 百度 函数名 头文件 查看函数所属头文件
关键字:
变量定义 | 类型定义 | 分支选择 | 循环控制 | 转移控制 | 修饰关键字 |
---|---|---|---|---|---|
char | enum | if | for | continue | unsigned |
wchar_t | union | else if | while | break | const |
int | struct | switch-case | do-while | goto | static |
注释:
- 单行注释:
//
- 多行注释:
/**/
注意事项:
- 运行程序:找到控制台所在文件夹,按住shift,选择打开Powershell,输入文件名 “.\001.exe”回车运行
- VS编译程序:
2.1 文件必须在项目里运行
2.2 #include “pch.h”/*VS2017强制要求在每个.c文件加这一句,也叫编译预处理*/
二、数据类型
基本数据类型:整型、浮点型、字符型
数据:常量与变量
C语言中:字符常量'A'
用单引号,字符串常量"abc"
用双引号
- 变量定义
- 变量类型选择:一般char、int、double
- 变量名定义:命名规范:小写字母开头(小写字母通常是这个变量的类型简写),然后以一个单词作为变量的名字(多个单词之间首字母应大写)
int nNum;char cCh;double dNum;
- 变量初始化
- int nNum = 0;//定义并初始化
- int nNum; nNum = 0; //此处不是初始化,只能叫赋值
- 变量赋值
- 变量数值交换 temp=a; a=b; b=c
- 不使用中间变量:
// 方式一:
x=x+y //x =3 + 6 ;x =9
y=x-y //y=9 - 6; y = 3
x= x-y //x=9 - 3; x = 6
// 两个数相加时,有可能超出int表示范围,不推荐
//方式二:
x = x^y // x= 3^6
y = x^y // y=(3^6)^6; y = 3
x = x^y // x=(3^6)^3; x = 6
标识符:编程时,有许多要命名的对象,比如函数名、变量名等,这些名称都叫做标识符。标识符的命名规则:
4. 只能由数字(0-9)、字母(a-z,A-Z)和下划线组成
5. 数字不能开头
6. 不能使用关键字
7. C语言标识符区分大小写
为了使我们定义的变量名清晰规范有含义,建议使用匈牙利命名法
:匈牙利命名法
三、输入输出函数
函数名 | 功能 |
---|---|
printf | 格式化输出数据到屏幕 |
scanf | 格式化输入数据 |
puchar | 输出一个字符到屏幕 |
getchar | 从键盘获取一个字符 |
_getch | 无回显的从键盘获取一个字符 |
puts | 输入一行字符串到屏幕 |
gets | 从键盘获取一行字符串 |
当然,这是一些基本的功能描述,有些细节的地方建议阅读《C Primer Plus》,比如输入字符串计算机怎么判断输入结束,getchar只获取一个字符,那么回车怎么处理等等问题
scanf
遇到空格会结束输入- scanf_s("",参数列表)
安全版函数针对%c和%s做了一定改进
在接收字符或字符串时,需要在参数列表额外提供一个数值类表示缓冲区的最大字节数 - printf("",参数列表)
功能:格式化输出数据到屏幕
char arr[10]={0};
char *p = arr;
printf("%s",p); //等价 printf("%s",arr)
printf("%s", *p); //等价printf("%s",arr[0])
int *p = 0;
char *pp = 0;
printf("%d",*p);
printf("%s", pp);
- getchar()
功能:从键盘输入一个字符
返回值:返回输入字符的ASCⅡ码
说明:在屏幕输入一串字符,直到遇到回车结束输入,但函数只接收第一个字符 - putchar()
功能:从终端输出一个函数,且转换成ASCⅡ码返回
函数原型:int putchar(int c)
函数参数:c为要输出的字符,可以是字符型常量、字符型变量、整型常量、整型变量表达式、转义序列等 - _getch()
功能:无回显字符输入函数
函数原型:int _getchar(void)
说明:不需要每次输入完成后输入回车表示结束,只要有键按下就会直接返回,并且不会在屏幕上显示
包含头文件:#include <conio.h> - gets_s(szBuff, sizeof(szBuff))
功能:从控制台接收带空格的字符串
格式字符:
控制符 | 说明 |
---|---|
%d | 十进制 |
%o | 八进制 |
%x,X | 十六进制(大小写输出) |
%u | int类型,无符号十进制 |
%c | 字符型输出 |
%s | 字符串输出 |
%f | float输出 |
%lf | double输出 |
%p | 打印内存地址 |
%% | 打印% |
转义字符:
转义字符 | 含义 | ASCII码 16/10进制 |
---|---|---|
\0 | 空字符(NULL) | 0/0 |
\n | 换行符(LF) | 0x0A/10 |
\r | 回车符 (CR) | 0x0D/13 |
\t | 水平制表符(HT) | 0x09/9 |
\v | 垂直制表符(VT) | 0x0B/11 |
\a | 响铃(BEL) | 0x07/7 |
\b | 退格符(BS) | 0x08/8 |
\f | 换页符(FF) | 0x0C/12 |
\ ‘ | 单引号 | 0x27/39 |
\ " | 双引号 | 0x22/34 |
\ \ | 反斜杠 | 0x5C/92 |
四、运算符
单目运算符是指运算所需变量为一个的运算符,即在运算当中只有一个操作数,又叫一元运算符,其中有逻辑非运算符:!、按位取反运算符:~、自增自减运算符:++, --等。
双目运算符是指运算所需变量为两个的运算符,例如+,-,*,/,%,<,>,>=,<=,==,!=,<<,>>,&,^,|,&&,||,=
三目运算符是指运算所需变量为三个的运算符,只有条件表达式【?:】
一般不会见到,说一个不常见的:
,
逗号运算符一般没有太大的效果,通常起到一个分割符的作用。特点:
- 优先级最低
- 逗号表达式的值是最右边的值
int nNum=0;
nNum = 12,13,14,15; //nNum=12
nNum = (12,13,14,15); //nNum=15
表达式:C语言中的表达式是若干个运算符和数据合在一起的算式。
语句:语句是C语言程序的基本组成,语句的结尾用;
分号标识,一个表达式加上一个分号就是一个语句。多个语句使用{}
括起来就是符合语句(程序块)
类型转换:
- 自动类型转换
int nNum;
char cCh1;
//小字节赋值到大字节变量
nNum = 'A';
/*将char类型数据赋值给int类型变量,将1字节填充到4字节空间,c语言编译器会将1字节数据扩充为4字节,然后存储,这种转换为隐式转换*/
nNum = 0x12345678;
cCh1 = nNum;
/*将int类型数据赋值给char类型变量,将4字节填充到1字节空间,c语言编译器会将4字节截断为1字节,然后再存储到1字节变量cCh1中,这个过程实际就是将int类型隐式转换为char型,int类型在转换过程中丢失了部分数据,cCh1 = 0x78*/
- 强制类型转换
格式:(变量类型)变量
float a=13.44;
int nNum=(int)a; //将float类型强制转为int型
五、一维数组
数组的定义:
- 数据类型 数组名[数组长度]
- 数组长度只能是常量和常量表达式(大于0),不能是变量
数组的初始化:
// 1.整体赋值
int arr1[5] = {1,2,3,4,5};
// 2.部分赋值
int arr2[5] = {1,2,3}; //还有最后两个元素为0
// 3.不给定数组长度,根据数组实际元素个数分配
int arr3[] = {1,2,3,4,5,6}; //6个元素,分配24字节空间
// 常见错误情况
int arr4[]; //没有初始化时,数组长度不能省略
int arr5[nNum]; //数组长度必须是常量,不能是变量
arr1=arr2; //数组不能整体赋值
arr6[] = {1,2,3,4,5,6};//数组不能整体赋值
数组的访问:
数组可以通过下标来访问某一元素,下标是具有整数值的表达式,也就是说下标可以是:
- 常量
- 变量
- 整数值的表达式
数组的存储:
- 数组在内存中的存储是连续的
- 数组名就是数组的起始地址,它是一个常量。
- 数组下标从0开始,最大下标(元素个数-1)
数组的输入输出:
- 在C语言中,数组是无法直接打印出来的
- 在C语言只有基本数据类型的值才能打印
- 数组是由基本数据类型组成,化整为零依次打印
//和变量的使用方式基本一致
printf("%d %d %d",arr[1],arr[2],arr[3]);
scanf_s("%d %d %d",&arr[1],&arr[2],&arr[3]);
数组的越界问题:
刚开始学C语言的时候,编译不报错,但是程序经常崩溃,一般是数组越界导致的。
int nArr[10]={0}; //定义一个有10个元素的数组
nArr[11]=10; //可以赋值吗?答案是可以的,这样会破坏内存原来的数组,导致程序间歇性不稳定
字符数组的初始化方式:
char arr1[] = {'H','e','l','l','o'}; //正常用法
char arr2[] = "Hello" //字符数组的特殊用法
/* c语言中每一个常量字符串都额外带有一个特殊字符串结束符“\0”
printf("%d %d\n", sizeof(arr1), sizeof(arr2)); // arr1 长度5 arr2 长度6
//sizeof()计算的是整个数组占用的内存空间字节数
//stelen()计算的是字符串的长度,不包括`\0`
//_countof()计算数组元素个数
字符数组的存储:
- 数组的最大下标缺省后,其最大元素个数由初始值个数决定
- 数组的元素个数一旦定义,其最大元素个数不会有变化
字符数组的输入输出:
char arr1[] = {'H','e','l','l','o'}; //正常用法
char arr2[] = "Hello" //字符数组的特殊用法
//字符串打印是以`\0`结尾的
printf("%s\n", arr1); //字符数组可以用%s直接输出,不会正确打印,会多打印一部分内存中的随机值,直到遇到`\0`结束
printf("%s\n", arr2); // 正确打印
scanf_s("%s\n", arr1, sizeof(arr1)); //安全版函数
注意:\0
的ASCII码值是0,而字符0
的ASCII码值是0x30
字符串操作函数:
这一系列字符串函数,都是库函数,头文件<string.h>,都有相同的前缀str
函数名 | 作用 |
---|---|
strlen | 求字符串长度 |
strcpy/strcpy_s | 字符串拷贝函数 |
strcmp | 字符串比较函数 |
strcat/strrcat_s | 字符串拼接函数 |
strstr | 字符串查找函数 |
sprintf | 格式化数字转字符串 |
sscanf_s | 格式化字符串转数字 |
示例程序:
//strlen求字符串的长度,只检测0,即'\0'
char cChar1[11] = "Hello";
char cChar2[20] = "Hello";
char cChar3[20] = "World";
//字符数组
printf("%d %d\n", sizeof(arr1), sizeof(arr2));
printf("%s\n", arr1);
printf("%s\n", arr2);
//字符串操作函数
cChar3[5] = 0; //'\0'
//sizeof()和strlen()的区别
printf("%d %d\n", sizeof(cChar1), sizeof(cChar2));
printf("%d\n", strlen(cChar1));
//cChar2[3] = 0;
printf("%d\n", strlen(cChar2)); //strlrn只检测0
//strcpy函数
//strcpy(cChar2, cChar3);
strcpy_s(cChar2, //目标字符串
sizeof(cChar2),
cChar3); //源字符串
printf("%s\n", cChar2);
六、三大结构
一个程序包含对数据的描述和对数据处理的描述,即数据结构和算法。
算法是为解决一个问题而采取的方法和步骤,无论简单还是复杂的算法,都由以下三大结构构成:
- 顺序结构
- 选择结构
- 循环结构
顺序结构:
最为简单,就是从上至下,一条一条语句执行
选择结构:
选择结构,就是当满足一定条件时执行某一段语句。
-
if-else
二义性:在多重嵌套中,else总是和在它之前出现的、尚未匹配的且离它最近的if相匹配- 使用if结构可以实现复杂的逻辑判断
- 用switch结构能够实现的结构,使用if结构都可以实现
- 分支较少的情况使用if结构更简单
- 分支较多的情况下使用if结构会使程序结构变得更复杂
- 如果if嵌套层次过深,也容易使程序结构变得复杂
-
switch-case
使用case进行语句时,case的条件只能是常量或者常量表达式- 没有复杂的逻辑判断,程序结构简单
- 对于分支很多的情况,特别适合
- switch结构只能基于一个整数值进行分支选择
- switch只能判断是否相等,不能判断在某一区间的值
- 虽然使用switch语句编写的程序有规律,可读性强,但是由于switch语句不能根据表达式的取值范围做出选择,所以使用时也有很大的局限性。
循环结构:
判断条件满足,重复执行某一段语句。
- for循环
- while循环
- do-while循环
- continue语句只能用于循环语句中, 作用是跳出本次循环, 执行下一次循环.而
- break语句用在循环语句中, 作用是跳出循环, 执行后面的语句. 用在switch语句中, 作用是跳出switch语句, 执行后面的语句. 如果将switch语句中每个case中的break注释掉, 则switch中的所有语句都被执行
七、二维数组
二维数组的定义:
格式:类型说明符 数组名[常量表达式1][常量表达式2];
二维数组的初始化:
// 1.整体初始化
int arr[2][3]={1,5,6};
// 2.分行初始化
int arr[2][3]={{1},{5,6}};
// 3.全部初始化
int arr[][3]={ 1,2,3,4,5,6}; //数组第一维可以不指定,第二维长度不能缺省
二维数组的输入输出:
原则:
- 在C语言中,只有基本数据类型(char,int,double)能通过printf输出,scanf输入,数组不能直接输入输出
- 数组的输入输出是数组元素的输入输出
二维字符数组的使用:
- 初始化(如果使用常量字符串来初始化,数组大小要比字符个数多一位,常量字符串包含"\0")
- 输入输出:可以使用“\s”格式符直接输入输出字符数组
- 二维数组不能直接赋值,但字符数组可以使用strcpy进行拷贝
//遍历二维数组
int main(int argc, char const *argv[])
{
/* code */
char szName[3][10] = { "小明","大明" };
//szName[2] = "小花"; //错误使用赋值
strcpy_s(szName[2], sizeof(szName[2]), "小花");
for (int i = 0; i < 3; i++)
{
printf("%s\n",szName[i]);
}
system("pause");
return 0;
}