1.什么是C语言?
1.1初识C语言
C语言是一门通用计算机编程语言,广泛应用于底层开发。C语言的设计目标是提供一种能简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。
C语言是一门面向过程的计算机编程语言,与C++,Java等面向对象的编程语言有所不同。
1.2 第一个C语言程序
#include <stdio.h> int main() /*main函数是程序的入口,一个程序中有且只有一个main函数;*/ { /*printf是一个库函数——C语言编译器提供的一个现成的函数,可以直接使用,*/ printf("Hello world!"); /*其功能就是在屏幕上打印数据,但是在使用之前要包含头文件—stdio.h;*/ return 0; }
其程序运行结果如下:
2.初识数据类型
2.1 数据有哪些类型?
生活中,我们的姓名、性别、手机号码、家庭住址......以及世间万物只要我们能用键盘上的字符表示出来的,我们都可以称它们为数据,它们总归都是用字母、数字、汉字、标点符号等字符表示出来的。所以自然而然可以将这些数据分类。
因此会有以下基本的数据类型:
char-字符数据类型(汉字、数字、英文、符号……只要键盘上敲出的任何东西都是字符);
int-整型(整数);
short-短整型(位数少一点的整数);
long-长整型(位数多一点的整数);
long long-长长整型(位数更多一点的整数);
float-单精度浮点数(小数点后有效数字和范围都小一点的小数);
double-双精度浮点数(小数点后有效数字和范围都大一点的小数).
2.2 每种数据类型的大小是多少?
我们可以写一个程序来计算它们的大小:
#include<stdio.h> int main() { printf("%d\n", sizeof(char)); /* \n-换行*/ printf("%d\n", sizeof(int)); /* %d-以十进制的形式打印一个整数;*/ printf("%d\n", sizeof(short)); /* sizeof是一个操作符,用来计算计算机类型或变量所占内存*/ printf("%d\n", sizeof(long)); /* 空间的大小;*/ printf("%d\n", sizeof(long long)); printf("%d\n", sizeof(float)); printf("%d\n", sizeof(double)); return 0; }
运行结果如下:
程序输出的1 4 2 4 8 4 8的单位为Byte(字节);
这里简单说一下计算机中的单位:
计算机中的单位从小到大有bit(比特位)、Byte(字节)、KB、MB、GB、TB、PB...
它们有这样的换算关系: 1Byte=8bit;
1KB=1024Byte;
1MB=1024KB;
1GB=1024MB;
1TB=1024GB;
......
2.3 为什么会出现这么多的类型呢?这些数据类型怎么用呢?
类型是用来创建变量的,变量是向内存申请一块空间来存放我们的数据.
例如:
①int weight=120;意思是创建一个weight变量向内存申请了4个字节的空间来存放我们的数据‘120’;(因为120是整数,故用int类型)
②char ch=‘w’;意思是创建一个ch变量向内存申请了1个字节的空间来存放我们的数据‘w’;
(因为w是字符,故用char类型)
3.初识变量与常量
生活中的有些值是不变的(比如:身份证号码、性别、血型、圆周率等),有些值是可变的(比如:身高、体重、年龄等)。
在C语言中,不变的值用常量的概念来表示,可变的值用变量来表示。
3.1 变量
3.1.1定义变量的方法
格式:类型+变量名;或 类型+变量名=初始值;
例如:int age=21; float weight=57.5f; char=‘x’;
(注:定义小数变量时,由于float占4个字节,double占8个字节,所以float存放的数据范围比double小得多,因此在定义的时候如果直接定义57.5,编译器会默认57.5为double类型;但在程序运行时float却比double效率更高一点,所以我们在定义数据时,如果数据很小,往往就需要用float类型来定义,因此便有:57.5为double类型;57.5f为float类型)
3.1.2 变量的命名
① 只能由字母(区分大小写)、数字、下划线(_)组成;
② 不能以数字开头;
③ 长度不能超过63个字符;
④ 变量名不能使用关键字.
3.1.3 变量的分类
变量分为局部变量和全局变量.
看如下程序:
#include <stdio.h> int global = 2023; /* 全局变量*/ int main() { int local = 2022; /* 局部变量*/ int global = 2021; /*局部变量*/ printf("global=%d\nlocal=%d", global, local); return 0; }
运行结果如图:
总结:
①大括号({})外部的叫全局变量,内部的叫局部变量。
②原则上定义的变量名不能相同,但在变量名相同的情况下,局部优先,才有了上面输出global=2021的情况。
变量的使用
#include <stdio.h> int main() { int a = 0; int b = 0; int sum = 0; printf("请输入两个整数,中间用空格隔开:\n"); scanf("%d %d", &a, &b); /* &a-取出a的地址*/ sum = a + b; /*&-取地址操作符*/ printf("%d ", sum); return 0; }
这里不得不说一下输入(scanf)、输出(printf)语句了:
printf与scanf都是库函数,是C语言的编译器提供的现成的函数,可以直接使用;scanf的作用是输入数据,使数据从键盘读取到内存中;printf的作用是输出数据,使数据从内存打印(输出)到屏幕上;使用的时候都要包含头文件stdio.h.
我们已经知道,不同的数据对应有不同的数据类型,自然而然,在数据输出时,不同的数据类型也应该按照不同的数据格式进行输出。
所以printf打印(输出)数据到屏幕上时,用%c打印字符型(char);%d打印整形(int);%s打印字符串(以后再介绍);%f打印单精度浮点型(float);%lf打印双精度浮点型(double);%p地址的打印(后面的指针会用到).
代码展示:
#include <stdio.h> int main() { char ch = 'x'; int age = 21; char xhs[10] = "大帅哥"; float weight = 58.5f; double height = 182.9; printf("%c\n", ch); printf("%d\n", age); printf("%s\n", xhs); printf("%f\n", weight); printf("%lf\n", height); return 0; }
(当然输出数据这块还有很多知识,入门篇,这里只讲一些基本的,其他的以后再介绍)
3.1.4 变量的作用域和生命周期
作用域
作用域(scope)是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
如:1.局部变量的作用域是变量所在的局部范围;
2.全局变量的作用域是整个工程。
生命周期
变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段。
1.局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。
2.全局变量的生命周期是:整个程序的生命周期。
3.2常量
C语言中的常量和变量的定义形式有所差异。
C语言中的常量分为以下几种:①字面常量;②const修饰的常变量;③#define定义的标识符常量;④枚举常量
3.2.1 字面常量
字面常量包括整型常量、字符型常量、字符串常量。
例如:int myage=21;其中myage是一个int类型变量,而21就是一个字面常量。
3.2.2 const修饰的常变量
变量就是变量,常量就是常量,常变量是什么玩意呢?
用const修饰,变量就有了常属性,此时的变量就称为常变量,上一段代码:
#include <stdio.h> int main() { const int myage = 21; printf("%d", myage); return 0; }
运行结果:
此时再上一段代码:
#include <stdio.h> int main() { const int myage = 21; printf("%d", myage); myage = 10; printf("%d", myage); return 0; }
这时就会报错:
这就是因为用const修饰后,变量有了常属性,myage的值就不能再被修改了。
3.2.3#define定义的标识符常量
上一段代码:
#include <stdio.h> #define MYAGE 21 int main() { int myage = MYAGE; printf("%d\n", MYAGE); printf("%d\n", myage); return 0; }
运行结果:
所以此时MYAGE就是一个常量,值为21。为什么叫标识符常量,可以理解为MYAGE就是21的小名,MYAGE就是21,21就是MYAGE。
3.2.4 枚举常量
在实际问题中,有些变量的取值被限定在一个确定的范围内。比如一周无非就是七天,一年无非就是12个月,性别无非分为两种等等。在程序中,可能需要对某些整数定义一个别名,如把1~7定义为周一到周日,把1~12定义为一年的12个月;这时我们可以想到用刚才提到过的#define来定义标识符常量: #define MON 1;
#define TUE 2;
#define WED 3;
#define THU 4;
#define FRI 5;
#define SAT 6;
#define SUN 7;
可是这样定义未免太麻烦了些,恰巧这些别名又出现在一个确定的范围内,这时候我们就可以用枚举(enum)来定义,其形式为:enum+枚举名{枚举值表},看代码:
#include <stdio.h> enum Weekday { MON=1, TUE, WED, THU, FRI, SAT, SUN }; int main() { printf("%d\n", MON); printf("%d\n", TUE); printf("%d\n", WED); printf("%d\n", THU); printf("%d\n", FRI); printf("%d\n", SAT); printf("%d\n", SUN); return 0; }
运行结果:
(注意:枚举常量的默认值是从0开始,依次向下递增1的,也就是说如果在以上代码中我们没有使MON=1,那么程序运行的结果就为0~6了。 当然关于枚举的知识还有很多,以后再讨论。)
4.选择语句和循环语句的简单介绍
4.1初识选择语句(分支语句)
选择语句包括if语句和switch语句,这里我们只简单提一下if语句;
那么if语句的语法结构是怎样的呢?
语法结构
单分支:
①if(表达式)
语句;
意思是:若表达式的值为真(非0),则往下执行语句。
②if(表达式)
语句1;
else
语句2;
意思是:若表达式的值为真(非0),则执行语句1;若为假(0),则执行语句2。
多分支:
③if(表达式1)
语句1;
else if(表达式2)
语句2;
else
语句3;
意思是:若表达式1的值为真(非0),则执行语句1;为假(0),则判断表达式2的值,若为真则执行语句2,若为假则执行语句3。
④更多的分支以此类推。
利用选择语句我们想实现这样一个多分支程序:
如果年龄在12岁以下我们输出‘少年’;13~18岁输出‘青少年’;19~35岁输出‘青年’;36~70岁输出‘老青年’;71~100岁输出‘老年’;101岁以上输出‘老寿星’;有代码:
#include <stdio.h> int main() { int age = 12; printf("请输入年龄:\n"); scanf("%d", &age); if (age <= 12) { printf("少年\n"); } else if (age >= 13 && age <= 18) { printf("青少年\n"); } else if (age >= 19 && age <= 35) { printf("青年\n"); } else if (age >= 36 && age <= 70) { printf("老青年\n"); } else if (age >= 71 && age <= 100) { printf("老年\n"); } else { printf("老寿星\n"); } return 0; }
运行结果:
4.2初识循环语句
我们已经了解到if语句:当条件满足的情况下,if后面的语句执行,否则不执行。但是这个语句只能执行一次,可是在生活中,同一件事情往往需要我们重复去做,这在C语言中怎么实现呢?
C语言为我们提供了三种循环语句:while语句、for语句和do...while语句,这里简单介绍一下while语句。
while语句的一般形式为:while(表达式){循环体}
意思是先计算表达式的值,当表达式为真时,进入循环体;执行完循环体,再跳回表达式,接着计算表达式的值,若表达式结果为真,继续执行循环体;以此往复,当表达式结果为假时,跳出循环,程序结束。
例如:用while循环语句设计程序打印1~9的数字:
#include <stdio.h> int main() { int i = 1; while (i <= 9) { printf("%d ", i); i = i + 1; /*进入一次循环体,i就+1,当i等于9时跳出循环,程序结束*/ } return 0; }
再比如,在此基础上打印出九九乘法表:
#include <stdio.h> int main() { int i = 1; while (i <= 9) { int j = 1; while (j <= i) { printf("%d*%d=%d ",j,i,i*j); j = j + 1; } printf("\n"); i = i + 1; } return 0; }
5.初识数组
要存储1~10的数字,怎么存储?要是这样 int a=1;int b=2;int c=3;......int j=10;这样来定义未免太繁琐了,这时C语言就给了数组的定义:一组相同类型元素的集合。
5.1数组的定义
以存放1~10的数字为例:
①int arr[10]={1,2,3,4,5,6,7,8,9,10}; //定义一个数组,最多存放10个元素;
②int arr[]={1,2,3,4,5,6,7,8,9,10}; //如果数组初始化的话,可以不直接指定大小,会根据初始化内容来自动确定大小;
③如果int arr[10]={1,2,3}; //其他7个元素默认取0;
5.2数组的下标
C语言规定:数组的每个元素都有一个下标,下标是从0开始的。数组可以通过下标来访问。
例如:int arr[10]={1,2,3,4,5,6,7,8,9,10}
数组有10个元素,它们的下标范围是0~9;如图:
5.3数组的使用
#include <stdio.h> int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; /*打印数字6:*/ printf("%d\n", arr[5]); /*打印1~10:*/ int i = 1; while (i <= 9) { printf("%d ", i); i = i + 1; } return 0; }
6.初识*指针
6.1什么是指针?
指针是C语言编程中十分重要又比较难理解的概念,想要了解指针,我们首先要了解计算机中的内存。
内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的。所以为了有效的使用内存,就把内存划分成一个个小小的内存单元,前面在计算每种数据类型的大小时,已经提到过计算机中单位的换算关系,一个char类型占1个字节(Byte),一个short类型占2个字节,一个int类型占4个字节...很显然,把一个内存单元的大小设为1个字节是最合适的。为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址,在C语言中,这个编号或者地址也被叫做指针。(当然,我们也可以联系实际生活,我们宿舍的门牌号就是我们的地址,也就可以理解为门牌号就是我们的指针。)
6.2指针或者地址(编号)如何产生?
在计算机中,有地址线的概念,就是物理的电线,32位操作系统就有32根地址线,每一根地址线通电后会产生物理上的高电平和低电平的电信号,它们转化为数字信号1或0;
1根地址线能产生2种的0/1排列,即可以产生2个地址,32根地址线就能产生2的32次方种的0/1排列(2^32=4294967296),即可以产生2^32=4294967296个地址,我们可以令一个地址去管理一个字节的空间,32位操作系统就可以产生最多有2^32个字节的内存空间,(2^32Byte=4GB)这就是32位操作系统最多支持4GB内存的原因。
6.3变量如何获得自己的指针?(内存如何给变量分配地址?)
说到这里,不得不先再介绍一下我们已经熟悉的二进制、八进制、十进制和十六进制了。
前面我们已经提到,计算机中的32根地址线能够产生2^32=4294967296种的0/1排列,为了能够更形象的表达,我们可以插入一张图:
由图可以看出,地址线产生的地址,实质上是产生了2^32种的二进制排列,可是以二进制来表示我们的指针(地址)未免太长了,就如同我们宿舍的门牌号,如果门牌号太长,显然是不行的。(二进制的1111换算成十进制:1*2^3+1*2^2+1*2^1+1*2^0=15;八进制的17换算成十进制:1*8^1+7*8^0=15;十六进制的F换算成十进制就是15;二进制的1111换算成十六进制就是F;) 由进制之间的换算关系可知,每四个二进制位可以由一个十六进制位来表示,所以以上的32位二进制位换算成十六进制位就变成了8位,即用十六进制来表示指针(地址)可以大大缩短地址的长度,实际上指针正是用十六进制来表示的。
接下来我们就写一段代码来打印地址
#include <stdio.h> int main() { int age = 21; printf("%p\n", &age); /*%p-以十六进制打印地址;*/ return 0; }
运行结果:
可见我们每次打印出age的地址都是不一样的,这是因为程序每进行一次,内存就会随机给age分配一次地址,每次程序运行结束后age就会被销毁,腾出空间(见3.1.4)。
需要注意的是,虽然age占4个字节,但是%p每次打印出age的地址,都是age第一个字节的地址,这个地址紧挨的后面三个地址正是age的另三个地址。(就如同在学校,我已经知道了信管212班有四个男生宿舍,而且这四个宿舍都紧挨着,这时候我向你询问信管212班男生宿舍的地址,你只告诉我了一个227,我就知道了信管212班男生宿舍的另三个地址为228,229,230。)
6.4初识指针的用途
6.4.1指针变量的定义
说了那么多,那么指针(地址)又如何存储呢?这就需要我们来定义指针变量:
int age=21;
int *p=&age;
(注:p是用来存放age的地址的,是一个变量,叫指针变量,意思是存放指针(地址)的变量;*是说明p是一个指针变量,*又有指向的意思,int *p表明p指向的是int类型的变量;同理如果double age=21.0,就需要用double *p来表明p指向的double类型的变量;
指针=地址=编号;指针变量=变量=存放地址的变量;经常说的指针,基本上都说的是指针变量。)
变量age向内存申请了空间来存放‘21’,假设age的地址为00F8FE6C,那么我们定义的p就来存放age的地址,即变量p向内存申请空间来存放‘00F8FE6C’。
6.4.2初识指针用途
上一段代码:
#include <stdio.h> int main() { int age = 21; int* p = &age; *p = 42; printf("%d\n", age); return 0; }
运行结果:
可见程序运行结果为42,并不是21,我们在拿到age的地址之后可以通过拿到的地址来改变age的值,也就是说指针为我们提供了另一种改变age的值的方法。
(当然,指针的用途和知识远远不止这些,笔者也还在处在学习的阶段,在以后的学习中一定会继续理解,继续总结)
6.5指针变量的大小
指针变量是用来存放地址的,其大小取决于地址的大小。
前面已经提到:在32位操作系统下,地址有32个二进制位,32个二进制位就是32个比特(bit)位即4个字节;同理,在64位操作系统下,地址有64个二进制位,64个二进制位就是64个比特(bit)位,即8个字节。
可以设计程序验证:
结论:指针大小在32位平台下是4个字节,在64位平台下是8个字节。
最后: