快速上手c语言
快速上手c语言
- 快速上手c语言
- 关于学c语言的一些信息杂谈
- 第一个C语言程序
- 通过命令行运行c程序
- Dev-c++5.11
- Visual Studio系列产品
- 数据类型
- 变量、常量
- 定义变量的方法
- 变量的命名
- 变量的分类
- 变量的使用
- 变量的作用域和生命周期
- 常量
- 操作符简单介绍
- 语句
- 选择语句
- 循环语句
- 数组
- 数组定义
- 数组的下标
- 数组简单使用
- 变长数组的概念
- 字符串和转义字符
- 字符串
- 转义字符
- 注释
- 函数
- 关于scanf的返回值
- 常见关键字
- #define 定义常量和宏
- 指针
- 内存
- 地址的产生(可跳过)
- 变量的地址
- 结构体
快速上手c语言
在看懂c语言的基础上,再学会如何使用。
同理,在学会使用c语言的基础上,再进行语法和细节上的拓展。
因此本篇的目的是基本了解C语言的基础知识,对C语言有一个大概的认识。
每个知识点就是简单认识,不做详细讲解,后期会进行补充。
关于学c语言的一些信息杂谈
仅为个人平时听别人说话总结的东西,可跳过:
- 开始学c,都是先从简单概念介绍,编译环境的搭建开始,会用就行,用熟后再学内部的东西。学其他编程语言(也可以是交流的语言)也是这样。
- 写博客是表示你愿意向其他人分享知识,同时也是知识点的总结,写其他博客也一样。在面试时可以加深面试官的印象。
- 写完的代码可以上传git。git可以是gitee(国内),也可以是githup(国外)。一般用gitee就够,也可以用githup。这个也是可以在面试时加印象分。
- 语言,我暂时理解为两个或多个对象之间的交流。比如人与人交流可以通过汉语、英语等。相对应的,人与计算机交流可以通过c语言、c++、Java等程序设计语言和开发环境。
- 学好编程不仅是学好c语言,这些都可以提升编程的能力,推荐去学:
- 计算机语言,推荐学完c语言和一门面向对象的语言(比如c++)后再去学其他语言,会有全新的认识。
- 算法和数据结构。
- 操作系统。
- 计算机网络。
- 项目实战。
- 数据库-MySQL。
- c语言广泛用于底层开发。比如,一个电脑刚买来的时候是一个硬件(称之为里面装有电路板的方块更恰当),这个硬件装了操作系统比如windows 10,Linux等。
操作系统之上的称之为上层软件。这里用一张图大致描绘电脑的结构:
c语言更擅长做底层软件开发,在嵌入式开发、单片机开发中应用广泛,但c语言也可以做上层软件。其中Linux内核就是用c语言写出来的(不懂没关系,以后会懂)。
-
计算机语言的发展:
-
编程可以狭义的称之为写代码。写代码是为了利用计算机解决生活中的问题。
-
c语言诞生的时候,c语言在不同人(公司)手上各自设有自己的标准。这就使得
A公司写的代码无法在B公司用。于是C语言的国际标准统一,也就是出现统一的标准。
ANSI C是最早的标准,当时称它为C89,后来不断根据需求增添和修改功能。目前的习惯是在C后面加年份的最后2位来指代这个标准。
C11之前的标准都是在20世纪末发布,C11是2011发布的最新标准。
-
c语言还需要通过编译器翻译成计算机看的懂的语言,计算机才能去做事。
常见的c语言编译器:- Microsoft Visual Studio系列(简称vs),推荐使用vs2019,版本太旧会出现BUG或版本不兼容,太新的话微软公司做的封装太严实,不利于新人学习c语言。
vs用的编译器是MSVC。
vs集结编辑器、编译器、调试器和别的东西,集结写源代码文本、编译和运行为一体,称这种大成集合体为集成开发环境。英文缩写:IDE。
类似的产品还有Visual Studio Code(简称vscode)只是用来写代码的编辑器,需要装很多插件才能用。 - Linux平台下的gcc。windows也有gcc,主要是Dev-c++和vscode用。
- 苹果维护的clang。
- Microsoft Visual Studio系列(简称vs),推荐使用vs2019,版本太旧会出现BUG或版本不兼容,太新的话微软公司做的封装太严实,不利于新人学习c语言。
-
c语言是一门通用计算机编程语言,广泛应用于底层开发。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。
-
尽管c语言提供了许多低级处理的功能,但仍然保持着良好跨平台的特性,以一个标准规格写出的c语言程序可在许多电脑平台上进行编译,甚至包含一些嵌入式处理器(单片机或称MCU)以及超级电脑等作业平台。
-
二十世纪八十年代,为了避免各开发厂商用的c语言语法产生差异,由美国国家标准局为c语言制定了一套完整的美国国家标准语法,称为ANSI C,作为c语言最初的标准。 2011年12月8日,国际标准化组织(ISO)和国际电工委员会(IEC)发布的C11标准是c语言的第三个官方标准,也是c语言的最新标准,该标准更好的支持了汉字函数名和汉字标识符,一定程度上实现了汉字编程。
-
C语言是一门面向过程的计算机编程语言,与C++,Java等面向对象的编程语言有所不同。其编译器主要有Clang、GCC、WIN-TC、SUBLIME、MSVC、Turbo C等。
我的c语言和c++的学习主要的环境是GCC(为方便我会写成gcc)和MSVC(为方便我会写成vs或vs2019)。 -
推荐书籍:
-
初学c语言和初学c++阶段:
《C和指针》
《高质量的C/C++编程指南》
《C语言深度剖析》
《数据结构》严蔚敏(c语言版)殷人昆(c++版)
《程序员的自我修养》
《C陷阱与缺陷》 -
对c++有一定认识之后:
《Effective C++》(中文版 第3版)
《STL源码剖析》 -
对操作系统,特别是Linux操作系统有一定了解之后:
《现代操作系统》(第4版)
《Unix环境高级编程》
《图解HTTP》
《图解TCP/IP》 -
关于数据结构和算法:
《数据结构》严蔚敏(c语言版)殷人昆(c++版)《漫画算法》
-
其他:
《知识大迁移》
仅为个人理解,可能有安排不合理的地方,如何选择看自己。
-
第一个C语言程序
通过命令行运行c程序
新建一个拓展名为.txt
的文件,在里面写上c语言的程序:
#include <stdio.h>
int main()
{printf("hello bit\n");printf("he he\n");return 0;
}
//解释:
//main函数是程序的入口
//一个工程中main函数有且仅有一个
之后将拓展名改为.c
即可。.c
是c语言的代码文本文件,.cpp
是c++的代码文本文件。c++的代码文本文件可以运行c语言的,反过来却不行,因为c++是在c语言的基础上进行补充。
这个程序是打印hello bit\n
和he he\n
在什么地方。\n
是换行的意思。
安装minGW-64,并配置环境变量。
之后只需在cmd中将目录切换到源码所在的目录,输入命令gcc name.c -o name.exe
即可生成可执行程序(在>
后输入)。运行可执行程序,输入程序的名称即可。
c语言的代码都是从main
函数的第一行开始执行。一个c程序背后的源码有且只有一个main函数。
printf
是一个库函数,即c语言的标准库中提供的一个线程的函数,可以直接用。它的功能是在终端控制台上打印信息。在使用printf
之前一定要包含头文件stdio.h
。
stdio.h
是标准输入输出头文件(standard,in,out三个部分组成)。关于头文件的本质,点击这里。
main
函数还有其他的写法。
void main(){}
古老的写法,现在大家都不这么写。
int main(void){}
void 表示
main
不接受参数,可以这样写。
int main(int argc, char* argv[]){}
argc:保存命令行字符串个数
argv:指针数组,每个成员都是指针(argv[0] argv[1] argv[2] …),用于成员指向命令行各字符串的首地址。例如下面的代码会将所有命令全部打印。
#include<stdio.h>int main(int argc,char* argv[]){int i=0;for(i=0;i<argc;i++){printf("%s\n",argv[i]);}return 0; }
但目前我用的编译器都是只打印一个调用最新生成的.exe的命令。
这些操作也可以在vscode界面使用。详细见vscode-CSDN博客。
Dev-c++5.11
Dev-c++5.11将命令行简化成了一个或两个按钮。只需点击这个按钮即可运行。
也可以按快捷键F11,Dev会自动帮我们完成输入命令的过程。
若出现某些语法不可用的情况,则可通过在编译选项中添加命令修改编译器的语言标准:
Visual Studio系列产品
vs系列产品我更推荐vs2019和vscode。
这两个软件最大的特色是调试方便。现在也有很多集成开发环境(IDE)也能做到,取决于个人需求。在初学阶段,社区版已经能满足绝大多数需求。
我不喜欢用vs2022这个新版本的IDE,是因为新版本做了更加严密的封装,在反汇编和特殊情况的编译下会做出多余的行动。当然我并不是否定vs2022的功能,vs2022也有自己的特色。
vs的编辑界面,解决方案资源管理器
在vs的安装路径中找到
Microsoft Visual Studio\2019\Community\Common7\IDE\VC\VCProjectItems
在其中有一个newc++file.cpp
,这个是vs2019生成新的c++文件的模版。我们可以在外取一个同名的.cpp
文件,之后在里面添加各种我们想要的初始语句,之后再拷贝到这个路径进行替换即可(这个路径可能无法修改)。
例如我们加上一句#define _CRT_SECURE_NO_WARNINGS 1
,之后在使用vs时用这个模版生成的新文件中会带有这样一个语句。
生成的默认文件就会带有这样一句:
vs也可以更换
数据类型
c语言的使用方式是将现实问题转化为能用数据和数据之间的关系模拟的抽象模型。所以学习使用c语言(甚至是其他编程语言)都绕不开用于表示数据的变量和常量。
c语言的数据类型在c++又有另外的别称:内置类型。
char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整形
float //单精度浮点数
double //双精度浮点数
//C语言没有字符串类型
案例:
#include <stdio.h>
int main()
{printf("%d\n", sizeof(char));printf("%d\n", sizeof(short));printf("%d\n", sizeof(int));printf("%d\n", sizeof(long));printf("%d\n", sizeof(long long));printf("%d\n", sizeof(float));printf("%d\n", sizeof(double));printf("%d\n", sizeof(long double));//类型的使用char ch='w';return 0;
}
-
sizeof()
是一个操作符,可以返回括号内的数据占用的内存大小。 -
printf
函数用于打印信息在控制台,%d
是指定打印的格式,sizeof(type)
是被打印的对象。 -
char ch='w';
的意思是向内存申请大小为sizeof(char)
的空间来存放字符w
。字符用单引号''
引起来。
关于计算机存储单位,我们知道计算机能完成这么庞大的运算,离不开底层大量的0和1的支持。
计算机只能看懂二进制,编译器的工作就是将人写的代码转变成二进制。
存放一个0或一个1所需的空间称之为1 bit(1位)。
单位转换:
1byte=8bit (1字节 = 8位)
1kB=2^10byte=1024byte
1MB=1024kB
1GB=1024MB
1TB=1024GB
1PB=1024TB
c语言的标准中,sizeof(long)
≥ \geq ≥sizeof(int)
。long
占用字节数的大小取决于编译器的版本。
变量、常量
生活中的有些值是不变的(比如:圆周率,性别,身份证号码,血型等等)
有些值是可变的(比如:年龄,体重,薪资)。
不变的值,C语言中用常量的概念来表示,变得值C语言中用变量来表示。
定义变量的方法
//定义变量的格式:类型 变量名=初始值;
//例如
int age=18;
//声明变量的格式:类型 变量名;
//例如
float weight=45.10f;
对于这个语句float weight=45.10f;
若采用float weight=45.10;
,系统默认45.10
是double型,我们希望45.10
只是float型的数据,于是在浮点数后面加后缀f
。
这里声明变量最好先给他赋一个值并养成习惯,直接用没赋值的变量,vs会出错,Dev早期版本会产生随机数。
变量的命名
变量的命名遵循以下规则:
-
只能由字母(包括大写和小写)、数字和下划线(
_
)组成。 -
不能以数字开头。
-
长度不能超过63个字符。
-
变量名中区分大小写的。
-
变量名不能使用关键字。
其中自己再加上一条:
变量名最好有它自己的意义。
这个只是总结的,其他资料有可能有更详细的,但遵守这个是最普适的。
变量的分类
变量按作用域分可分为全局变量和局部变量。作用域暂时可以理解为离变量最近的{}
。
局部变量是在{}
内定义的变量,全局变量是在{}
之外定义的变量(狭义的理解),也可以理解为在函数外定义的变量。
#include <stdio.h>
int global = 2019;//全局变量
int main()
{int local = 2018;//局部变量//下面定义的global会不会有问题?int global = 2020;//局部变量printf("global = %d\n", global);return 0;
}
上面的局部变量global变量的定义其实没有什么问题的。
当局部变量和全局变量同名的时候,局部变量优先使用。但不可以将两个同一作用域的变量设置为同一个名字,会产生重名的错误。
变量的使用
变量可以当成一个存储数据的容器使用。包括修改、利用变量存储的数据。
#include <stdio.h>
int main()
{int num1 = 0;int num2 = 0;int sum = 0;printf("输入两个操作数:>");scanf("%d %d", &num1, &num2);sum = num1 + num2;printf("sum = %d\n", sum);return 0;
}
变量的作用域和生命周期
作用域
作用域(scope)是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的而限定这个名字的可用性的代码范围就是这个名字的作用域。
总结以下就是:一个变量,哪里可以用,哪里就是它的作用域。
-
局部变量的作用域是变量所在的局部范围。(即自己所在的
{}
内部) -
全局变量的作用域是整个工程。
例如:
#include<stdio>int main(){int a=1;{int b=2;printf("%d\n",b);}printf("%d\n",b);//b不在作用域,不可用return 0;
}
生命周期
变量的生命周期指的是变量的创建到变量的销毁之间的时间段。(比如c程序的生命周期指的是程创建到销毁的时间段)
-
局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。
-
全局变量的生命周期是:整个程序的生命周期。
一个程序的生命周期与
main
函数是一样的,而全局变量与main
函数也是一样的。
常量
常量即不可修改的量。c语言中的常量和变量的定义的形式有所差异。
c语言中的常量分为以下以下几种:
-
字面常量。例如
int a=10;
中的10。 -
const
修饰的常变量。例如const int a=10;
中的a
(a
本质上是一个变量,但具有了常属性不可以被改变。)。 -
#define
定义的标识符常量 -
枚举常量
枚举,就是一一列举所有可能性,生活中很多东西如性别、血型等情况有限可能。
#include <stdio.h>
//举例
enum Sex
{MALE,FEMALE,SECRET
};
//括号中的MALE,FEMALE,SECRET是枚举常量
int main()
{//字面常量演示3.14;//字面常量1000;//字面常量//const 修饰的常变量const float pai = 3.14f; //这里的pai是const修饰的常变量pai = 5.14;//是不能直接修改的!//#define的标识符常量 演示
#define MAX 100printf("max = %d\n", MAX);//枚举常量演示printf("%d\n", MALE);printf("%d\n", FEMALE);printf("%d\n", SECRET);//注:枚举常量的默认是从0开始,依次向下递增1的return 0;
}
上面例子上的 pai
被称为 const
修饰的常变量, const
修饰的常变量在c语言中只是在语法层面限制了变量 pai
不能直接被改变,但是 pai
本质上还是一个变量的,所以叫常变量。
const int a=10;
,a不可用于定义数组,即int arr[a];
和int arr[a]={0}
不允许。但总有特例。
在C99标准中引入变长数组的概念,可以使用变量来声明(但不能定义)。
典型的编译器如Dev-C++ 5.11的gcc支持。
大部分网站的OJ题支持c99。
最后,变长数组不能初始化。
操作符简单介绍
有了变量之后,还需要有某种操作来沟通部分或全部的变量,使得变量能真正用于解决问题。例如a
、b
代表两个数字,想用它们解决四则运算,则需要有四则运算符。
因此c语言提供了操作符。在有的资料上,操作符又称为运算符。
算术操作符:用于数学计算的操作符。两边都要有变量、常量或表达式(变量和操作符组成的集合)。
+
:加法。-
:减法。*
:乘法。/
:除法得到的是商,整型会自动舍弃小数部分,浮点型按精度返回。%
:取模(求余数),两个不能是整型,模正负与%
前的正负相同。
样例解析:
void f(){float a = 10/3;//等式右边都是整数,a为3.000000float b = 10.0f/3;//或float b = 10/3.0f;
}
形如10.0f
,这种是单精度浮点数表示法。我们希望用float
存储10.0
,10.0
默认为double型。double型的值赋值给float型变量,在vs中会报警告。
赋值操作符,用于修改变量。
=
:将右边的值赋值给左边的变量。例如int a; a=3;
只要不是跟在类型后面用于初始化,都是用于给变量赋值。+=
:例如:a+=2;
等价于a=a+2;
。下同。-=
、*=
、/=
、%=
单目操作符,只和一个操作数(可以是变量,也可以是变量和操作符组成的表示一个值的集合)组合的操作符。
&
:取地址(详见指针)。sizeof
:返回操作数的类型长度(以字节为单位)。例如sizeof(int)
就是返回int
型变量在计算机中占用的空间的大小。--
:分前置和后置。本质是变量=变量-1
的缩写,但因前置、后置有不同的执行顺序,前置是优先执行操作数再执行其他部分,后置则是优先执行其他部分再执行操作数。++
:和--
同理。本质是变量=变量+1
的缩写。*
:间接访问操作符(解引用操作符,和指针有关)。(类型)
:强制类型转换。例如:(char)65=='A')
关系操作符:用于数学意义上的比较操作符。靠返回真和假来判断。
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
逻辑操作符:用于组合
&& 逻辑与
|| 逻辑或
条件操作符(三目操作符)
exp1 ? exp2 : exp3
/*
等价于
if(exp1)exp2;
elseexp3;
*/
逗号表达式:从左到右依次计算,返回最后一个表达式expn的值。
exp1, exp2, ...,expn;
下标引用、函数调用和结构成员访问
[]
:用于表示数组的索引(或下标)。()
:有两个含义:1、(表达式)
表示这个表达式(暂时理解为语句的一部分)最先执行。2、子程序名(参数清单)
表示执行某个子程序(总的程序的一部分。见函数)。.
:用于结构体的成员调用(见下方结构体)。->
:用于结构体指针的结构体的成员的调用。
其他的操作符有的都是涉及二进制,放在另一篇详细解释。
语句
变量和操作符进行组合,便完成了一个功能,需要用;
结尾,这个用;
结尾的代码就是语句。
c语言的代码由语句和函数(子程序)组成,而函数的主干部分是语句。这些语句分成普通语句、选择语句和循环语句。普通语句就是以;
结尾的语句。
选择语句
if
后面的括号内的表达式如果判断为真,则执行if
后面的{}
内的语句(或{}
以及内部语句的代码块),为假则不执行。
若if
后没有{}
,则if
会自动匹配离它最近的语句。
而且if
还能和else if
以及else
组成多分支。
#include <stdio.h>
int main() {int coding = 0;printf("你会去敲代码吗?(选择0 or 1 or 2):>");scanf("%d", &coding);//单分支if (coding == 2) {//匹配后面的{}printf("三天打鱼两天晒网,啥也不能做,只能\n");}//2个分支if (coding == 1)//匹配最近的语句printf("坚持,你会有好offer\n");else{printf("放弃,回家卖红薯\n");}//3个及以上的分支printf("如果再来一次,你会去敲代码吗?(选择0 or 1 or 2):>");scanf("%d", &coding);if (coding == 2) {//匹配后面的{}printf("三天打鱼两天晒网,啥也不能做\n");}else if (coding == 1)//匹配最近的语句printf("坚持,你会有好offer\n");else {printf("放弃,回家卖红薯\n");}return 0;
}
循环语句
有一部分功能,需要执行很多次,同样地拷贝粘贴会增加代码长度。因此就有了循环语句:通过满足某种条件来执行特定的语句或代码块。
c语言实现循环的方式有4种:
while
for
do {} while
goto
这里只介绍while
,详见循环。
while
只要满足后面括号内的语句的返回值为真,后面的语句块会一直执行。
#include <stdio.h>
int main()
{int line = 0;while(line<=20000){line++;printf("我要继续努力敲代码\n");}if(line>20000)printf("好offer\n");return 0;
}
数组
既然有了变量,又会有新的问题:有的问题需要大量的变量,这时重复的拷贝粘贴会使代码变得特别长。
c语言中给了数组的定义:一组相同类型元素(变量)的集合。
数组定义
int arr[10] = {1,2,3,4,5,6,7,8,9,10};//定义一个整形数组,最多放10个元素
数组的下标
C语言规定:数组的每个元素都有一个下标,下标是从0开始的。
数组可以通过下标来访问的。
比如:
int arr[10] = {0};
//如果数组10个元素,下标的范围是0-9
数组简单使用
#include <stdio.h>
int main()
{int i = 0;int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };while (i < 10){printf("%d ", arr[i]);i++;}printf("\n");return 0;
}
printf("%d ", arr[i]);
的解释:
arr[i]
是用于访问下标为i的元素(下标访问操作符[]
),将arr
与i
结合在一起,让我们访问arr[i]
存放的元素。
所以arr
和数组中的内容4
是[]
这个操作符的操作数。
变长数组的概念
C99标准之前,数组的大小只能是常量指定。例如:
int a[10];
。
即使用#define
定义符号,在编译阶段也会全部替换回来,本质还是只能常量指定。
在C99标准中,引入一个变长数组的概念。变长数组允许数组的大小使用变量来指定,例如:
void f(){int n = 10;int a[n];
}
大部分网站的OJ题都支持C99。
此外,变长数组不能初始化。也就是说不允许这种写法:
int a[n]={0};
字符串和转义字符
字符串
像"Hello, world!\n"
这种由双引号(Double Quote)引起来的一串字符称为字符串字面值(String Literal),或者简称字符串。c语言表示字符用单引号。例如:'a'
,'2'
。
字符串是最后一个元素为\0
的char
型数组。
注:字符串的结束标志是一个\0
的转义字符,ASCII值为0。在计算字符串长度的时候\0
是结束标志,不算作字符串内容。
#include <stdio.h>
//下面代码,打印结果是什么?为什么?(突出'\0'的重要性)
int main()
{char arr1[] = "bit";//字符串里隐藏一个'\0'char arr2[] = {'b', 'i', 't'};//无'\0',只是将三个字符放进去char arr3[] = {'b', 'i', 't', '\0'};printf("%s\n", arr1);printf("%s\n", arr2);printf("%s\n", arr3);return 0;
}
打印结果(不同的设备情况可能不同):
bit
bit烫烫烫烫蘠it
bit
分析:
char arr1[] = "bit";
内部结构:
char arr2[] = {'b', 'i', 't'};
的结尾并不是\0
,也就不是字符串。
printf("%s\n", arr1);
:%s
的格式是打印字符串,直到检测到'\0'
结束。因arr2
内无'\0'
,系统找不到'\0'
,则继续访问内存直到找到\0
为止。而这个数组存放在内存中,前后都有空间,所以避免不了访问不属于自己的空间。
打印烫的原因:烫字的GBK编码正好都是0xcc
。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>int main()
{char arr4[] = { "烫" };char arr5[] = { "烬" };int i = 0;for (i = 0; i < 2; i++) {printf("%x ", arr4[i]);//十六进制打印}printf("\n");for (i = 0; i < 2; i++) {printf("%x ", arr5[i]);}return 0;
}
p.s. 函数strlen
返回值为size_t
( #define unsigned int size_t
),但它依旧是以\0
为结束标志。所以strlen(arr2)
的返回值是随机的(统计\0
之前的字符数)。
即使printf("%d",strlen(arr2));
成功输出,结果也无代表性。
转义字符
转义,即转变意思的意思。
案例:
#include <stdio.h>
int main()
{printf("c:\code\test.c\n");return 0;
}
输出:
这里就不得不提一下转义字符了。转义字符顾名思义就是转变意思。
下面看一些可能比较常用的转义字符。
\?
在书写连续多个问号时使用,防止他们被解析成三字母词。
比如:
??]
就是]
,在早期的一些编译器会将??]
解释成三字母词并转变成其他符号。
\'
用于表示字符常量
'
\"
用于表示一个字符串内部的双引号
\\
用于表示一个反斜杠,防止它被解释为一个转义序列符。
\a
警告字符,蜂鸣
\b
退格符。
例如。
#include <stdio.h> int main() {int i=0;printf("ab\b");scanf("%d",&i);printf("\n");printf("ab\bc");return 0; }
第5行输出ab,但光标夹在a、b中间,输入任意数字都会将b给覆盖。
第8行先输出ab,后经过
\b
退格,使光标夹在a、b中间,再输出c就会将b给覆盖掉。
\f
进纸符
\n
换行
\r
回车。感觉上更像Tab键,推进2~4个字节(空格)的空间。
\t
水平制表符
\v
垂直制表符
\ddd
ddd表示1~3个八进制的数字。 如:
\130
表示X。13 0 ( 8 ) = 1 × 8 2 + 3 × 8 1 + 0 = 8 8 ( 10 ) 130_{(8)}=1\times8^2+3\times8^1+0=88_{(10)} 130(8)=1×82+3×81+0=88(10),ASCII码值可知这个符号是
X
。在计算机中的字符被解释成十进制(详见下方的ASCII码表),每个字符规定一个值。
其中
\777
转换成十进制数则大于127(即ASCII码的最大码值),对字符来说太大,vs会警告。
\xdd
dd表示2个十六进制数字。 如:
\x30
是字符0
。
在了解这些转义字符后,再来看这个代码:
#include <stdio.h>
int main()
{//在屏幕上打印一个单引号'printf("%c\n", '\'');//在屏幕上打印一个字符串,字符串的内容是一个双引号“printf("%s\n", "\"");return 0;
}
若第5行这样写:printf("%c\n", ''');
,编译器会将前两个'
解释成一对,从而使后面的'
落单,这样一来编译器反而不能识别这个代码。
三个0的区别:
0
: 数字0
'0'
:字符0,ASCII值为48。
'\0'
:字符,ASCII值是0。
注释
-
代码中有不需要的代码可以直接删除,也可以注释掉。
-
代码中有些代码比较难懂,可以加一下注释文字。
可以理解为注释是写给程序员看的,编译器在预编译阶段会将注释删除。
注释有两种风格:
- c语言风格的注释
/*xxxxxx*/
缺陷:不能嵌套注释。
例如,
/*1 /* */2 */3
1和2进行匹配,使得3暴露在外。
-
C++风格的注释
//xxxxxxxx
- 一次只能注释一行。
函数
同样是代码块执行多次,但不能每次都写循环,这样会增加代码的长度,于是就有了函数。
因此函数的作用是代码复写(或者说重复利用)。
#include <stdio.h>
int main()
{int num1 = 0;int num2 = 0;int sum = 0;printf("输入两个操作数:>");scanf("%d %d", &num1, &num2);sum = num1 + num2;printf("sum = %d\n", sum);return 0;
}
上述代码,写成函数如下:
#include <stdio.h>
int Add(int x, int y)
{int z = x + y;return z;
}
int main()
{int num1 = 0;int num2 = 0;int sum = 0;printf("输入两个操作数:>");scanf("%d %d", &num1, &num2);sum = Add(num1, num2);printf("sum = %d\n", sum);return 0;
}
关于scanf的返回值
例如:
#include<stdio.h>int main(){int a,b;int c;c=scanf("%d %d",&a,&b);return 0;
}
scanf
是从键盘上获取数据到变量空间中,若输入成功,则scanf
返回成功填充的字符个数。
所以scanf
将读到的数据赋值给a
后返回1;给a
、b
返回2,即c的值为1或2。
所以scanf
的返回值为读到的数据个数。若读取失败则返回EOF
也就是-1。
stdio.h
给出的定义是
#define EOF (-1)
EOF:end of file,用于文件结束的标志。
让scanf
读取失败,在控制台输入Ctrl+z即可。部分编译器比如vs可能需要多输入几次。
常见关键字
c语言提供了丰富的关键字,这些关键字都是语言本身预先设定好的,用户自己是不能创造关键字的。
我的打算是学习时遇到一个关键字就做总结,在学完c语言的大部分核心内容之后,再出一篇c语言的关键字的总篇集,方便查询。
c语言的关键字:
auto break case char const continue default do double else enum
extern float for goto if int long register return short signed
sizeof static struct switch typedef union unsigned void volatile while
#define 定义常量和宏
在一篇文章看懂c语言-CSDN博客已经提到#define
的作用。这里再介绍一个和函数的功能十分类似的功能,也就是宏。
例如:
//define定义标识符常量
#define MAX 1000
//define定义宏
#define ADD(x, y) ((x)+(y))
int main()
{int sum = ADD(2, 3);sum = 10 * ADD(2, 3);return 0;
}
将这个代码在控制台通过gcc进行预编译即可得到如下结果:
#include
,#define
这种#
开头的符号称预处理指令,它不是关键字。
关于宏:宏虽然在使用上和函数类似都是代码重用,但毕竟是符号替换,在程序源码编写上存在区别。
宏的不标准写法:#define ADD(x, y) (x+y)
。将参数用()
括起来可以更准确,防止因为优先级与其他符号产生冲突。
指针
通过指针可以理解变量是如何在内存中存在的。
内存
内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的 。
所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。
为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。这种地址在c/c++中又把他称作指针。
为啥不分到最小单位bit?若这样分配,则相当于指定到某房间的第几平米处处理数据(太细不好处理),1个char类型的变量1个字节,再细的话不合理。
很多时候32位的二进制序列太长,我们将他们用十六进制表示。
每个内存单元都有自己的编号,编号称做地址,地址又称作指针。
所以编号、地址、指针三个说法是一回事。
地址的产生(可跳过)
当我们在32位机器上,有32根地址线(有物质形态的电线),该电线是通电的,通电后有高、低电平,高、低电平转换成数字信号就是高电平为1和低电平为0(也可以反逻辑即高电平为0和低电平为1)。
这种32位机器表示的32位二进制数能代表 2 32 2^{32} 232种电信号,此时32根电线产生的电信号,转换成数字信号的二进制序列可作为内存编号,通过内存编号可找到对应的内存空间。
所以内存分为一个个内存单元,每个内存单元都对应一个编号,编号由硬件电路产生。
一个内存单元是1个字节,这种内存单元可以给出一个地址。能产生 2 32 2^{32} 232个地址(或 2 32 2^{32} 232个二进制序列),即一个32位机器用于记录地址的数据有 2 32 2^{32} 232个,它们能够管理2^{32}
byte的空间。
而 2 32 2^{32} 232byte = 4 =4 =4GB。
在vs界面可发现x86处有一个配置管理器,通过更改配置管理平台,指针变量的地址存储空间会发生变化。
一般 X86 对应 32位(bit),X64 对应 64位(bit)。
变量的地址
#include <stdio.h>
int main()
{int num = 10;#//取出num的地址//注:这里num的4个字节,每个字节都有地址,//取出的是第一个字节的地址(较小的地址)printf("%p\n", &num);//打印地址,%p是以地址的形式打印return 0;return 0;
}
&num
实际占用4byte的空间(X64下是8byte),每个byte都有自己的地址,&num
只是取出第1个byte空间的地址。
取出的地址也是十六进制数,也需要内存空间存储,通常用
type* name = #
来存储。我们把name
称指针变量(存放地址也就是指针的变量),name
的类型是type*
。
*
说明name
是指针变量,type
说明name
指向的对象是type
类型。*
跟随变量还是类型没有具体要求。
指针的使用实例:
#include <stdio.h>
int main()
{int num = 10;int* p = #*p = 20;return 0;
}
&num
:获得num的地址。&
是取地址操作符。
*p
:*
是解引用操作符,解引用是p
里存地址,*p
就是解引用,它是通过p
里的地址找到这个地址指向的变量,对那个变量的存储的数据进行操作。
结构体
结构体是一些具有某种联系的变量、数组等的集合体。尽管不用结构体,将变量单独列出来通过运算也能展示关系。
比如描述学生,学生包含: 名字+年龄+性别+学号
这几项信息。
就可以用结构体来描述。
struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[15];//学号
};
复杂类型生成的复杂对象(类似数据类型生成的变量)不能使用单个内置类型来描述。
内置类型即c语言本身就具有的,比如:
char
,short
,int
,long
,long long
,float
,double
。
struct
是自定义类型,并不属于内置类型。
结构体的简单应用:
#include <stdio.h>
struct Stu {char name[20];//名字int age;//年龄char sex[5];//性别char id[15];//学号
};
int main() {//结构体变量的初始化struct Stu s = { "张三",20, "男", "20180101" };//.为结构成员访问操作符printf("name = %s age = %d sex = %s id = %s\n", s.name, s.age, s.sex, s.id);//->操作符,用于指针变量struct Stu* ps = &s;printf("name = %s age = %d sex = %s id = %s\n", (*ps).name, ps->age, ps->sex, ps -> id);return 0;
}
ps->name
可用(*ps).name
代替。即先找到对象,再访问成员。
通常引用成员:
结构体变量.成员
结构体指针->成员
看到这里,对c语言相比有了一个大概的框架。后面都是对某一类知识点做更详细的介绍。全篇下来整合成一篇完全适合小白的书籍完全可以。