一、初识c语言
1、什么是C语言?
2、第一个C语言程序
3、数据类型
4、变量、常量
5、转义字符
6、注释
二、选择与循环语句
1、选择语句(if和switch语句)
2、循环语句(while、for、do while)
三、函数
1、C语言常用库函数
2、自定义函数
3、函数参数
4、函数的声明和定义
5、函数递归
四、数组
1、一维数组
2、二维数组
五、操作符
1、操作符分类
2、移位操作符
3、位操作符
4、逻辑操作符
5、条件操作符
6、逗号表达式
六、指针
1、指针是什么?
2、野指针
七、结构体
1、结构体的声明
2、结构体变量的定义和初始化
3、结构体传参
C语言是一门面向过程的计算机编程语言,广泛应用于底层开发,与C++、java等面向对象的编程语言有所不同。Cyuyan的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。
#include //头文件
int main()//main函数为程序入口,一个工程中main函数仅有一个
{
printf("hello\n");
return 0;//return函数结束,后面不执行
}
数据类型 | 打印方式 | |
char | 字符 | %c |
short | 短整型 | %d |
int | 整型 | %d |
long | 长整型 | %d/%ld |
float | 单精度浮点数 | %f |
double | 双精度浮点数 | %lf |
注:打印八进制数(%0),打印十六进制数(%0x)
(1)变量的作用域和生命周期
a、局部变量作用域为变量所在的局部范围。
b、全局变量作用域为整个工程。
变量对应的生命周期为该变量的作用域。
(2)C语言中常量分类
转义字符 | 释义 |
\' | 表示字符常量' |
\" | 表示字符串内部的双引号 |
\\ | 表示反斜杠 |
\b | 退格 |
\n | 换行 |
\r | 回车 |
\ddd | ddd表示1~3个八进制数字 |
\xdd | dd表示两个十六进制数字 |
注:几个常用快捷键
Ctrl+k+f //整理代码格式
Ctrl+k+c //整体注释
Ctrl+k+u //取消注释
(1)if语句
“=”表示赋值,“==”判断相等,“!=”判断不相等。
//多分支情况
if (表达式)
{
语句一;
}
else if (表达式)
{
语句二;
}
else
{
语句三;
}
如果表达式为真,则语句执行。
注:在C语言中如何表示真假呢?
0表示真,非0表示假。
(2)switch语句(常用于多分支情况)
比如:
输入1:输出星期一
输入2:输出星期二
输入3:输出星期三
输入4:输出星期四
输入5:输出星期五
输入6:输出星期六
输入7:输出周日
如果用if语句的话会显得很麻烦,我么们可以用switch语句来实现。
#include
int main()
{
int day = 0;
scanf("%d", &day);
switch (day)//条件只能是整型
{
case 1: //case后面只能是整型常量
printf("星期一\n");
break;
case 2:
printf("星期二\n");
break;
case 3:
printf("星期三\n");
break;
case 4:
printf("星期四\n");
break;
case 5:
printf("星期五\n");
break;
case 6:
printf("星期六\n");
break;
case 7:
printf("周天\n");
break;
}
return 0;
}
(1)while循环
在屏幕上打印1-10的数字
#include
int main()
{
int i = 1;
while (i <= 10)
{
printf("%d ", i);
i++;
}
return 0;
}
while循环里的break和continue
(2)for循环
for(表达式一;表达式二;表达式三)
循环语句;
表达式一用于初始化循环变量。表达式二条件判断部分,用于判断循环是否终止。表达式三用于循环条件的调整。
使用for循环在屏幕上打印1-10数字
#include
int main()
{
int i = 0;
//for (i = 1/*初始化*/; i <= 10/*判断部分*/; ++i/*调整部分*/)
for (i = 1; i <= 10; ++i)
{
printf("%d ", i);
}
return 0;
}
注:1、不可在for循环内修改循环变量,防止for循环失去控制。
2、建议for语句的循环控制变量的取值采用前闭后开区间写法。
(3)do……while()循环
do
循环语句;
while(表达式);
特点:循环至少执行一次,适用的场景有限,所以不是经常使用。
C语言函数分类:库函数、自定义函数
注: 但是库函数必须知道的一个秘密就是:使用库函数,必须包含 #include 对应的头文件。
ret_type fun_name(para1, * )//ret_type返回类型,fun_name函数名,para1函数参数
{
statement;//语句项
}
举一个例子:
写一个函数可以找出两个整数中的最大值。
#include
//get_max函数的设计
int get_max(int x, int y)
{
return (x>y) ? (x) : (y);
}
int main()
{
int num1 = 10;
int num2 = 20;
int max = get_max(num1, num2);
printf("max = %d\n", max);
return 0;
}
形式参数(形参):
真实传给函数的参数,叫实参。实参可以是:常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。
实际参数(实参):
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。
上面代码x、y为形参,num1、num2为实参。形参实例化之后其实相当于实参的一份临时拷贝。
(1)函数声明
test.h放置函数声明
#ifndef __TEST_H__
#define __TEST_H__
//函数的声明
int Add(int x, int y);
#endif //__TEST_H__
(2)函数定义:交待函数的功能实现。
test.c放置函数定义
#include "test.h"
//函数Add的实现
int Add(int x, int y)
{
return x+y;
}
(1)什么是递归?
把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。 递归的主要思考方式在于:把大事化小
(2)递归的两个必要条件
例:求斐波那契数
int fib(int n)
{
if (n <= 2)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
代码在运行时存在以下问题:
在调试 factorial 函数的时候,如果你的参数比较大,那就会报错:stack overflow(栈溢出这样的信息)。 系统分配给程序的栈空间是有限的,但是如果出现了死循环,或者(死递归),这样有可能导致一直开辟栈空间,最终产生栈空间耗尽的情况,这样的现象我们称为栈溢出。
注:
1. 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。
2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。
(1)数组创建
type_t arr_name [const_n];
//type_t 是指数组的元素类型
//const_n 是一个常量表达式,用来指定数组的大小
注:数组创建, [] 中要给一个常量才可以,不能使用变量。数组在创建的时候如果想不指定数组的确定的大小就得初始化。数组的元素个数根据初始化的内容来确定。
(2)一维数组的使用、在内存中的存储
a、数组是使用下标来访问的,下标是从0开始。 数组的大小可以通过计算得到。
b、在内存中的存储
随着数组下标的增长,元素的地址,也在有规律的递增。 由此可以得出结论:数组在内存中是连续存放的。
(1)二维数组初始化
//数组初始化
int arr[3][4] = {1,2,3,4};//其余的为0
int arr[3][4] = {{1,2},{4,5}};
int arr[][4] = {{2,3},{4,5}};//
注:
二维数组行标可以省略,列标不能省略;数组计算长度只有在定义时使用有效。
(2)二维数组在内存中的存储
二维数组在内存中也是连续存在的。二维数组是特殊的一维数组。
注:数组名代表数组首元素地址。
(两个例外,sizeof(arr)、&arr)
算术操作符、移位操作符、位操作符、赋值操作符、单目操作符、关系操作符、逻辑操作符、条件操作符、逗号表达式
<< 左移操作符
>> 右移操作符
(1)左移操作符
左边抛弃、右边补0
(2)右移操作符
1. 逻辑移位 左边用0填充,右边丢弃
2. 算术移位 左边用原该值的符号位填充,右边丢弃
& //按位与,两位都为1时为1
| //按位或,对应位为1,结果为1
^ //按位异或,不一样的位为1,一样为0
注:他们的操作数必须是整数。
0异或任何数为数字本身;两个相同的数字异或为0。
逻辑操作符有哪些?
&& 逻辑与(短路与)
|| 逻辑或(短路或)
区分逻辑与和按位与 区分逻辑或和按位或
1 & 2 -----> 0
1 && 2 ----> 1
1 | 2 -----> 3
1 || 2 ----> 1
exp1 ? exp2 : exp3
//exp1为真,执行exp2,否则执行exp3
exp1, exp2, exp3, …expN
逗号表达式,就是用逗号隔开的多个表达式。 逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向
(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。
总结:
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。
(1)野指针成因
#include
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
#include
int main()
{
int arr[10] = { 0 };
int *p = arr;
int i = 0;
for (i = 0; i <= 11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
如何规避野指针?
1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放即使置NULL
4. 指针使用之前检查有效性
例如描述一个学生:
typedef struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}Stu;//分号不能丢
结构体成员的类型:
结构的成员可以是标量、数组、指针,甚至是其他结构体。
struct Point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值。
struct Point p3 = {x, y};
#include
struct S
{
int data[1000];
int num;
};
struct S s = { { 1, 2, 3, 4 }, 1000 };
//结构体传参
void print1(struct S s)
{
printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
printf("%d\n", ps->num);
}
int main()
{
print1(s); //传结构体
print2(&s); //传地址
return 0;
}
上面的 print1 和 print2 函数哪个好些?
答案是:首选print2函数。 原因:
函数传参的时候,参数是需要压栈的。 如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。