文章目录
C语言基础1:初识C语言
前言:本章是对目录中的所有的内容简略概述,既点到为止,后续会将所有对应章节进行详细的介绍呢。
1.C语言简介
1.1什么是C语言
C语言是一门计算机语言,用于人与计算机之间的交流,广泛应用于底层开发。它的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。它是一门面向过程的计算机编程语言,与C++,Java等面向对象的编程语言有所不同,其编译器主要有Clang、GCC、WIN-TC、SUBLIME、MSVC、Turbo C等。
尽管C语言提供了许多低级处理的功能,但仍然保持着良好跨平台的特性,以一个标准规格写出的
C语言程序可在许多电脑平台上进行编译,甚至包含一些嵌入式处理器(单片机或称MCU)以及超级电脑等作业平台。
1.2C语言的发展
早期的二进制编程语言(正电1、负电0),一般由科学家来编程,采用010101010000111001这种的编程方式,科学家编程需要查手册,不同长度的01段表示不同的字符假设101000111表示ADD(ADD被称作助记符),用助记符来表达的语言叫汇编语言,同理用一句话表示了很多的汇编语言就叫做了B语言,接着发展为C语言,之后演变为C++(当发展到C、C++时,这类语言被称作高级语言)
二十世纪八十年代,为了避免各开发厂商用的C语言语法产生差异,由美国国家标准局为C语言制
定了一套完整的美国国家标准语法,称为ANSI C,作为C语言最初的标准。 目前2011年12月8日,国际标准化组织(ISO)和国际电工委员会(IEC)发布的C11标准是C语言的第三个官方标准,也是C语言的最新标准,该标准更好的支持了汉字函数名和汉字标识符,一定程度上实现了汉字编程。
2.第一个C语言程序
第一个C语言程序,我们打印一个hello world,熟悉下流程。
2.1创建项目
2.2添加源文件
.c是源文件,.h是头文件
2.3写代码
#include<stdio.h>
int main()
{
printf("Hello!\n");
return 0;
}
代码写完后,按Ctrl+F5(Fn)执行程序,而Ctrl+F10(Fn)表示调试程序,继续按程序上的光标,程序会逐过程执行。
如果执行后,cmd窗口一闪而过,可尝试对以下进行设置
解释:
(1)#include<stdio.h>
① # 是一种指令形式
②include指包含,包含一个叫做stdio.h的文件
③ <> 尖括号是C语言的一种格式
④stdio.h:std标准(standard),i(input)o(output)标准输入输出
(2)main()函数是程序的入口,也被叫做主函数,一个工程中主函数有且仅有一个
①写程序不能没有main()函数
②主函数不可以设置多个,设置多个程序无法对入口进行选择
(3)int 是整形的意思,main前面的int表示main函数调用返回一个整形值。还有一种写法 void main()的写法,这种写法,不是不可以,而是已经过时了。
(4)return 0; 返回0,因为我们定义的是int main()的整形函数,程序运行完,需要返回一个整数表明程序运行完毕,此处返回的是0,表示程序运行完毕。
(5){}表示是程序运行的位置,相当于在这里完成程序
(6)printf即print function打印函数的意思,它是库函数,库函数是C语言本身提供给我们使用的函数。
(7)\n表示换行
3.数据类型
数据类型 | 注释 | 所占内存字节 |
---|---|---|
char | 字符数据类型 | 1 |
汉字 | 字符数据类型 | 2 |
short(int) | 短整型 | 2 |
int | 整形 | 4/8 |
long | 长整型 | 4/8 |
long long | 更长的整形 | 8 |
float | 单精度浮点数 | 4 |
double | 双精度浮点数 | 8 |
long double | 长 双精度浮点数 | 8 |
之所以存在这么多的类型,其实是为了更加丰富的表达生活中的各种值。
#include<stdio.h>
int main()
{
//向内存申请一个空间(空间的大小后续说),起名叫ch,并把字符A存入
char ch = 'A';
int age = 20;
//打印字符格式数据,打印整形十进制数据
float f = 5.00;
double d = 5.00;
printf(" %C\n %d\n %f\n %lf\n", ch,age,f,d);
return 0;
}
上面讲到向内存空间申请大小,那么不同的字符类型,分别占用内存空间多少字节呢,我们可以用下面这个代码打出
#include<stdio.h>
int main()
{
//整型
printf("%d\n", sizeof(short));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(long));
printf("%d\n\n", sizeof(long long));
//浮点型
printf("%d\n", sizeof(float));
printf("%d\n", sizeof(double));
printf("%d\n\n", sizeof(long double));
//字符型
printf("%d\n", sizeof(char));
return 0;
}
我们从程序运算结果中可以看到,不同的字符类型所申请的存储大小都不一样,而这些数值的单位叫做字节(Byte),不同大小的字节存储的bit位也不一样
存储单位 | 名称 | 单位换算 | 备注 |
---|---|---|---|
bit | 比特 | 1B=8bit | 计算机中最小的存储单位,可以理解存储电信号(正电1,负电0) |
Byte | 字节 | 存储8个二进制位 | |
KB | 千字节 | 1KB=1024Byte | |
MB | 兆字节 | 1MB=1024KB | |
GB | 千兆字节 | 1GB=1024MB | |
TB | 太字节 | 1TB=1024GB | |
PB | 拍字节 | 1PB=1024TB | |
EB | 艾字节 | 1EB=1024PB | |
上述程序运行, printf("%d\n", sizeof(long)); 的运行结果是4,但实际运行结果可能是4或8,可能和计算机的运行位数有关系,另外: | |||
C语言标准规定,sizeof(long)>=sizeof(short),运算的结果取决于平台。 |
4.变量、常量
生活中的有些值是不变的(比如:圆周率,性别,身份证号码,血型等等),但有些值是可变的(比如:年龄,体重,身高)。
对于不变的值,C语言中用常量的概念来表示,变得值C语言中用变量来表示。
4.1定义的方式
定义整形、浮点型、字符型示例:
int age = 20;
float heigh = 160.5f;
char ch = 'a';
4.2变量的分类
4.2.1全局变量和局部变量
全局变量和局部变量通过几个例子来说明
#include <stdio.h>
//全局变量,定义在代码块({})之外的变量
int global = 20;
int main()
{
//局部变量,定义在代码块({})内部的变量
int local = 10;
printf("global = %d\nlocal = %d\n\n", global,local);
//当局部变量和全局变量同名的时候,局部变量优先使用。建议不要相同容易误会
int global = 30;//局部变量
printf("global = %d\n", global);
return 0;
}
当局部变量和全局变量同名时,局部变量优先使用,建议变量之间不要相同,容易误会。
修改代码如下,将局部变量放在其代码块中,对于该变量的代码块之外打印会报错,这说明在局部范围定义的,无法在局部范围之外使用,
全局变量在主函数外定义后,可以在主函数内直接使用
4.2.2变量的使用
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
int sum = 0;
printf("输入两个操作数:");
scanf("%d %d", &a, &b);
sum = a + b;
printf("sum = %d\n", sum);
return 0;
}
scanf("%d %d", &a, &b);
&取地址符号,意思是取a的 地址 取出来放在这里。
我们看到程序编译的过程中出现了报错,我们看报错的提示是scanf可能是不安全的,可以使用scanf_s来消除告警,scanf是C语言提供的,而scanf_s不是标准C语言提供的,而是由VS编译器提供的。因此如果将scanf_s这段代码放到了其他编译环境,例如gcc中,就会无法运行,说明代码不具备跨平台性,和可移植性。我们可以将告警字段粘出来,然后写在第一行(必须是在第一行,不然会出错)。
这里也会有个问题,每当我们用scanf等库文件时,都续要加这么一句话,就会比较繁琐,我们可以采用这种方式,修改 newc++file.cpp ,里面定义好需要内置的代码,这样我们在每次创建项目的时候自动生成这一段代码。
我们修改这个文件后,在里面添上这样一句话,之后每次新建项目的时候都会自动包含这句话了。
C语言语法规定,变量要定义在当前代码块的最前面,否则就会报下面这样的错误。下面是错误的演示,将 int c;没有定义在最前面
4.2.3变量的作用域和生命周期
4.2.3.1作用域
(1)什么是作用域:
作用域(scope)是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用
的,简单理解为哪里可以用,哪里就是它的作用域。
而限定这个名字的可用性的代码范围就是这个名字的作用域。
(2)变量的作用域:
①局部变量的作用域是变量所在的局部范围。
②全局变量的作用域是整个工程。
(3)全局变量举例:
①示例一:
定义全局变量c,定义函数f,打印c的值,我们可以看到由于全局变量的定义,函数f,即后续的main函数调用后,都有用。
②示例二:
项目工程文件中举例:先在同一个工程文件中新建源文件,然后再当前源文件中调用同一个工程文件下的源文件,证明全局变量在整个工程中。
对于同一个工程内的其他源文件的调用,这里需要用到 声明外部的标识符extern,用于声明同一工程文件中其他源文件的使用。
4.2.3.1生命周期
变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段
1.局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。
2.全局变量的生命周期是:整个程序的生命周期
4.3常量
C语言中的常量和变量的定义的形式有所差异,C语言中的常量分为以下以下几种:
①字面常量
②const修饰的常变量
③#define 定义的标识符常量
④枚举常量
4.3.1字面常量
字面常量:直观写出来的值,叫做字面常量,例如2、100等。
4.3.2const修饰的常变量
(1)const 修饰的常变量,其本质上还是变量。被const修饰的常变量无法继续被赋值。
(2)数组中的常量也不可以用常变量来代替
如果加上const,依然会报错,因为本质上被const修饰的常变量依然是变量。
4.3.3 #define 定义的标识符常量
4.3.4枚举常量
枚举常量:一一列举,采用enum,枚举常量是有值的,他们不可以被改变,使用方式如下。
5. 字符串+转义字符+注释
5.1字符串
字符串:由双引号(Double Quote)引起来的一串字符称为字符串字面值(String Literal),或者简称字符串。例如:“hello!”、“a b c”、“”(空字符串)、
至于为什么这样呢,我们可以通过调试来看一下:
首先按Ctrl+F10(Fn),让程序进入调试状态,不然后面找不到监视按钮,然后如下图进行设置:
添加要监视的值,然后将他们进行展开
我们可以看到arry1中存储了4个字符:a、b、c、\0,最后一个字符是 ‘\0’ ,儿arry2中存储了3个字符:a、b、c
从上面可以得出,"abc"字符中实际存放的是 ‘a’,‘b’,‘c’,‘\0’ 这4个字符,此处的 ‘\0’ 表示字符串的结束标志,arry2第二种写法种,在结束标志由于不清楚放置的是什么东西,打印的是随机值。
结论:字符串的结束标志是一个 \0 的转义字符。在计算字符串长度的时候 \0 是结束标志, 不算作字符串内容。
另外,这里为什么0就是 ‘\0’ 呢,这就涉及到ASCII码(美国信息交换标准代码)了。可以从ASCII表中看到:十进制的0对应的字符是转移字符 \0 ,其他框中的是常用的一些字符对照表。
5.2 转义字符
转义字符我们可以理解就是转变原来的意思,这里如下图举个例子,我们可以清楚的看到两次打印的china,由于加了 \ ,n 的意思已经失去了原来的字母的意思,而是变成了换行。
打印某个路径,“\t"水平制表符,相当于键盘的table键。
转义字符 | 注释 |
---|---|
? | 在书写连续多个问号时使用,防止他们被解析成三字母词 |
’ | 用于表示字符常量’ |
\“ | 用于表示一个字符串内部的双引号 |
| 用于表示一个反斜杠,防止它被解释为一个转义序列符。 | |
\a | 警告字符,蜂鸣 |
\b | 退格符 |
\f | 进纸符 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\d | dd ddd表示1~3个八进制的数字。 如: \130 X |
\x | dd dd表示2个十六进制数字。 如: \x30 0 |
上面的打印字符中有个框框,表示的是这个字符无法显示,这个和 \13 这个字符有关系,查看上面表格 \ddd 表示1-3个八进制的数字,可以用下面的数字举例:打印字符:\52 但实际是 * | |
其实结果是52表示的是一个八进制代表的十进制数,这个十进制作为ASCII码对应的 字符 。52(八进制)转换成十进制是42,查ASCII表,可以发现是 * ,所以上面屏幕打印的符号是 *。 | |
5.3注释
代码中有不需要的代码可以直接删除,也可以注释掉,如果有些代码比较难懂,可以加一下注释文字,注释的使用方法如下图,但是需要注意的是:
C语言的注释风格不可以嵌套,嵌套后会有异常如下图的右图
6. 选择语句
这里举例一下:①高考成绩高→考上大学②高考成绩不高→没考上大学
7. 循环语句
循环语句有很多,这里用while循环举例:1+2+3+···+? < 100,求?的值,这里设置了个循环,只要sum的总和小于100,i 就在原来的基础上再加一,然后再加入之前的和中去比较,直到sum超过了100为止。
8. 函数
定义一个函数,将两数相加,之后在使用的时候,直接进行调用即可。
9. 数组
数组的定义:一组相同类型元素的组合。 在编程的过程中,如果要存放1-10个数组,可以用数组进行存放。数组的下标是从0开始。
10. 操作符
操作符 | 举例 |
---|---|
算术操作符 | + - * / %(取余) |
移位操作符 | >> << |
位操作符 | & (按位与) ^ (按位异或) |(按位或) |
赋值操作符 | = += -= *= /= &= ^= |= >>= <<= |
单目操作符 | 注解 |
---|---|
! | 逻辑反操作 |
- |负值
+|正值
&|取地址
sizeof|操作数的类型长度(以字节为单位)
~|对一个数的二进制按位取反
–|前置、后置–
++|前置、后置++
*|间接访问操作符(解引用操作符)
(类型)|强制类型转换
关系操作符
|
注解
|
---|
>= < <=|
!=|用于测试“不相等”
==|用于测试“相等”
逻辑操作符 | 注解 |
---|---|
&& | 逻辑与 |
|| | 逻辑或 |
条件操作符 | 注解 |
---|---|
exp1 ? exp2 : exp3 |
逗号表达式 | 注解 |
---|---|
exp1, exp2, exp3, …expN |
下标引用、函数调用和结构成员 | 注解 |
---|---|
[] () . -> |
10.1 与、或、异或举例
10.2 sizeof举例
sizeof用于计算字符所占存储单元的大小,使用的过程中不可以用 sizeof int会出错,需要加对计算的量加上()。
10.3 取反运算举例
目前可以看到,对5和9进行取反运算,为什么得出的结果是-6、-10?
(1)负数二进制的三种表现形式
①源码:负数绝对值的二进制(符号位为1)
②反码:源码取反(符号位不动)
③补码:反码 + 1
(2)计算过程:
①5的2进制:0000 0101
② 取反得负数的补码:1111 1010(只要是整数,内存中存储的都是二进制的补码(正数的补码就是原码本身,负数的补码是其反码加1),这里负数也包含再整数,因此需要转成原码)
③求负数的反码:已知负数的补码:1111 1010 ,那么源码取反得到反码,反码+1得到补码,因此需要 倒置过来求源码,补码减1得到反码:
1111 1010 -1 =1111 1001
④求负数的源码:反码取反得到源码 取反(符号位不变,其他位取反)
1000 0110(也就是-6)
10.4 精度的丢失举例
10.5 逻辑操作符举例
10.6 条件操作符举例
11. 常见关键字
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|
11.1 部分关键字举例
(1)auto:当我们再{}中创建一个 int a;
的局部变量时,auto其实被隐藏掉了,正常应该是 auto int a;
,它相当于是在再这个作用域自动创建,然后自动销毁,因此定义的这个a也叫做自动变量。
(2)register: resgister int a;
,建议把a定义成寄存器变量。
(3)sigend:int 定义的变量是有符号的,当我们用int定义变量的时候,实际上是将sigend省略掉了了。
(4)unsigend:当我们定义无符号变量时,定义变量时可以加上unsiged。
(5)typedef:类型定义,可以理解为类型重命名。
11.2 static举例
static :用来修饰变量与函数
(1)修饰局部变量-称为静态局部变量
(2)修饰全局变量-称为静态全局变量
(3)修饰函数-称为静态变量
11.2.1 static 修饰局部变量
这是一个正常的程序的运算结果,通过函数将a=2打印了5次。
在函数中,将局 部变量a加上static 修饰之后,局部变量a再每次被调用后没有被销毁,而是继续使用了,它的生命周期被延长 。
11.2.2 static 修饰全局变量:
在工程文件中,定义一个新的原文建,里面存放变量 c=100;
在含有主函数的源文件声明后,可以看出打印结果是c的值100
修改工程文件,修饰全局变量c:
运行后报错,原因是因为: static 修饰全局变量,改变了全局变量的作用域,让静态的全局变量只能在自己所在的源文件内部使用,出了源文件,就无法再使用。
11.2.3 static 修饰函数
定义一个源文件,源文件中写一个计算两个数相加的函数。
将函数用 static 进行修饰
static 修饰函数会改变函数的链接属性,如果一个函数没有被static修饰的时候,它是有外部的链接属性。被修饰后,外部的链接属性会转换为内部的链接属性。
12. define 定义常量和宏
define 定义标识符常量
define 定义宏
13. 指针
内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的。
所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。
为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地
址。
在生活中,当我们住宾馆的时候, 我们可以把住进去的人理解为存储的数据,而把住人的房间的门牌号看作是存储数据的地址。 那么当我们存储数据的时候首先有两个问题需要考虑:①地址的编号从哪里开始?②划分的每个存储单元格的数据给它划分多大合适?
当我们用电脑的时候,我们在选择电脑的时候会知道它是32位还是64位的,这里的32位是指电脑拥有32根地址线(数据线),电脑通电后会有正(1)、负电荷(0),那么将电信号转换成数字信号,32根地址线会有0000 0000 0000 0000 0000 0000 0000 0000的位数,也就是232,那我们就可以产生 232个内存编号,可以如下图:
现在我们知道32根地址线可以产生232个地址,现在我们需要考虑内存单元格划分多大合适呢,目前有(bit、Byte、KB、MB、GB),假设①一个空间就是一个bit,也就是232bit大小的内存,折合成GB也就是0.5GB,内存会有些小。②一个空间就是一个Byte,也就是232Byte大小的内存,折合成GB也就是4GB,内存差不过刚好。
下面举例指针,顶定义一个变量 a,赋值为10,打印存放变量a的地址单元。
相当于这样一个过程:
在32位系统的电脑上,一个指针变量的大小是32(位)÷8(比特)=4(Byte),同理,64位的机器拥有64根地址线,每根地址线能产生0、1这样的2进制,那么÷8后就说明64位系统的电脑上,一个指针变量的大小是8字节。
由于 目前的编译器的平台位数是32位的,打印指针p的所占内存的大小为4个字节。
当我们把编译器的平台位数改成64为后,指针p的所占内存的大小为8个字节。
我们把瓶体位数改后,我们再看看存储其他类型的数据的指针所占内存空间也是8个字节。说明指针大小在32位平台是4个字节,64位平台是8个字节,与所存储的指针变量类型没有关系。
14. 结构体
对于描述复杂对象,C语言中用结构体:结构体是C语言中特别重要的知识点,结构体使得C语言有能力描述复杂类型。
比如描述学生,学生包含: 名字+年龄+性别+学号 这几项信息,这里只能使用结构体来描述了。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
struct human
{
char name[10];
int age;
char sex[5];
float height;
};
int main()
{
struct human Zhang3 = { "张三",20 , "男" , 1.73 };
//创建该类型的结构体变量
struct human* pb = &Zhang3;
//. 结构体变量.成员
printf("人名:%s\n", Zhang3.name);
printf("年龄:%d\n", Zhang3.age);
printf("性别:%s\n", Zhang3.sex);
printf("身高:%f米\n\n", Zhang3.height);
printf("修改名字、年龄:\n");
strcpy(Zhang3.name,"张四");
Zhang3.age = 21;
printf("人名:%s\n", Zhang3.name);
printf("年龄:%d\n", Zhang3.age);
//利用结构体变量pb打印出性别、身高
// -> 结构体指针->成员
printf("性别:%s\n", pb->sex);
printf("身高:%f米\n\n", pb->height);
//另外一种打印方式
printf("性别:%s\n", (*pb).sex);
printf("身高:%f米\n", (*pb).height);
return 0;
}