C语言的学习笔记文档在三年多以前已经完成了,不过由于考研专业课要考C语言,便边一边复习专业课一边重新整理了C语言的学习笔记到博客上。
初始C语言
C语言是一门计算机语言
所谓计算机语言就是人和计算机交流的语言
计算机语言的发展:
最早是二进制语言 对应硬件电的正电[1]和负电[0]
其后是 使用助记符的 汇编语言
B语言
C语言
Java、C++、Python.....等高级语言
国际标准
由美国国家标准局为C语言制定了ANSIC,此后有陆续规定了C89/C90.....不过目前多以C89/C90为主
主流C语言编译器
Clang、GCC、MSVC
项目创建
看到这个界面就说明项目创建成功啦!
5.创建源文件
关于源文件的一些说明
.c -C语言的源文件
.cpp - C++的源文件
.h- 头文件
C语言的最基本的一个代码结构
#include<stdio.h>//引入头文件<stdio.h>
int main(){ //主函数-程序的入口 程序中必须有且仅有一个main函数 main前面的int表示main函数 返回一个整型值
printf("hello world"); //打印函数print function 该函数是C语言本身提供的库函数需要通过#include<stdio.h>引入
return 0; //成功运行后返回 整数型数0
}
关于main函数的说明:
一个工程中可以有多个.C文件,但多个.c文件只能有一个main函数
编译及运行
编译+链接+运行代码
快捷键:ctrl+f5
如果运行代码时没有出现停留则需要参照下图设置一下
数据类型
数据单位常识
一个bit(比特位)用来存一个二进制位(0/1)
一个byte(字节)=8个bit
1kb=1024byte 之后以此类推
C语言中数据类型
sizeof
sizeof是C语言中关键字也是操作符,用于计算类型或变量所占空间的大小
通过sizeof关键字可以来查看数据类型
int main(){
int sum=100;
printf("%d\n",sizeof(sum));
return 0;
}
数据类型 | 中文类型名 | 占位大小 | 备注 |
---|---|---|---|
char | 字符数据类型 | 占1个字节 | 例:char ch = 'a'; |
short | 短整型 | 占2个字节 | 例:short num=10; |
int | 整型 | 占4个字节 | 例:int num=10; |
long | 长整型 | 占4/8个字节 | win/llinux32 是4字节 linux64是8字节 C语言的标准:long的大小>=int的就行 |
long long | 更长整型 | 占8个字节 | |
float | 单精度浮点数 | 占4个字节 | |
double | 双精度浮点数 | 占8个字节 | |
字符串类型值:通过创建一个 char类型数组进行存放字符串 例 char name[]=“zhangsan”;
得注意的是 char a[]="abc" 与 char b[]={'a','b','c'}结果却不同后者由于没有结束标志会随机打印一些内容 而是和 char c[]={'a','b','c',0}是一样的 原因是 “abc”其实是存放了'a','b','c',‘\0’ \0是字符串结束的标志 \0的值是0
查看字符串长度的语法 strlen(字符串名) 仅仅计算结束标志\0之前的内容 如果没有\0那么会计算到直到随机出现0停止计算
char ch = 'a';
printf("%c",ch);//%c 表示声明打印字符格式的数据
return 0;
int ch = 20;
printf("%d",ch);//%c 表示声明打印整型十进制格式的数据
return 0;
使用printf打印时不同变量的语法格式
注意 只要是整数型 都用%d来声明
float ch = 20.0;
printf("%f",ch);//%c 表示声明打印浮点型格式的数据
return 0;
%s用来声明打印字符串类型数据
%f 用来声明打印单精度浮点型数据
%lf 用来声明打印双精度浮点型数据
%p用来声明以地址的形式打印
%x 用来声明打印16进制数字
常量与变量
C语言中的变量
变量的创建
//创建语法 1.类型名 变量名; 2.类型名 变量名 =0;
int a;
int b =0;
变量的分类
变量分为局部变量和全局变量
int b=100;
int main(){
int a=2;
return 0;
}
局部变量
在大括号之内定义的变量 为局部变量,如代码中的变量a
全局变量
在大括号外部定义的为全局变量,如代码中的变量b
注:当全局变量和局部变量冲突的时候 局部变量优先!!!但不建议把局部变量和全局变量名字写一样
int b=100;
int main(){
int b=2;
printf("%d",b);
return 0;
}
可以发现,代码中的b的值实际是2而不是100,这就是遵循了局部优先原则,
变量的作用域
作用域(scope):程序设计概念,通常来说,一段代码中所用到的变量并不总是有效,而限制这个变量的的可用性的代码范围就叫做变量的作用域
局部变量的作用域:就是变量所在的局部范围
全局变量的作用域:整个工程 只不过如果在同一工程的其他源文件中使用,则需要在头部进行 extern 数据类型 变量名(例:extern int a)来声明一下就可以正常使用该变量了
变量的生命周期
变量的生命周期:变量的创建和销毁之间的时间段
局部变量的生命周期:进入局部范围生命开始,出局部范围声明结束,约等于他的作用域
全局变量的声明周期:整个程序的生命周期
scanf函数
scanf函数是输入函数,在VS中使用scanf函数时会提示不安全,需要在所有代码(包括引入头文件的代码)的最上方写
#define _CRT_SECURE_NO_WARNINGS,就能够解决该问题
使用语法
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
int a = 0;
int b = 0;
int sum = 0;
scanf("%d %d", &a,&b);
sum = a + b;
printf("sum=%d", sum);
return 0;
}
C语言中的常量
1.字面常量
//例如:
3.14;
10;
'a';
"abcdefg";
2.const修饰的常变量
//例如:
const int num1=10;
//代码中的num1就是是常变量,其具有常属性,但是本质仍然是一个变量
//例如 int a[num1]; 这就是不允许的 因为定于数组元素个数的必须是一个真常量 如 1 10
3.#define定义的标识符常量
//例如
#define Max 100 //那么以后 MAX直接就是100 标识符常量本质就是常量 可以作为数组的索引值
int main() {
int a = MAX;
printf("a=%d", a);
return 0;
}
//会发现输出的结果就是 a=100
注意:已经定义过的标识符常量不能重新赋值来改变该常量的值,例:MAX=2000;就是错误的
4.枚举常量
枚举常量是指可以一一列举出来的常量
枚举关键字 enum
//例
enum Sex
{
//下面为该枚举类型未来可以取的值,索引从0递增
boy,
girl,
secert //每一个枚举内容由逗号隔开 每一个对应的默认值为索引值
}; //最后大括号后加分号
int main()
{
enum Sex a = boy; //进行实例化使用
return 0;
}
字符串
"hello";
字符串:字符串就是一串字符,通过创建一个 char类型数组进行存放字符串 ,这种由双引号引起来的一串字符称为字符串字面值,或者字符串
注:字符串的结束标志是一个 \0的转义字符,在计算字符串长度时\0只是结束标志,而不算作字符串内容,也不会算作字符串的长度
用printf函数分别打印输出 char a[]="abc" 与 char b[]={'a','b','c'}会发现打印出结果不同,后者由于没有结束标志将会随机打印一些内容 而前者则和 char c[]={'a','b','c',0}是一样的 原因是 “abc”其实是存放了'a','b','c',‘\0’ ,通过该形式创建的字符串系统会默认在其末尾添加一个‘\0’ ,而\0的值是0,所以在数组,末尾加0相当于加了'\0'
关于用strlen求字符长度时:
char a[]="abc" 的长度是3,\0并不会被算进去
char c[]={'a','b','c',0} 的长度也是3,0并不会被计算进去
char b[]={'a','b','c'} 的长度将是是一个随机值,因为计算机也不知道该字符串到哪里算结束
C语言中的转义符
转义字符 | 含义 | |
---|---|---|
\n | 换行符 | |
\t | 水平制表符 | |
\v | 垂直制表符 | |
\\ | 表示将\去除转义的效果 代表一个\ | |
\b | 退格符 | |
\r | 回车 | |
\" | " | |
\ddd | 代表ddd 代表1-3个八进制的数字 | 比如\130代表的就是十进制的88,如果以字符格式输出将会输出88对应的ASCII值的字符内容“X” |
\xdd | dd表示两个16进制数字 | 其用法参考\ddd即可 |
值得注意的是:当计算字符串长度是若有转义字符,那么其只能代表一个字符哦,如\130 ,计算其长度时只能算作一个字符,即使它转义后是88如果直接计算是2个字符但由于其是转义字符仍只能算一个字符
一个小坑试题
printf("%d\n",strlen("c:\test\328\test.c"));
需要注意的是:1:转义字符只能代表一个字符 2.\328 这个是非常容易计算错误的,因为在8进制中数字中是不可能存在8这个数字的,因此计算机会将\32算作一个字符,而8有单独算作一个字符
注释
1.C语言中原版注释风格
/* 注释内容
......
......
......*/
2.C++注释风格,但现在实际上C语言的编译器也都支持这种风格,而且更推荐使用这种注释风格
//注释内容
//注释内容
//注释内容
C语言结构语句
C语言是一门结构化的程序设计语言
1.顺序结构
2.选择结构
3.循环结构
什么是语句?
C语言中由一个分号隔开就是一条语句
只有一个分号也是一条语句 --空语句
分支语句(选择结构)
if语句
语法结构
/*1
if(表达式)
语句1;
2.
if(表达式)
语句1;
else
语句2;
3.
if(表达式1)
语句1;
else if (表达式2)
语句2;
else
语句3;
一次类推....*/
值得注意的是
1.如果不同情况有多条语句的话 我们需要加上{}形成代码块
if(表达式)
{语句1;
语句2:}
2.else会和离它最近且未匹配的if进行匹配 所以需要加代码块{}进行规范和约束
3.需要注意的是表达式中 究竟是 = 是==
switch语句
switch语句也是一种分支语句,常常用于多分支的情况
switch(整型表达式(必须是一个整型数据类型))
{
语句项:
}
所谓语句项 就是一些case语句
例:
case 整型常量表达式:
语句;
实例演示
int main()
{
int day=0;
scanf("d%",&day);
switch(day)
{
case1 :
printf("周1");
break;
}
case 2:
printf("周2");
break;
case 3 :
print("周3");
break;
default:
printf("输入错误");
break;
}
case 需要配合 break跳出来一起使用 不然一直执行到有break的地方
当出现多种情况对应一种返回情况是可以这样写
int main()
{
int day=0;
scanf("d%",&day);
switch(day)
{
case1 :
case 2:
printf("工作日");
break;
case 3 :
print("周3");
break;
default:
printf("输入错误");
break;
}
当给出的所有情况都不适配时 这会执行 default(违约,默认值)语句项
循环语句
C语言中的循环语句类型
1.while
2.for
3.do while
while循环语句
while语法结构
/*while(表达式) 只要表达式条件成立 /不为0 就会不断执行
循环代码块;
*/
在循环结构中 break和continue的作用
break的作用是直接跳出最近的循环然后向下执行代码
continue是跳出循环结构的本轮循环 然后继续判断进行循环
for循环语句
for(表达式1;表达式2;表达式3)
循环语句
表达式1位初始化部分,用于初始化循环变量的,表达式2为条件判断部分,用于判断循环是否终止,表达式3为调整部分,用于循环条件的调整。
例:
int main()
{ int i =0;
for(i=1;i<=10;i++)
{ printf("hello world!")
}
}
for循环的其他例子
for( ; ;) for循环的初始化调整判断都可以被省略但是如果for循环的判断部分被省略 那么会导致死循环
{
}
do while循环语句
do
循环语句
while(表达式)
getchar()获取键盘输入的一个字符
putchar()输出字符
C语言中的函数
C语言中函数的分类
库函数
一些实现基础功能不是业务性质的代码,我们在开发中每个程序员都有可能用的到,为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。
库函数的分类
注意要使用库函数 需要引入相关头文件
include< .h>
IO函数
字符串操作函数
字符操作函数
内存操作函数
时间/日期函数
数学函数
其他库函数
自定义函数
自定义函数和库函数一样,有函数名,返回值类型和函数参数,但不一样的是这些都是我们自己来设计的
引入自定义函数 #“函数名.h”
自定义函数的基本组成
函数返回类型 函数名(参数类型 函数参数,参数类型 函数参数,....)
{
函数体;
}
例:
int max(int x, int y) {
int z = 0;
if (x > y) {
z = x;
}
else
{
z = y;
}
return z;
}
int main() {
int a = 10, b = 20;
int c = max(a, b); //函数调用
printf("最大值是%d", c);
}
定义无返回值的函数
void 函数名(参数类型 函数参数,参数类型 函数参数,....)
{
函数体;
}
例:
交换两个变量的值
void swap(int *pa, int*pb) {
int z = 0;
z = *pa;
*pa = *pb;
*pb = z;
}
int main() {
int a = 10, b = 20;
swap(&a, &b); //函数调用
printf("a是%d,b是%d", a,b);
}
注意:一个函数如果不写它的返回类型的话默认是int类型
函数实参:
真实传给函数的参数叫做实参,实参可以是:常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传给形参
函数的形参:
形参只用在函数调佣过程中才实例化(分配内存单元),所以叫形式参数,形参在函数调用完成后就自动销毁了,因此形参只有在函数中才有效,形参的命名可以与实参相同也可以不同
关于函数中形参和实参的问题
形参实例化之后其实相当于实参的一份临时拷贝 就是说当实参传给形参时候,形参其实是实参的一份临时拷贝 对形参的修改是不会改变实参的 需要对实参的指针变量进行操作才能改变
函数的调用分类
传值调用
函数的形参和实参分别占有不同的内存块,对于形参的修改不会影响实参。
传址调用
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用方式。
这种方式可以让函数和函数外部变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量
当数组实参传参的话如果实参写的是数组名 传递过去的实际是数组中第一个元素的地址,因此如果要计算穿过去的数组大小的话就只会是1了,因此如果涉及到数组元素大小(个数)需要在函数外先求好将其作为一个参数传过去
函数的嵌套调用与链式访问
链式访问
把一个函数的返回值作为另外一个函数的参数
函数的声明与定义
函数的声明:
1.告诉编译器有一个函数叫什么,参数是什么,返回类型是什么,但是具体是不是存在无关紧要
2.函数的声明一般出现在函数的使用之前,要满足先声明后使用,若函数定义定义在使用后面了 需要在使用前通过 “ 函数值返回类型 函数名(参数类型 形参变量 ....) ”;来进行声明,一般情况下还是习惯将函数定义在main函数外面
3.函数的声明一般要放在头文件中
函数的定义:
函数的定义是指函数的具体实现,交代函数的功能实现。
函数的递归
递归的含义:程序调用自身的编程技巧称为递归(recursion),递归作为一种算法在程序设计语言中广泛应用。一个过程或者函数在其定义或者说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需要少量的程序就可以描述出解题过程所需要的多次重复计算,大大减少了程序的代码量。递归的主要思考方式在于:把大事化小
递归的两个必要条件
存在限制条件,当满足这个限制条件时候,递归便不再继续。
每次递归调用之后越来越接近这个限制条件。 ·
指针
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(point to) 存在电脑存储器中另一个地方的值,由于通过地址就能找到所需要的变量单元,可以说,地址指向该变量单元。因此将地址形象化称为“指针”,意思是通过它能找到以他为地址的内存单元
有一种变量是用来存放地址的 ---指针变量
int* 变量名 char* 变量名 等等取决于存放的数据类型 来定义 指针变量 int char ....是类型
指针的类型将会决定进行指针解引用操作是,能够访问空间的大小 也会决定指针+1将会改变多少步长
int*p 那么*p可以访问4个字节
char*p *p可以访问1个字节
double*p *p可以访问8个字节
变量名 就是解引用操作符 就是对地址进行找其对应存放的值
例 : int a =20
int *p=&a 将a的地址存放在 p中
*p=20 将20赋值到p存放的地址中
地址变量所占内存大小取决于 操作系统的平台 指针大小在32位平台是4个字节在64位平台是8个字节
配置VS上操作平台的方式:点击 Debug 点击 配置管理器 选择活动解决方案平台中新建X64即可
当前x64操作系统一个内存空间大小为1个字节
野指针
野指针就是指 指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)
1.指针未初始化
局部变量不初始化,默认是随机值
局部的指针变量就被初始化随机值
int main()
{
int*p;
*p=20;
return 0;
}
2.指针越界访问
3.指针指向的空间释放
如何规避野指针
1.指针初始化
2.小心指针越界
3.指针指向空间释放即使置null int* p= NULL; 用来使指针初始化 如果给了NULL则这个指针以后也无法进行访问了
4.指针使用之前检查有效性
指针的运算
指针的+-
指针变量+1 表示向后跳一个指定变量数据类型的储存大小
数组中 指针-指针 =中间的元素个数-1
指针是可以比较大小的
关于指针比较的相关的一些规定:允许指向数组元素的指针与指向数组最后一个元素的后面那个内存位置的指针比较,但不允许与指向第一个元素之前那个内存位置的指针进行比较
关于多级指针
例: int a =10;
int*p=&a
int**pa=&p
pa就是二级指针 以此类推
指针数组
存放指针的数组
例 int*arr[10]={0}