本文两万一千多字,篇幅较长,干货较多,请耐心食用~~
经过了第一篇的学习,相信你已经对C语言的学习充满期待了。那在C语言之中我们应该学习哪些内容呢?这节课,我们会重点地将C语言学习总体框架搭建起来,让大家对C语言有个整体的了解。这一篇博客的主要目标是:让你可以轻松看懂一些简单的代码,并可以实现一些简单功能。
正文开始,学习过程中跟着讲解一起打代码会事半功倍哦
我们知道,C语言的研究是为了更好地解决生活中遇到的问题,而解决这些问题,我们需要有这些问题的信息。比如我有15的苹果,分给三个小朋友,那么这里的15 和 3就是我们的信息,也就是数据。但是生活中的数据不可能只有整数这一种类型。在超市买东西,你还需要用到小数;查找一个英语单词时,还需要用到字符…因此,C语言给出了以下几种数据类型,来满足我们的需求:
关键字 | 对应数据类型 |
---|---|
char | 字符型 |
short | 短整型 |
int | 整型 |
long | 长整型 |
long long | 更长的整型 |
float | 单精度浮点数 |
double | 双精度浮点数 |
看到这里,一定有小伙伴有疑问了:关键字是什么意思?为什么整型还有长短之分?有什么区别?啥叫浮点数?这里来一一解答。
解答问题1:啥是关键字
首先关键字也叫保留字,是C语言自己所保留的,用来表达特定含义的字母组合(其实就是一些单词缩写),你在写代码的时候是不能将其作为变量名的(变量待会儿介绍,先别急),C语言中常用关键字有这些:
关键字分类 | 具体关键字举例(下列每一个词都是一个关键字) |
---|---|
C语言自带类型关键字 | char int short long float double signed unsigned void const |
自定义类型关键字 | struct enum union |
语句中常使用关键字 | if else switch case default while do for break continue return typedef |
储存类型关键字 | auto register extern static |
sizeof | sizeof(它同时也是一个操作符) |
解答问题2:为什么整形还要更细分类short ,long,long long?
我们知道,我们生活中的不同事物的取值是有不同范围的,比如说一个常见单词里有多少个字母,是不是1 ~ 50就可以概括所有的单词长度了,而如果是一个国家的人口有多少,上面的范围显然不够了,这时候范围大概就变成了0 ~ 1500000000。我们又知道,数据在计算机中存储是要占用内存的,那么我们把不同类型细化有什么具体意义呢?
看代码,记得在你的Visual Studio里给它打出来哦~:
#include
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));
return 0;
}
然后你又蒙圈了,这一串数据是啥玩意儿啊。哈哈,其实sizeof这个操作符(什么是操作符待会儿会有介绍的)是用来求指定对象所占内存大小的,然后这个指定对象就是在它后面的()里东西。然后用printf函数给它们大小都打印出来。放心不下你们的小颖在这里顺带解释下printf为啥跟第一节课里学得不大一样(printf函数用法以后会总结)。
那你给我这一串数字是啥呢?char类型的大小是一个?一斤?一桶?啊当然不是,sizeof计算出的大小的单位是“字节”,见下图
然后常见的换算:
1KB = 1024 byte
1MB = 1024 KB
1GB = 1024 MB
在计算机中,由于计算机只能识别 0 和 1 ,一个最小存储单位里只能有两种可能值 0 或者 1 ,这就是我们熟知的二进制储存,那么我们知道,如果给你一个bit大小的空间,你只能表示出十进制里的0 或 1,而给你两个bit大小的空间,你就可以表示0,1,2,3这四个十进制数,他们的二进制表示如下:
十进制 | 二进制 |
---|---|
0 | 00 |
1 | 01 |
2 | 10 |
3 | 11 |
二进制和十进制的转化我就不详细解释了,想了解的同学可以点进去下面这个链接(实在是没有找到站内专门讲这个的,所以还是用了站外链接)
https://zhuanlan.zhihu.com/p/75291280
然后你就明白了,其实不同类型能够取得的最大值是不相同的,为了能够最充分的利用内存,所要出现很多的类型。至于为什么long 和long long所占内存大小相等,其实这个倒不一定,只能说它最少是4个字节,至于什么时候不是,这个目前我不介绍了,可以自己去查。
解答问题3.啥是浮点数
浮点数其实就是小数,因为在科学计数法里,小数点是可以移动的,因此叫小数浮点数,比如下图:
而这里的单精度浮点数和双精度浮点数也是由于其精度不同而产生的,这和它们在计算机里的储存方式有关,比较复杂。改天小颖会专门写一篇文章来解释。
好了,看到这个我们也解决掉了第一篇我们留下的问题,主函数main之前的int 是什么意思,其实就是返回一个int类型的值,和主函数里的最后一句话return 0;正好对应,因为0就是整形类型。
所谓变量,就是会随着一定条件改变的量,比如人的年龄,你步行的速度,天气等等,而常量就是不变的量,比如圆周率,自然对数等等。接下来我们来介绍C语言里的变量和常量。
你现在有十个苹果,你要告诉计算机并把这个数据存储起来,怎么做到呢?看代码
int apple = 10;
这段代码的意思是:创建一个叫apple的整形类型变量,然后把它初始化为10。
同样我们还可以创建更多的变量:
char ch = 'a'; //注意字符类型赋值时,字符由''单引号括起来,并且只能有一个字符。且一个汉字算两个字符
int num = 4;
float weight = 120.2f; //注意float类型的数字尾最好加上f字母,不然小数计算机都默认是double类型
int a;//没有初始化,只是定义了一个变量a
我们总结出定义变量的方式:类型名 变量名 = 初始值(可以不初始化,但是不建议),另外记得一个语句结束要打分号!
对变量名的要求是:
1.只能以字母(大小写均可)、数字、下划线组成
2.不可以以数字开头
3.不能使用关键字作为变量名
4.严格区分大小写(a和A是不相同的两个变量名)
5.一般大小不超过63个字符
6.最好起的有意义,这样使用才方便
既然是变量,我们来更改一下,请读者跟着打出这段代码:
#include
int main()
{
int age = 10;
printf("%d\n",age);
age = 20;//age已经定义过了,不用再加int了而是可以直接使用
//注意,如果这里写的是int age = 20;会报错,因为重复定义了!!
printf("%d\n",age);
return 0
}
(注意:一个变量只能定义一次,重复定义会报错!!)
Ctrl + F5 走!是不是年龄第一行是10,第二行是20呢~果然变了吧。
变量分为局部变量和全局变量,他们的作用域和生命周期不同。
变量类型 | 作用域 | 生命周期 |
---|---|---|
局部变量 | 局部变量所在的局部范围 | 定义它时开始,出作用域时结束 |
全局变量 | 整个工程 | 整个工程(可理解为进入main函数开始,出main函数结束) |
局部范围说人话就是:{ }内的范围就是局部范围
敲一段代码解释:
#include
int main()
{
int a = 1;
printf("%d\n", a);
{
int b = 2;
printf("%d", b);
}
printf("%d", b);
return 0;
}
你的编译器是不是报错了?哈哈解释一波:
这时候在大括号里int b = 2;的下一行加一段代码:
printf("%d\n",a);
再运行一遍,发现是可以打印出a的值的。因为a的生命在这里仍然是存在的,这个局部变量还没有结束生命。它的生命是把它括起来的{ }的范围。
那全局变量呢?看代码:
#include
int global = 3;
int main()
{
int a = 1;
printf("a = %d\n", a);
printf("global = %d\n", global);
return 0;
}
简单地理解就是,在局部(即大括号内部)定义的变量为局部变量,其有效范围是这个变量定义开始到这个大括号结束的位置,而不被大括号括起来的变量定义都是全局变量,其有效范围是整个工程。
但是!!!你猜猜下面的代码会打印出什么
#include
int a = 1;
int main()
{
int a = 2;
printf("a = %d\n", a);
return 0;
}
你回问了:啊?不是说重复定义同一个变量会报错吗。你把这行代码敲上去,看看会不会报错。肯定不会。
为什么啊?因为你可以理解成全局变量和部分变量不算是同一个变量。而且当全局变量和部分变量有冲突时,以局部变量优先。局部变量会屏蔽掉全局变量,所以打印出的是2
要是不信全局变量a没有被改变,我来向你证明一下,看代码:
#include
int a = 1;
void test() //自己创建一个函数,返回值为void类型即无返回值类型
{
printf("a = %d\n", a);//这个函数的内容就是打印a的值
}
int main()
{
int a = 2;
printf("a = %d\n", a);
test();
return 0;
}
由于程序从主函数开始执行,且在主函数里代码是一行一行往下执行的,故这里的tset函数的执行是在局部变量定义之后,但是打印的a的值是1,说明小颖确实没有骗你。
说完了变量,接下来我们介绍常量。在C语言中的常量有以下几种:
1.字面常量
2.const修饰的常变量
3.#define定义的标识符常量
4.枚举常量
字面常量就是字面上的值,比如100,100就是100,不能这样说我把200赋值给100而改变100的值,还有’a’,这是字符a,不能说我把字符b赋值给字符a,然后字符a以后都变成字符b了吧。
像这样:
const int num = 20;
在变量定义的时候,在前面加上const修饰,就赋予了它常属性,如果你再试图去更改,编译器就会报错:
但是为什么const修饰的不能改变的量不直接叫常量,而是常变量呢。因为本质上其实num这这里还是一个变量,只会被赋予了常属性(不能被更改的属性),代码为证:
#include
int main()
{
const int n= 10;
int arr[n] = { 0 };
return 0;
}
这里先超前使用一下数组~你只要知道,数组是相同类型元素的几何定义的时候[ ]方括号内部的数是其数组元素个数,并且必须是常量!(当然C99标准引入了变长数组的概念,可以在把元素个数设置成变量,但是数组不可以初始化,这个在Linux系统或者gcc编译器下才能实现,我们暂且不做演示了)
上诉代码运行起来是这样:
证明了其实const修饰的常变量本质还是变量,只是不能被更改。
像这样:
#include
#define MAX 100//MAX就是常量100了
int main()
{
printf("%d\n",MAX);
return 0;
}
MAX其实就是一个#define定义的常量。
这个部分在进阶——预处理那一篇还会详解,别着急。先知道是怎么回事,能看懂就OK。
生活中有些有限的东西可以一一列举,就是枚举,在C语言中enum关键字就是专门为枚举这个类型建立的,比如我们列举出国家,列举出你们班同学的姓名,等等。这里以三原色举例:
#include
//声明的一个表示三原色的枚举类型
enum COLOR
{
Red,
Green,
Blue
};
int main()
{
printf("%d\n",Red); //输出的数是0
printf("%d\n",Green); //输出的数是1
printf("%d\n",Blue); //输出的数是2
return 0;
}
这里的Red,Green,Blue都是常量了,不能更改。你也可以在主函数里改一下,看看编译器报的错。enum枚举的量(如果不初始化),从上到下,每个成员的值依次是0,1,2…(默认从0开始。依次加一)。但是也可以给这些常量初始化。
enum COLOR
{
Red = 4,//Red的值为4
Green, //Green的值就是5
Blue //Blue的值就是6
};
枚举后面也会讲解
前面我们介绍了字符类型,字符具体有哪些?所谓字符,就是你键盘上可以打出来的一系列字母,符号,数字(数字也可以是字符)
但是生活中我们遇到的往往不是这一个个单独的字符,而是很多字符组成的串,即字符串。
char ch = 'a';//这是一个字符
char ch2 = '$';//这也是一个字符
char ch3 = '6';//这也是一个字符
char ch[] = "abcdef";//这是一个字符串
前面我们说了,用char类型定义一个存放字符的变量时,要用单引号,且单引号里只能有一个字符,但是字符串并非如此,由于字符串里不止一个字符(而且C语言里没有专门的字符串类型)所以要用数组来创建。
char str1[] = "abc";//第一种方式
char str2[] = {'a','b','c','\0'};//第二种方式,注意'\0'这个字符
由上面的两种方式可知道,字符串有这两种创建方式
但是注意为什么用双引号创建字符串的时候可以不加’\0’?因为计算机自动给你加好了
我们打出以下的代码,然后跟着小颖一块操作
#include
int main()
{
char str1[] = "abc";
char str2[] = {'a','b','c','\0'};
printf("%s\n", str1);
printf("%s\n", str2);
return 0;
}
然后箭头到第13行的时候,看一看两个数组里都有什么:
好了,到这里你肯定理解字符串是如何创建的了。接下来介绍字符串长度
首先介绍strlen函数,它是专门计算字符串长度的函数
来求一下我们刚刚写的代码里的两个字符串长度
#include
#include //strlen函数是在这个头文件里的
int main()
{
char str1[] = "abc";
char str2[] = {'a','b','c','\0'};
printf("%d\n", strlen(str1));//输出是3
printf("%d\n", strlen(str2));//输出是3
//顺便带你体验一下'\0'的作用:
char str3[] = {'a','b','c'};//自己Crtl+F10调试一下,看看大小是不是3
printf("%d\n",strlen(str3));//输出随机数
return 0;
}
‘\0’是字符串结束标志,不算在字符串长度中。strlen函数不遇到’\0’就会一直往下数,直到strlen遇到’\0’才停止计算,但是你不知道str3什么时候是’\0’,因为你只给了三个元素,后面的元素是什么你根本不知道,是随机的,所以计算出的长度也是随机的。所以我们一般是使用str1这样的创建方式,计算机会动给你加’\0’,就不用自己动手了,简便还不易错。
那我们看到的’\0’,'\n’到底都是什么?,接下来介绍转义字符。
上表!
转义字符 | 含义 |
---|---|
\a | 响铃,计算机会发出声音 |
\b | 退格(光标往前移一格,类似backspace键) |
\f | 换页 |
\n | 换行(光标移到下一行,相当于Enter键) |
\r | 回车(光标移到本行开头) |
\t | 水平制表符(相当于Tab键) |
\v | 垂直制表符 |
\\ | 表示一个字符反斜杠 \ |
\’ | 表示一个字符单引号 ’ |
\" | 表示一个字符双引号 " |
\? | 表示一个字符问号 ?防止多个问号被解析为三字母词 |
\0 | 空字符 |
\ooo | 表示1~3位八进制数 |
\xhh | 表示1~2位十六进制数 |
前七个没啥好解释的,后面学滚动字幕的时候会用到,这里不详解了
\\这个表示一个反斜杠,因为计算机会把\和后面的一个字母解析成转义字符的
比如你想打印\test\120这样一串字符:
printf("\test\120");//这样打试试
printf("\\test\\120");//再这样打试试
明白了没~~
对于引号,一般引号会自动两两配对,所以你要打印单双引号:
printf("%c",''');//这样打试试,报错
printf("%c",'\'');//再这样试试
printf(""");//这样打试试,报错
printf("\"");//这样打试试
会了吧~~
三字母词比如说 ??)会被解析成 ]
printf("(do you like xiaoying??)");
printf("(do you like xiaoying\?\?)");
貌似打出来的是一样的,但是在有些老一点的编译器上第一种打出来是这样的:
(are you ok]
\ooo中o是指输入一个的八进制数字,然后这个组合整体是表示十进制数
比如:
\120 就是 1 * 8 ^ 2 + 2 * 8 ^ 1 + 0 * 8 ^ 0 = 80
所以\120就是十进制的80这个数
\xhh同理,\x12就是十进制的18
好了,现在我要你打印出下面这个东西,你要怎么写代码呢?
C:\Users\\小颖加油啊~~\test\1207"2022".c
注意哦,一个转义字符只算是一个字符,即使是\ooo只算是一个字符!!!
好了,这里插一嘴打印字符要怎么打印呢~~~?看代码:
char ch = 'a';
printf("%c\n",ch);
你一定已经发现了 :是%c,那么我们试一试新奇玩法,用打印字符的格式打印整数
printf("%c\n",65);
千万注意65不是字符数字啊,字符数字是这样写的'65',所以这里并不是打印字符'65'
打印的是A,为什么呢?
因为在计算机中是没办法直接存储字符的,只能存储二进制的0或者1,所以可以将某些数字和字符对应起来,于是有了统一标准ASCII码表:
查一下65对应字符,果然是A
对printf函数小小总结一下
打印格式 | 打印对象 |
---|---|
%d | 打印整数 |
%c | 打印字符 |
%s | 打印字符串 |
都打了这么多代码了,大家有没有发现这么一件事,在小颖为大家解释代码的含义的时候,会在代码的后面先打上两个反斜杠 // 然后才开始解释,而且后面输入的东西就会变暗(在VS上会变绿),这个就是注释。注释的部分在编译是会被计算机自动忽略掉,所以不用担心注释部分的代码合不合语法要求。
那为啥子我们自己写代码的时候也要写注释呢?其实以后在写一个比较大的工程的时候,时间会很长,有时候你今天写代码时候想到一种非常好的思路人,然后解决了这个问题,但是可能过几个星期或者几月之后,你回头看自己写的代码的时候很可能已经不知道自己当时的代码是干什么的,什么样的思路了。所以需要写上注释,方便自己未来复盘的时候查阅和思考。同样,很多人写一个工程时候,也需要让自己的合作者很容易理解自己的代码,可以大大的提高团队的合作效率。
怎么注释呢?
C语言注释风格:
/* */在这个范围内的所有代码全会被注释
C++注释风格:
// 在这个之后的该行代码全被注释
/*这是C语言的注释风格*/
//这是C++的注释风格
但是我们一般都会使用C++的注释风格,比较方便,如果你想注释很多行,既可以使用C语言注释风格,也可以像这张图一样
生活就是由无数的选择和循环所组成的,循环和选择可以解决几乎一切的问题,也是学习各类编程语言都十分重要的一个知识点
现在你就面临一个选择
哈哈哈所以你会不会摆烂呢~现在就来检验一下,跟着小颖敲这样一段代码:
#include
int main()
{
printf("要好好敲代码吗?\n");
printf("请选择:1.YES 2.NO\n");
int input = 0;
scanf("%d", &input);//现在不懂这句话没关系
//接下来进行选择语句
if (input == 1)
{
printf("成为大佬\n");
}
else
{
printf("原地踏步\n");
}
return 0;
}
你一运行,哇,直接报错,点击否之后看一眼为啥,挑战你英语的时候到了
说人话:它说这个函数不安全,考虑使用scanf_s去替换掉scanf函数,然后也可以使用这么一段话:#define _CRT_SECURE_NO_WARNINGS
来解决这个问题。
建议使用后面的方式,因为scanf_s看似打起来方便,但是这只是Visual Studio自己的东西,别的编译器可不一定认识这个东西,那玩意有一天别人用你的代码在别的编译器里处理问题,结果一运行,密密麻麻全是“未定义”这三个字就不太合适了。
但是后面这段话是万能的,所有的编译器都认识,都是老熟人所以代码迁移的时候就完全没障碍了,不会产生水土服的问题。
让我改一下成这样:
#define _CRT_SECURE_NO_WARNINGS
#include
int main()
{
printf("要好好敲代码吗?\n");
printf("请选择:1.YES 2.NO\n");
int input = 0;
scanf("%d", &input);
if (input == 1)
{
printf("成为大佬\n");
}
else
{
printf("原地踏步\n");
}
return 0;
}
运行走起,输入你的答案,然后看一看结果是啥。
那么这里你就看到了选择语句的一种基本格式:
if(判断部分)
{
if判断成立的执行的语句
}
else
{
if 判断不成立则执行这里的语句
}
好了,按照刚刚小颖敲的代码,也敲出你自己的一个选择语句把
曾经有一句话让我很喜欢:你是把一年过成了365天,还是把1天,重复了365次。
如果停滞不前,那么你就会把一生,过成了一个无味的循环,所以,来跟着小颖学编程,不断的更新自己哦~别忘了点击关注
好了,看图:
然后写代码先不考虑你突然成为天才的情况:
#include
int main()
{
printf("跟着小颖学编程\n");
printf("敲代码~~\n");
int knowledge = 0;//知识掌握度 0,假设掌握度为6时出师
while (knowledge < 6)
{
printf("认真学习中\n");
knowledge = knowledge + 1;//认真学习后在原基础上知识掌握加1
}
printf("出师啦!\n");//当循环结束,达到出师条件,就出师啦
return 0;
}
所以概括出循环的基本格式
while(判断条件)
{
循环执行的语句
}
由于这里只是简单介绍,其他的选择(switch语句)语句和循环(比如for,do while循环)语句就不做介绍,可以参照第一篇博客前言里的学习计划大纲,看看具体第几篇博客会介绍到分支和循环以及后面每篇博客将学习的重点。
链接:http://t.csdn.cn/lTvI7
我们在初中的时候就学过了函数,就是给你自变量,然后会产生一个值,不同的自变量会生产出不同的值。
比如这样:f(x) = 3x + 4
就是让你给出来一个x,然后它计算出一个f(x)并返回给你这个值
C语言中的函数也可是实现这样的功能,比如求和函数
#include
int Add(int x, int y)//自己定义一个求和的函数,函数名一般要取得有意义
{
int z = 0;//创建新变量来存储x 和 y的和
z = x + y;//进行运算
return z; //把运行的结果返回去
}
int main()
{
int a = 1;
int b = 2;
int c = 0;
c = Add(a, b);//用c来接受函数的返回值
printf("%d\n",c);
return 0;
}
画图讲解:
当然你可能觉得,哇这还不如我直接c = a + b ;
来的直接,
确实,这种小型的代码用函数很麻烦,但是如果项目多,工程量大,把一些功能打包到函数里效率会很高的,比如strlen函数,你使用的时候只要知道怎么传参,怎么接受返回值就可以直接求字符串长度了。但是如果每一次都让你写一遍这个功能,字符串多了你不得累死~。。。
好了,函数也就先介绍这么多,后面也会也详细的章节介绍。
如果变量很少的情况下我们可以一一写出来,一个一个定义,一个一个使用。但是如果我现在给你你们全班人(假设有45个人)的成绩(假设都是整数),让你存起来,怎么办?难道
int score1 = 97; int score2 = 98;
…这样打45次?
别急,给你个好东西——数组,多个相同类型元素的集合
我们把1~10这些数字放在一个叫num的数组里,可以这样来写:
当然也可以不完全初始化:int num[10] = {1,2,3};
这个数组有10个元素,但是我只初始化三个元素。这时候其他七个元素都会被自动初始化为0;可以通过之前说过的调试来看
当然还可以通过打印出每一个元素的方式来看,就要用到接下来我们介绍的数组下标
我现在想看一下数组中一个,第五个,第九个元素,要怎么看呢?
首先,在数组中,其实每一个元素都是有下标的:(注意!!下标从0开始!!!)
当我们需要访问特定的元素时,直接使用下标就OK了
比如我要打印出9这个元素,它的下标是8所以:
printf("%d\n",num[8]);//打印下标为8的元素
所以在定义之后,num[n]就是指下标为n的元素啦。
那我们来用循环打印每一个元素这样写:
#include
int main()
{
int num[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
while (i < 10)//打印十个元素
{
printf("%d\n", num[i]);//打印下标为i的元素
i = i + 1;//每次都把i加1,以便在下一次循环中打印下一个元素
}
return 0;
}
数组的知识还是有很多的,这里也不详细介绍了,先了解即可。
C语言的一个特色就是有很多的操作符,这也使得C语言是一种非常灵活的编程语言,操作符具体有哪些呢?我们来简单介绍一下:
模,幂运算(C语言没有幂运算的操作符,这个符号:^不是幂运算!!)而加减乘除取模对应的符号是 + - * / %
加减乘不用介绍了,因为小学就学过了,但是除和取模要强调一下
首先除,键盘上打不出手写体除号,所以用的是斜杠: /
但是这个除也不是直接除,比如10除以4,10 / 4;
结果不是2.5,而是2
这是因为在除号两边都是整形的时候,其实除号的功能是整除,小数部分直接省略了.
但是如果除号两边只要含有浮点数,那么结果就是浮点数10/4.0或者10.0/4
结果就是2.5。
然后是取模(符号是%),就是取余数,但是要特别注意的是%的操作数只能是整数!!是不能对浮点数取模的。9%2
结果就是1.这个应该很好理解了。但是对负数的取模如-10%4
和10%-4
的结果不一样的原因这里不详细介绍,回头小颖也会写专门的博客介绍。
这个和二进制有关篇幅有限,会在后面的章节详解。
也涉及二进,后面章节详解
=就是赋值,把等号右边的值给左边(注意在定义变量的时候 = 的意思是初始化)
int a = 1;//这叫初始化
a = 2;//把2赋值a,这时a的值是2了,这个叫赋值。
剩下这些操作符原理都一样,举个例子你就明白了
比如’a += 3;
就相当于 a = a + 3;
同理a *= 2;
相当于a = a *2
其他的同理。
所谓单目就是只有一个操作数。
比如a++;
只有一个操作数a;而前面的a = 1
有两个操作数a和1。
单目操作符 | 含义 |
---|---|
! | 逻辑取反 |
- | 负值 |
+ | 正值 |
& | 取地址 |
* | 解引用操作符(间接访问操作符) |
sizeof | 求出操作数的类型长度(以字节为单位) |
~ | 对一个数的二进制按位取反 |
-- | 前置、后置-- |
++ | 前置、后置++ |
(类型) | 强制类型转换 |
逻辑取反(!),就是真变假,假变真,在C语言里,规定0是假,非0是真(只要不是0,都是真)
在选择和循环语句里,具体怎么执行也是判断括号里的表达式是真是假
int a = 0;
while(a < 10)//判断a < 10的真假性,真就进循环,假就跳出去
{
a+=1;
}
第一次进循环a = 0,确实小于10,所以是真,进循环
但是如果改成这样
int a = 0;
while(!(a < 10))
{
a+=1;
}
首先a = 0,确实小于10,所以表达式(a < 10) 是真,但是!让它取反,变成了假,所以不执行循环体中的语句,直接跳出循环。
然后+ 和 - 没啥讲的,就和数学里的负号正号一个意思
取地址和解引用符号马上讲指针才能理解,过
sizeof前面说过了,就是求对象的类型所占用内存大小比如:
int a = 10;
printf("%d\n",sizeof(a));//求的是a的类型int的大小
~ 涉及二进制,不解析了这里
前置和后置++ --
举例说明++a或者a++;
就相当于a = a + 1;
这是一种很方便的写法。着两种写法有什么区别呢?
前置后后置表示了进行运算和使用的顺序,前置++/--就是先++/--再使用,后置就是想使用再++/--
举例:
强制类型转换
其实就是把类型转换为想要的类型,比如int c = (int)3.14;
就把3.14这个浮点数转化成整形放到了a里,类型转换一般用的很少,后面用到的时候知道是怎么回事了。
前四个不用介绍了把,小于,小于等于,大于,大于等于
!=的意思是不等于,是判断两个操作数是否不等价,不等价就是真。同理==也是判断是否等价,等价就是真。上代码:
int a = 1;
if(a != 0)//a是1不是0,判断为真,执行下面的打印
printf("a不等于0\n");
if(a == 0)//a是1不是0,判断为假,不执行下面语句
printf("a等于0\n");
注意!!!一个等号是赋值,两个等号才是判断是不是相等
&&就是并且的意思,&&两边的表达式全为真的时候为真,一旦有假就是假
||就是或者的意思,||两边的表达式有一个真即为真
表达式 | 整体的真假性 |
---|---|
真&&真 | 真 |
真&&假(假&&真) | 假 |
假&&假 | 假 |
真||真 | 真 |
真||假(假||真) | 真 |
假||假 | 假 |
exp是expression的缩写,是指一个表达式
这个条件操作符看起来复杂,实则很好理解,先判断表达式1是否为真,如果为真,这个整体就是表达式2,否则就是表达式3,比如输出两个数的较大值:
#include
int main()
{
int a = 3;
int b = 6;
int c = a>b?a:b;
printf("%d\n",c);
//上面两行代码可直接替换为printf("%d\n",a>b?a:b);或者 printf("%d\n",(a>b?a:b));
return 0;
}
逗号表达式就是一系列逗号隔开的表达式如a -= 3.b = 4 - a,c = b + 4;
逗号表达式是由左到右依次执行,但是逗号表达式整体的值是最后一个表达式的值、
举例:
#include
int main()
{
int a = 1;
int b = 2;
int c = 3;
printf("%d\n", (a += 4, b = a - 2, c += b));//依次执行后a = 5,b = 3,c = 6故输出为6
return 0;
}
后面文章还会说到,这里就先理解就行了
其实前面说数组的时候就用到了[ ]这个操作符来使用特定下标的元素
而()是我们在前面说函数中就用到的,表示我们在调用函数
结构成员相关的在下面结构体中会说到。
前面列举过C语言中所有的关键字,现在拿出来几个比较常用的介绍,其他的在后续的章节精讲各个知识点的时候说到。
auto就是automatic缩写,是“自动”的意思,用来修饰局部变量,表示这个变量由编译器自动分配内存和释放,一般存放在栈区。
其实我们在创建局部变量时一直在用到,只是没有写出来auto int a = 10;
我们可以直接省略auto写成int a = 10;
默认是自动变量。
前方高能:接下来两个关键字可能比较难理解,如果实在看不懂也别急,后面代码写着写着你的理解会慢慢加深的。
register就是寄存器的意思,这个关键字也和内存有关。我们的设备上一般存储信息的设备有:寄存器,高速缓存,内存,硬盘,网盘。从左到右,设备储存大小递减,造价递减。所以一般寄存器只有几十个,一个大小为4字节。CPU处理数据的时候,寄存器里的数据处理的是最快的。
用register修饰变量,就是建议计算机将该变量储存在CPU内部寄存器里,但是具体会不会这样分配还要看计算机大爷同不同意了。regiter int a = 2;
static有三大作用,我们来一一介绍
首先我们还是要介绍一下有关于内存的简单常识,一般计算机中的内存会大致这样划分(实际上要复杂得多):
我们上面说的auto修饰的局部变量就是分配在栈区的,而最开始说的全局变量分配在静态区。
但是static可以可以改变局部变量的储存位置并改变了其生命周期。
先看两段代码:
第一段代码现在是不是很清晰了呢~~
#include
void test()
{
int a = 1;
printf("%d\n", a);
a++;
}
int main()
{
int i = 0;
while (i < 10)//循环10次
{
test();//从这里进入函数
i++;
}
return 0;
}
第二段代码:
#include
void test()
{
static int a = 1;//这里多了一个static修饰
printf("%d\n", a);
a++;
}
int main()
{
int i = 0;
while (i < 10)
{
test();
i++;
}
return 0;
}
打印出的结果分别是啥呢?
分别是:
第一段代码:1 1 1 1 1 1 1 1 1 1(每个1占一行)
第二段代码:1 2 3 4 5 6 7 8 9 10(每个数字占一行)
为什么呢?来解释一下,图很复杂,耐心看
每次进入函数都要创建一个新的a并赋值1,出函数立刻销毁,这是重点
那第二段代码呢?从结果我们大概可以猜到,是a在出函数的时候没有销毁。(在出函数是没销毁,第二次进入函数的时候int a = 1;
这句话不再执行,而由于第一次在函数中a++;
了让a的值变成了2所以第二次打印的是2,后面同理。注:下一次进入函数时int a = 1;
这句话直接会被跳过,不会把a重新变为1)
所以简单概括:static修饰局部变量,可以改变局部变量的存储位置,使局部变量出了作用范围后不会被销毁,即改变了局部变量的生命周期(使其生命周期扩大到整个工程的生命周期)。(并注意,static不会改变局部变量的作用范围,在主函数里还是无法使用a这个变量)
全局变量是可以在同一个工程的其他源文件里使用的,现在右键源文件,再新建一个源文件
现在这个工程里有两个源文件了,在新建的源文件里打上这样一段代码:
int g_val = 20;
在一开始的源文件里打上这样的代码(test.c是最开始的文件test2.c是后来创建的)
发现运行失败,不是说全局变量可以在其他文件使用麻~~,但是要告诉电脑啊,这时候要用到外部声明的关键字extern,照着图片里说的打,看看结果是不是就正确了。
那如果在test2.c把代码改成这样static int g_val = 20;
呢,你再运行是不是报错了。
static在修饰全局变量时,将全局变量的外部链接属性会变成内部链接属性,让这个全局变量只能在该文件中使用,即影响了它的作用域。
修饰函数和修饰全局变量很相似,因为函数也是有外部链接属性的。有图为证:
别忘了自己打一遍代码,但是如果现在你在函数定义(左边的文件里)前面加上static修饰,就会报错:
这里就说明了:
static修饰函数会使函数的外部链接属性变为内部链接属性,使函数只能在当前源文件中使用。
signed 英语翻译就是“有符号的”,而unsigned翻译“无符号的”
一个整数没有符号,其实就是指它只能取到正值,没有负值,这样他的最大取值会变大会变大大概两倍,至于为什么后面会有博客专门写数据是怎么储存的。到时候就明白了。
就是空的意思,前面有写过一个函数返回值是void,你还记得吗?所以我们的主函数还可以这样写:
void main()
{
}
这样就没有返回值也就不用谢return了,但是不建议,这种写法太老了。当然还可以这样:
void main(void)
{
}
表示函数不需要参数,或者这样:
int main(void)
{
return 0;
}
typedef是type define的缩写是类型重定义就是可以给一些类型取个小名:
#include
typedef double dou;
void main(void)
{
dou a = 3.14;
printf("%lf\n",a);//%lf是用来打印double类型数据的
}
上面的dou就是double的小名,可以直接代替double,这个在结构体中很方便。
好了,关键字目前就介绍这么多,后面的学习会学习到更多,一点点来,不急不急。
#define定义的标识符常量前面我们已经介绍,现在主要介绍#define定义的宏
前面我们实现两个数相加时介绍了可以通过写Add函数来实现,但是其实用#define也可以实现
#include
#define ADD(x ,y) (x + y)//定义宏
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int c = ADD(a, b);
printf("%d\n", c);
return 0;
}
它的功能和函数很相似,而且不需要定义参数的类型。这个知识点具体在后的章节——预处理会详解。
这里不多解释。
终于遇到了C语言中的老大哥——指针~~,我知道你会有点害怕,但是别急,我们来慢慢解析。
不讲内存的指针是在耍流氓。所以先说内存。计算机存储数据肯定是需要空间的,那么这些空间就是内存,为了方便使用,我们把计算机里的内存要编上编号。这个过程就相当于一个送信,一个地区太大了,为了方便找到送达地,会把我们每家每户边上一个编号,比如光明村廖城路幸福小区一栋五楼503.这样看快递员就可以精准快速地把信送达。或者是你要找好朋友,如果知道他在玛伽山大学五号楼419宿舍,那么你立马就知道去哪里找他。
在计算机里,这么大的内存为了使用方便,也要给每个内存单元编上地址,像这样:
我们就把这个编号就做地址,由于地址可以精确指向你想用到的东西,故也叫指针(内存编号=地址=指针)
那么就产生了两个问题:
1.1个内存单元多大比较合适?
2.内存的编号怎么产生?
首先,如果规定一个比特bit为一个最小内存单元行吗?不行,因为我们知道,最小的类型char是一个字节,也就是八个比特,如果一个比特位就编上一个地址,一个最小的char类型数据就要分配到8个地址,这就相当于告诉你把信放在光明村廖城路幸福小区一栋五楼503进门后第二个平方米上。这样划分太细了!反而没有必要。
如果是1kb呢,1kb是1024字节,一个整形4个字节,甚至都不够一个内存单元的百分之一。就相当于让你把信送到光明村廖城路幸福小区,这么大一个小区,我怎么知道你具体在哪???
所以综合考虑,其实一个字节为一个内存单元是最合适的。
然后,内存编号如何产生?在32位的机器上,其实是有32根地址线的(地址线就是电线),每根地址线有低电平和高电平两种情况(对应着0和1),那么32个地址线不同的组合就有232这么多种电信号,也就是232种数字信号:
那么就产生了232个编号,即地址。那我们来计算一下,32位机器的内存大小是多少呢?一个编号(即地址)是1字节,232个编号就是232个字节,然后1kb = 210byte,1mb = 210kb,1gb = 210mb.……这个运算量对小学生刚刚好,但对大学生的你肯定已经晕了,所以给上结论4GB。哇原来就这点内存~
好了,到了这里,你大概明白指针是什么意思了。下面看代码:
int a = 10;
从内存的角度来看这句话,就是向内存申请int大小(即四个字节大小)的空间来存放a这个变量,这个变量存储着数据10。
现在取出a的地址&a;
&这个操作符就是取得一个变量地址的意思,现在用一个变量再把a的地址存储int * pa = &a;
由于pa这个变量储存的是一个地址,所以pa叫做指针变量。
可以用%p来打印地址,在主函数里打上这样的一段代码:
int a = 10;
int * pa = &a;
printf("%p\n",&a);
printf("%P\n",p);
打印出的两个都是地址,且是相同的。
单独解释一下int * pa = &a;
即指针变量的定义
看看这段代码可以理解了吗?
char ch = 'w';
char * pc = &ch;
那我们的指针怎么来定位到它指向的数据的呢?就要用到另一个操作符 * (解引用操作符)
#include
int main()
{
int a = 10;
int* pa = &a;
printf("%d\n", *pa);//*pa就是说通过地址找到10这个数据
return 0;
}
当然也可以借助解引用改变变量的值
*pa = 20;和 a = 20;是等价的
那么指针变量的大小是多少?
1.可以直接用sizeof操作符来求printf("%d\n",sizeof(int*));
2.直接分析,首先我们前面说地址是怎么产生的?32位机器上是不是32根地址线产生的,也就是32个二进制,一个比特位可以存储一个二进制数字,所以只要32个二进制就可以储存一个地址,也就是四个字节的大小。所以所以的指针变量都是4个字节,无论指向的对象是什么类型。
至于64位机器上,分析方式相同。在Visual Studio上是可以调节的:
当然关于指针的知识这里知识冰山一角……哈哈没关系,后面还有初阶和进阶指针的详解,别着急,先把最基础的掌握了。
生活中不可能说一个事物只要一个参数就可以描述了,比如一个学生,你要知道他的姓名,年龄,性别,学号等等大概才可以知道他的基本信息。但是C语言里可没有学生这种类型,但是C语言给了我们创建这个类型的权利,结构体
结构体需要这个关键字struct(英文翻译“结构”)怎么创建这样的变量呢?看代码
struct Student
{
char name[20];//姓名是字符,用字符数组储存
int age;//年龄是整数
char id[20];//学号可以用字符数组储存
float score;//分数是浮点数
};//别忘了打分号!!!
注:大括号里面的叫做结构体成员。
struct Student s = {"张三",20,"202200100123",94.4f};
这里要用到 . 和 ->(结构体成员访问操作符)
先介绍多个数据打印的格式:
打印结构体中每个成员:
printf("%s %d %f %s",s.name,s.age,s.score,s.id);
用指针来打印也可以
struct Student * ps = &s;
printf("%s %d %f %s",(*ps).name,(*ps).age,(*ps).score,(*ps).id);
指针还有专门的更形象的方位打印 ->
printf("%s %d %f %s",ps->name,ps->age,ps->score,ps->id);
篇幅有限,这篇文章只能浅浅地带你了解C语言中的主要知识点,相信学完了这一章,你基本上能读懂简单代码了,并且对未来学习的具体内容有个大体的了解。
也相信你一定还有很多的疑惑和好奇,没关系,后面的每一篇都会解决你的一点点迷惑,记得关注收藏,和小颖一起学习C语言!
@小颖加油啊~~