【通俗易懂】C语言教程----初识C语言
1.什么是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等。
2. 第一个C语言程序
#include <stdio.h>//引用头文件,使我们可以使用C语言库本身就已经提供给我们的函数,即库函数 int main()//main()是主函数 { printf("hello world\n");//printf()是输出函数,'\n'是换行的意思 return 0;//使程序退出,0的意思是程序正常退出 } //解释: //main函数是程序的入口 //一个工程中main函数有且仅有一个
程序执行结果:
3. 数据类型
char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整形
float //单精度浮点数
double //双精度浮点数
//C语言有没有字符串类型?
答案:C语言本身并没有字符串类型,我们在C语言程序中使用的字符串实际上是字符数组,即多个字符构成的就是字符串!
下面是字符串的两种定义方式:
#include<stdio.h> int main() { char string1[] = "abc"; char string2[] = { 'a','b','c' }; return 0; }
这两种方式的区别并不在此处进行详细的讲解,在后面会进行详细的讲解,此处只是希望大家能够了解即可!
- 为什么出现这么的类型?
答案:一方面是能够存储更加多样的数据,便于进行数据处理;另一方面的原因就是为了能够更好节约我们的内存空间!至于为什么会这么说呢,后面关于不同数据类型所占的内存空间大小的时候大家能够了解到,并且对其能够有一个更加深入的了解。
- 每种类型的大小是多少?
#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)); return 0; }
下面即为代码的运行结果:
类型的使用:
char ch = 'w'; int weight = 120; int salary = 20000;
3. 变量、常量
生活中的有些值是不变的(比如:圆周率,性别,身份证号码,血型等等)
有些值是可变的(比如:年龄,体重,薪资)。
不变的值,C语言中用常量的概念来表示,变的值C语言中用变量来表示。
3.1 定义变量的方法
int age = 150; float weight = 45.5f; char ch = 'w';
3.2 变量的分类
- 局部变量
- 全局变量
#include <stdio.h> int global = 2019;//全局变量 int main() { int local = 2018;//局部变量 //下面定义的global会不会有问题? int global = 2020;//局部变量 printf("global = %d\n", global); return 0; }
输出结果展示:
总结:
上面的局部变量global变量的定义其实没有什么问题的!
当局部变量和全局变量同名的时候,局部变量优先使用,这就是我们常说的局部优先原则!
3.3 变量的使用
#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; }
此处简单带大家了解一下printf()函数和scanf()函数!
printf()函数:输出函数。在双引号内的将进行输出,而%就是控制变量的输出格式,即起到了格式控制的作用,比如%d就是将变量以整型的形式进行打印输出到屏幕上来,%c则是以字符型的形式进行打印输出!在逗号后面的就是我们想要输出的变量,在这个地方需要大家进行注意,%格式控制一定要与后面的变量进行意义对应!
scanf()函数:输入函数。跟上面的一样,%也是进行格式控制,不过此处与前面的区别就是此处是进行输入的格式控制,比如%d,就是将我们的输入的内容以整型的形式存储到我们的变量中,此处与前面也一样,都要与逗号后面的变量进行严格的对应,但此处仍然有一个需要大家进行注意的点,比如我们在输入时,我们在%d %d两个%d中间有一个空格进行分隔,那么我们在通过键盘进行输入的时候也要在两个变量中间进行分隔,即我们的输入要与双引号之间的内容进行严格第对应!
由于本节旨在带大家进行初识C语言,所以不进行过多的讲解!
3.4 变量的作用域和生命周期
(1)作用域
作用域(scope)是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用 的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
- 1. 局部变量的作用域是变量所在的局部范围。
- 2. 全局变量的作用域是整个工程。
(2)生命周期
变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段
- 1. 局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。
- 2. 全局变量的生命周期是:整个程序的生命周期。
3.5 常量
C语言中的常量和变量的定义的形式有所差异。
C语言中的常量分为以下以下几种:
- 字面常量
- const 修饰的常变量
- #define 定义的标识符常量
- 枚举常量
const修饰的常变量:
#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 100 printf("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 本质上还是一个变量的,所以叫常变量。
4. 字符串+转义字符+注释
4.1 字符串
"hello bit.\n"
这种由双引号(Double Quote)引起来的一串字符称为字符串字面值(String Literal),或者简称字符 串。
注:字符串的结束标志是一个 \0 的转义字符。在计算字符串长度的时候 \0 是结束标志,不算作字符串 内容。
#include <stdio.h> //下面代码,打印结果是什么?为什么?(突出'\0'的重要性) int main() { char arr1[] = "bit"; char arr2[] = { 'b', 'i', 't' }; char arr3[] = { 'b', 'i', 't','\0'}; printf("%s\n", arr1); printf("%s\n", arr2); printf("%s\n", arr3); return 0; }
在着我们可以看到,我们在输出arr2时出现了乱码形式,为什么会出现这种情况呢?因为在arr2中并不包含字符串的结束标志即'\0',所以会出现上面的乱码!
注意:
1、在我们用printf()函数以字符串形式进行输出时,只有遇到'\0'才会停止输出!
2、我们在使用上面arr1这种形式进行定义字符串时,在""里面的字符串中就已经自动包含了字符串的结束标志即'\0'!
3、当我们在使用{}字符串的定义的 方式时我们一定要记得加上字符串的结束标志,虽然我们有时候用不到,但这却是我们必须要做的,这也是一个合格的程序员必备的素养,当然,有的时候确实并不需要加上,在新手期间,建议还是加上。
4.2 转义字符
假如我们要在屏幕上打印一个目录: c:\code\test.c
我们该如何写代码?
#include <stdio.h> int main() { printf("c:\code\test.c\n"); return 0; }
但其实打印结果是这样的:
这里就不得不提一下转义字符了。转义字符顾名思义就是转变意思。
下面看一些转义字符。
下面是一个简单的练习:
#include <stdio.h> int main() { //问题1:在屏幕上打印一个单引号',怎么做? //问题2:在屏幕上打印一个字符串,字符串的内容是一个双引号“,怎么做? printf("%c\n", '\''); printf("%s\n", "\""); return 0; }
下面是一道笔试题,带大家来了解一下吧!
//程序输出什么? #include <stdio.h> int main() { printf("%d\n", strlen("abcdef")); // \62被解析成一个转义字符 printf("%d\n", strlen("c:\test\628\test.c")); return 0; }
下面是我们在VS上的运行结果:
为什么会得到上面的结果呢?
我们首先来了解一下strlen(),这个函数的作用是求字符串的长度,当遇到字符串的结束标志时将停止,但是大家需要注意一点,就是字符串的结束标志'\0'本身并不作为字符串长度的一部分。
我们先看上面这一段代码,strlen()括号中的字符串内容为abcdef还有字符串的结束标志'\0',但是由于字符串的结束标志并不作为字符串长度的一部分,所以字符串的长度为6。
接下来我们来看下一段代码,上面我们已经了解了转义字符,此处需要给大家明确一个概念,转义字符被我们看作成是一个字符,即使它的形式是\后面跟了1个或者多个字符,但strlen()函数只把它看成是一个字符。所以为什么我们的编译器给出上面的结果也就不难理解了,在上面的结果也就不难理解了,\t是一个转义字符,\62也是一个转义字符,为什么8不跟着一块呢?因为\后面跟的是八进制,即只包含0到7的数字,不能包含8,所以8不被包含在内!后面还有已给\t也是一个转义字符!最终得出14的结果!
5. 注释
- 代码中有不需要的代码可以直接删除,也可以注释掉
- 代码中有些代码比较难懂,可以加一下注释文字
比如:
#include <stdio.h> int Add(int x, int y) { return x + y; } /*C语言风格注释 int Sub(int x, int y) { return x-y; } */ int main() { //C++注释风格 //int a = 10; //调用Add函数,完成加法 printf("%d\n", Add(1, 2)); return 0; }
注释有两种风格:
- C语言风格的注释 /*xxxxxx*/ 缺陷:不能嵌套注释
- C++风格的注释 //xxxxxxxx 可以注释一行也可以注释多行
6. 选择语句
如果你好好学习,校招时拿一个好offer,走上人生巅峰,你女朋友继续陪在你的身边!
如果你不学习,毕业等于失业,你女朋友跟你分手了!
这就是选择!
#include <stdio.h> int main() { int coding = 0; printf("你会去敲代码吗?(选择1 or 0):>"); scanf("%d", &coding); if (coding == 1) { printf("坚持,你会有好offer,女朋友陪在身边\n"); } else { printf("毕业即失业,女朋友跟你分手\n"); } return 0; }
下面是运行截图:
7. 循环语句
C语言中如何实现循环呢?
- while语句-讲解
- for语句(后期讲)
- do ... while语句(后期讲)
#include <stdio.h> int main() { int line = 0; while (line <= 20000) { line++; printf("我要继续努力敲代码\n"); } if (line > 20000) printf("好offer\n"); return 0; }
8. 函数
#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; }
函数的特点就是简化代码,代码复用。
9. 数组
要存储1-10的数字,怎么存储?
C语言中给了数组的定义:一组相同类型元素的集合.
9.1 数组定义
int arr[10] = {1,2,3,4,5,6,7,8,9,10};//定义一个整形数组,最多放10个元素
9.2 数组的下标
C语言规定:数组的每个元素都有一个下标,下标是从0开始的。
数组可以通过下标来访问的。
比如:
int arr[10] = {0}; //如果数组10个元素,下标的范围是0-9
9.3 数组的使用
#include <stdio.h> int main() { int i = 0; int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; for (i = 0; i < 10; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }
运行结果如下图所示:
10. 操作符
算术操作符
- + 加
- - 减
- * 乘
- / 除
- % 取余
运算符有很多,此处不再一一列举!后面的内容中会讲!
#include<stdio.h> int main() { int a = 10; int b = 5; int c = 3; printf("%d\n", a + b); printf("%d\n", a - b); printf("%d\n", a * b); printf("%d\n", a / b); printf("%d\n", a % c); return 0; }
下面是运行结果:
11. 常见关键字
- 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 关键字 typedef
typedef 顾名思义是类型定义,这里应该理解为类型重命名。
比如:
typedef unsigned int uint_32; int main() { //观察num1和num2,这两个变量的类型是一样的 unsigned int num1 = 0; uint_32 num2 = 0; return 0; }
11.2 关键字static
在C语言中:
static是用来修饰变量和函数的
- 修饰局部变量-称为静态局部变量
- 修饰全局变量-称为静态全局变量
- 修饰函数-称为静态函数
11.2.1 修饰局部变量
//代码1 #include <stdio.h> void test() { int i = 0; i++; printf("%d ", i); } int main() { int i = 0; for (i = 0; i < 10; i++) { test(); } return 0; } //代码2 #include <stdio.h> void test() { //static修饰局部变量 static int i = 0; i++; printf("%d ", i); } int main() { int i = 0; for (i = 0; i < 10; i++) { test(); } return 0; }
代码1运行结果:
代码2运行结果:
结论:
static修饰局部变量改变了变量的生命周期 让静态局部变量出了作用域依然存在,到程序结束,生命周期才结束。
11.2.2 修饰全局变量
注:add.c和test.c代表两个不同的源文件
//代码1 //add.c int g_val = 2018; //test.c int main() { printf("%d\n", g_val); return 0; } //代码2 //add.c static int g_val = 2018; //test.c int main() { printf("%d\n", g_val); return 0; }
代码1正常,代码2在编译的时候会出现连接性错误。
结论:
一个全局变量被static修饰,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使用。
11.2.3 修饰函数
//代码1 //add.c int Add(int x, int y) { return c + y; } //test.c int main() { printf("%d\n", Add(2, 3)); return 0; } //代码2 //add.c static int Add(int x, int y) { return c + y; } //test.c int main() { printf("%d\n", Add(2, 3)); return 0; }
代码1正常,代码2在编译的时候会出现连接性错误.
结论:
一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。
12. #define 定义常量和宏
//define定义标识符常量 #define MAX 1000 //define定义宏 #define ADD(x, y) ((x)+(y)) #include <stdio.h> int main() { int sum = ADD(2, 3); printf("sum = %d\n", sum); sum = 10 * ADD(2, 3); printf("sum = %d\n", sum); return 0; }
运行结果:
13. 指针
13.1 内存
内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的 。
所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。
为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。
变量是创建内存中的(在内存中分配空间的),每个内存单元都有地址,所以变量也是有地址的。 取出变量地址如下:
#include <stdio.h> int main() { int num = 10; #//取出num的地址 //注:这里num的4个字节,每个字节都有地址,取出的是第一个字节的地址(较小的地址) printf("%p\n", &num);//打印地址,%p是以地址的形式打印 return 0; }
那地址如何存储,需要定义指针变量。
int num = 10; int *p;//p为一个整形指针变量 p = #
指针的使用实例:
#include <stdio.h> int main() { int num = 10; int* p = # *p = 20; return 0; }
以整形指针举例,可以推广到其他类型,如:
#include <stdio.h> int main() { char ch = 'w'; char* pc = &ch; *pc = 'q'; printf("%c\n", ch); return 0; }
13.2 指针变量的大小
#include <stdio.h> //指针变量的大小取决于地址的大小 //32位平台下地址是32个bit位(即4个字节) //64位平台下地址是64个bit位(即8个字节) int main() { printf("%d\n", sizeof(char*)); printf("%d\n", sizeof(short*)); printf("%d\n", sizeof(int*)); printf("%d\n", sizeof(double*)); return 0; }
上述即为32位平台!
上述为64位平台!
结论:指针大小在32位平台是4个字节,64位平台是8个字节。
14. 结构体
结构体是C语言中特别重要的知识点,结构体使得C语言有能力描述复杂类型。 比如描述学生,学生包含: 名字+年龄+性别+学号 这几项信息。 这里只能使用结构体来描述了。
例如:
struct Stu { char name[20];//名字 int age; //年龄 char sex[5]; //性别 char id[15]; //学号 };
结构体的初始化:
//打印结构体信息 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);