首先介绍一下我使用的工具:VS2019——集成了很多的功能:编辑、编译、链接、运行、调试等
了解:写C语言代码其实写出来的是一个.c的文本文件,本身是不能运行的,是需要经过编译、链接、运行等一系列操作,最终生成一个.exe的可执行文件。一般.c称为源文件,.exe称为可执行文件。
在VS上写代码: 1.创建项目:如下
2.新建源文件 .c文件
3.写代码
该行代码表示:在控制台上打印The First C
源文件可以用记事本,用VS,各种查看软件打开,但要想看到运行效果,就必须得编译、链接、运行,VS的快捷键是ctrl+F5,或者点击下图中按键
效果如下
#define _CRT_SECURE_NO_WARNINGS 1
//scanf函数接受键盘的输入
#include "stdio.h"
//main()主函数
int main()
{
printf("The First C");
return 0;
}
所有的代码都是从main()函数开始执行的,main()函数是程序的入口,C语言成千上万行的代码都是从main()函数的第一行开始执行的,每一个程序代码都必须有main()函数,且一个程序中只能有一个main()函数,有且仅有一个
整型的意思,表示main()函数执行完之后,返回一个整型(int)
大括号内表示函数体,main()函数也是函数,是有函数体的,在大括号内写代码
printf()是库函数,是C语言标准库中提供的一个现成的函数,可以直接使用,printf()函数的功能是在屏幕上打印信息,在” “内部写需要打印的信息
库函数的使用是需要包含头文件的,printf()函数需要的头文件是stdio.h
用#include"----.h"或#include<----.h>的方式包含一个头文件
注意:C语言中每一行代码都是英文的,包括符号
为什么要写程序呢?
其实写程序是为了用程序解决生活中的一些问题
首先要描述这个问题,例如网上商城:描述商品:名字、定价、优惠
C语言要能描述这些信息,就得有数据类型
C语言中提供的基本数据类型有 :
char //字符数据类型 short //短整型 int //整型 long //长整型 long long //更长的整型 float //单精度浮点型(小数) double //双精度浮点型(小数) 为什么叫做浮点数呢,举个例子:123.45是一个小数,使用科学记数法可以表示为12.345*10^1或1.2345*10^2,小数点是可以浮动的
计算数据类型的长度 sizeof()操作符:计算()里面的大小
sizeof()计算的单位是字节
计算机中常见的单位:
bit(比特位),byte(字节),KB,MB,GB,TB,PB......
1byte=8bit,1KB=1024byte,1MB=1024KB......
#define _CRT_SECURE_NO_WARNINGS 1
#include "stdio.h"
int main()
{
//%d以十进制的形式打印整数
//'\n'表示换行
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;
}
类型是用来创建变量的; 例如:int num=0; char ch='b'; 不变的值——常量;变化的值——变量
变量的定义方法:类型 变量名 举个例子
int age=150;
float weight=45.5f;
//如果直接写45.5,VS会直接认为是double类型
//要是float类型,就要加上f
char ch='w'
变量有局部变量跟全局变量
简单说,在{}内定义的就是局部变量,在{}外定义的就是全局变量
当局部变量和全局变量在一个地方都可以使用的时候,局部优先
#define _CRT_SECURE_NO_WARNINGS 1
#include "stdio.h"
int a = 100;//定义全局变量
int main()
{
int a = 50;//定义局部变量
printf("%d\n", a);
return 0;
}
常量,顾名思义就是不变的量,跟变量刚好相反 C语言中常量有以下几种
直接写出的值就是字面常量;例如:100、20.0、3.14、'a' 等等
这里的常,表示的是不变的意思。const是常属性,可以用来修饰变量
通过这段代码可以看出,经过const修饰的变量,就不能再被修改了 这里,我们称b就是const修饰的常变量,具有了常属性;b本质上还是一个变量,只不过它被const在语法层面上加了一层限制,具有了常属性 cons是在语法层面加上的一层限制,就是告诉我们,该变量的值不能再被修改了
运行结果如下
通过代码,我们能很明显地感受到#define定义的是一个常量
枚举的意思就是一一列举 生活中有一部分数据是可以一一列举出来的:例如三原色、性别、星期、月份等等。 C语言中就给出一个枚举类型,可以把这些数据一一列举出来,比如下面这段代码:
enum Color//自定义的一个枚举类型
{
RED,
GREEN,
BLUE//枚举常量
//即该枚举类型的取值只有{}内的三种
};
这就是一个枚举类型,而枚举常量的取值是从0开始依次往下排,通过下面这段代码的执行效果就可以感受到
#define _CRT_SECURE_NO_WARNINGS 1
#include "stdio.h"
enum Color//自定义的一个枚举类型
{
RED,
GREEN,
BLUE//枚举常量
//即该枚举类型的取值只有{}内的三种
};
int main()
{
enum Color a = RED;
enum Color b = GREEN;
enum Color c = BLUE;
printf("%d\n", a);
printf("%d\n", b);
printf("%d\n", c);
return 0;
}
所以,枚举常量其实就是枚举类型可能的取值,{ }内一一列举出来的
作用域:限定该变量可以使用的范围
#define _CRT_SECURE_NO_WARNINGS 1
#include "stdio.h"
int main() {
{
int a = 10;
printf("%d\n", a);//a在{}内部创建,可以在{}内部使用
}
return 0;
}
运行结果是
如果将代码改成下面这样,结果会怎么样呢
#define _CRT_SECURE_NO_WARNINGS 1
#include "stdio.h"
int main() {
{
int a = 10;
}
printf("%d\n", a);//a在{}内部创建,不可以在{}外部使用
return 0;
}
该段代码运行结果是
原因是printf()函数使用时,超出了a的作用域,a的作用域在定义int a 的大括号内部
这是局部变量作用域的展示
接下来是全局变量的作用域
#define _CRT_SECURE_NO_WARNINGS 1
#include "stdio.h"
int a = 100;
void test()
{
printf("test()--->%d\n", a);//定义test()函数,打印出“”内的内容
}
int main() {
printf("%d\n", a);//定义a为全局变量,可以在整个工程中都使用
test();
return 0;
}
该段代码的效果如下
可见,全局变量的作用域是整个工程
变量的生命周期是指:变量的创建到变量的销毁之间的一个时间段 1.局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束 2.全局变量的生命周期:整个程序的生命周期
#define _CRT_SECURE_NO_WARNINGS 1
#include "stdio.h"
int a = 100;
int main() {
printf("%d\n", a);
printf("%d\n", a);
return 0;
}
该段代码的效果如下
上述代码就是全局变量a的生命周期,是整个程序的生命周期
#define _CRT_SECURE_NO_WARNINGS 1
#include "stdio.h"
int main() {
int a = 10;
printf("%d\n", a);
a = 20;
printf("%d\n", a);
return 0;
}
上述代码是局部变量的生命周期,第一个a在第四行被定义,在第五行被执行并销毁,这就是他的生命周期;同理,第二个a在第六行被定义,在第七行被执行并销毁,这是第二个a的生命周期。 下面是该段代码的执行效果
C语言中有字符(char)类型,但是没有字符串类型;那么在C语言在如何表示字符串呢
"hello world\n"
像这样,用" "引起来的一串字符称为字符串;字符串的结束标志是一个'\0'的转义字符。 字符串其实就是一串字符,字符串是可以存放到字符数组里边的。
#include 'stdio.h'
int main(){
char arr1[] = "abc";//直接用" "引起来
char arr2[] = { 'a','b','c' };//字符用{}括起来,中间用逗号隔开
char arr3[] = { 'a','b','c','\0' };
return 0;
}
在每个字符串的末尾都隐藏了一个'\0',利用VS2019中的监视(调试---窗口---监视)功能可以看出
当一个字符串真正遇到'\0'的时候,才认为这个字符串结束了;如下段代码的显示效果
#define _CRT_SECURE_NO_WARNINGS 1
#include "stdio.h"
int main()
{
char arr1[] = "abc";//直接用" "引起来
char arr2[] = { 'a','b','c' };//字符用{}括起来,中间用逗号隔开
char arr3[] = { 'a','b','c','\0' };
printf("%s\n", arr1);//打印字符串用%s
printf("%s\n", arr2);
printf("%s\n", arr3);
return 0;
}
注意:在计算字符串长度的时候'\0'是结束标志,不算作字符串的内容。 strlen( )函数是一个库函数,用来求字符串长度,统计的是字符串中'\0'之前的字符个数 我们可以利用strlen( )函数计算字符串的长度;如下段代码效果
'\0'之前的字符个数就是该字符串的长度,而arr2[ ]没有'\0',所以他的长度是随机的 注意:使用strlen( )函数的时候,需要头文件 #include "string.h"
这里我们解释一下'\\',他的效果是在屏幕上输出一个 "\";如下图代码效果
这里我们很明显能感受到,需要打印出"\",就得使用'\\'转义字符
\xhh:
\x后面跟两位十六进制数,该两位十六进制数的值即为对应字符的十六进制ASCII码值。
\ddd:
斜杠后面跟三位八进制数,该三位八进制数的值即为对应的八进制ASCII码值。
一个转义字符就算作一个字符,因此一个转义字符的长度为1
为了方便看懂代码,给代码的一段说明性文字;在编写C语言源代码时,应该多使用注释,这样有助于对代码的理解。在C语言中有两种注释方式:
/*
开始、以*/
结束的块注释(block comment);//
开始、以换行符结束的单行注释(line comment)。在计算机中,所有的数据在存储和运算时都要使用二进制数表示(因为计算机用高电平和低电平分别表示1和0),例如,像a、b、c、d这样的52个字母(包括大写)以及0、1等数字还有一些常用的符号(例如*、#、@等)在计算机中存储时也要使用二进制数来表示,而具体用哪些二进制数字表示哪个符号,当然每个人都可以约定自己的一套(这就叫编码),而大家如果要想互相通信而不造成混乱,那么大家就必须使用相同的编码规则,于是美国有关的标准化组织就出台了ASCII编码
ASCII 码使用指定的7 位或8 位二进制数组合来表示128 或256 种可能的字符。标准ASCII 码也叫基础ASCII码,使用7 位二进制数(剩下的1位二进制为0)来表示所有的大写和小写字母,数字0 到9、标点符号,以及在美式英语中使用的特殊控制字符
有人将 ASCII 编码分成两部分:
ASCII表中可以记下部分特殊的值(字母从A到Z,从a到z,ASCII值依次递增)
由于ASCII表中的大小写字母对应的ASCII值相差32,所以我们在编写大小写转换程序的时候,就非常便捷
C语言是一种结构化的程序设计语言,它有三种结构:顺序结构,选择结构,循环结构
生活中的任何一件事都可以抽象成这三种结构中的一种或者组合,所以用C语言来写代码,用这三种结构,基本就可以描述生活中的每一个场景了
C语言中提供了两种选择语句
if (表达式)
语句1;
else
语句2;
如果满足表达式,即表达式为真,则执行语句1;
若不满足表达式,即表达式为假,则执行语句2。
那何为真假呢?
0表示假,非0表示真
if就是是否、如果的意思,表示判断,比如下面这段代码,他的意思是 判断a的值是1还是0
应该是很容易理解的
#define _CRT_SECURE_NO_WARNINGS 1
#include "stdio.h"
int main()
{
int a;
printf("恭喜进入大学\n");
printf("你打算好好学习吗?\n");
printf("1.好好学习 0.不好好学习\n");
scanf("%d", &a);
if (a==1)//判断a的值
{
printf("提升自己\n");
}
else
{
printf("无法就业\n");
}
return 0;
}
//多分支
if (表达式1)
语句1;
else if (表达式2)
语句2;
else
语句3;
这是if-else语句的升级版,多了几个分支,同样如果表达式1为真,则执行语句1;表达式2为真,则执行语句2;否则,执行语句3
当然,依照这样的代码,根据选择条件,我们可以有若干个分支
当我们要写多条语句时,要用到{},将代码括起来。如下
if (a==1)
{
printf("提升自己\n");
printf("成为大佬\n");
}
else
{
printf("自暴自弃");
printf("自暴自弃");
}
注意:else的匹配:else是和它离的最近的if匹配的
多分支语句的执行顺序
if (a=1)
这样一来就不是判断了,而是将1赋值给a!!!
因此我们要写成下面这样
if (a==1)
switch语句从字面上讲,可以称为开关语句,是一种多分支选择结构,一般与case、break、default配合使用,对流程进行控制。
switch(表达式){
case 常量表达式1: 语句1;
case 常量表达式2: 语句2;
……
case 常量表达式n: 语句n;
default: 语句n+1;
}
其中switch、case、break、default都是关键词。
switch作为一个开关
switch
语句常常用于多分支的情况。
这种多分支,一般指的是很多很多分支,用if-else来书写非常麻烦,且判定条件主要以整型为主:
如:输入数字,输出相应的星期几
#include <stdio.h>
int main()
{
int day = 0;
printf("请输入:");
scanf("%d", &day);
switch (day) {
case 1:
printf("星期一\n");
break;
case 2:
printf("星期二\n");
break;
case 3:
printf("星期三\n");
break;
case 4:
printf("星期四\n");
break;
case 5:
printf("星期五\n");
break;
case 6:
printf("星期六\n");
break;
case 7:
printf("星期天\n");
break;
default:
printf("Error\n");
}
return 0;
}
switch语句非常有用,但在使用时必须谨慎。所写的任何switch语句都必须遵循以下规则:
C语言中提供了三种循环语句
while语句可以在条件表达式为真的情况下,循环执行指定的一段代码,直到表达式不为真的时结束。
while(表达式)
{
语句块
}
#include <stdio.h>
int main (void)
{
int i=0; //初始条件i=0;
while(i<10) //while 循环
//while(表达式) 如果为真执行{ }里面语句块。
{
printf("i的值为:%d\n",i); //输出i的值
i++; //自增
}
return 0;
}
这段代码是输出0-9的数
for循环是一种常用的shell编程语句,用于重复执行一段代码块,直到满足某个条件为止。
for循环通常用于遍历数组、文件列表等场景
for (表达式 1 ;表达式 2 ;表达式 3) {
若干语句;
}
日常代码输出
#include <stdio.h>
int main( ){
printf("爱惜粮食,从一粒米开始\n");
printf("爱惜粮食,从一粒米开始\n");
printf("爱惜粮食,从一粒米开始\n");
printf("爱惜粮食,从一粒米开始\n");
printf("爱惜粮食,从一粒米开始\n");
printf("爱惜粮食,从一粒米开始\n");
printf("爱惜粮食,从一粒米开始\n");
printf("爱惜粮食,从一粒米开始\n");
printf("爱惜粮食,从一粒米开始\n");
printf("爱惜粮食,从一粒米开始\n");
}
用for循环输出
#include <stdio.h>
int main(){
int i ;
for (i=1;i<=10;i++)
printf("爱惜粮食,从一粒米开始\n");
}
do while循环语句是一种循环控制语句
在执行循环体之前先判断循环条件。即无论循环条件是否成立,先执行循环体中的语句,然后再判断循环条件是否成立,如果成立则继续执行循环体,直到循环条件不成立时结束循环。
与while循环不同的是,do while循环保证循环体至少执行一次。
do
{
循环体;
}
while (循环条件);
用do-while输出1到10的值
#include <stdio.h>
int main (void)
{
int i = 1; // 设置初始值,i为1。
do
{
printf("数值为:%d\n",i);
i++;
}
while(i<=10); //先执行语句,在判断结果。
return 0;
}
函数的概念第一次出现在我们的数学中,例如:f(x)
但是,在C语言中,函数可不同于数学中的函数;C语言中函数就是具有某项功能的代码段,它是C语言管理代码的最小单位(英言是function),早期被翻译成函数,就一直沿用了,现在新的编程语言都翻译成方法。
函数把具有某些功能的若干行代码封装在函数中方便管理代码且方便重复调用
维基百科中,对函数的定义:子程序
像上面描述的这些基础功能,它们并不是业务性的代码。我们在开发的过程中每个程序员都可能用得到,为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员进行程序开发
库函数不是C语言提供的
C语言标准中预定好,由编译器的厂商提供实现
比如strlen函数,C语言标准规定:
当标准规定好后,这时候A厂商和B厂商写出了这个函数,功能一样,但是由于不同厂商的程序员编写方法不同,内部的实现细节可能不同
size_t strlen(const char*str)这就是strlen函数的原型
所有的库函数都在标准库中
那么我们怎么学习库函数呢?
这里我推荐大家可以通过一个网站来学习:
C语言中,常用的库函数都有:
例如 stdio.h 头文件包含标准输入输出函数
例如 math.h 头文件包含数学相关的函数
double pow ( double base , double exponent ) ;
他的意思是求base的exponent次方
pow库函数包含在math.h头文件中
https://cplusplus.com/reference/cmath/pow/
他的使用方法是这样的
#include "math.h"
#include "stdio.h"
int main(){
pow(x,y);//求x的y次方
return 0;
}
这里我们知道他的结果是一个整数,可以强制转换成int型
cplusplus.com/reference/cmath/pow/
char * strcpy ( char * destination , const char * source ) ;
把source指向的字符串拷贝到destination的数组空间里面去
strcpy库函数包含在string.h头文件中
举个例子
https://cplusplus.com/reference/cstring/strcpy/
void * memset ( void * ptr , int value , size_t num ) ;
把ptr指向的内存块中前num个字节的内容设置成value值
memset库函数包含在string.h头文件中
https://cplusplus.com/reference/cstring/memset/
如果库函数能够干所有的事情,那还要程序员干什么?
所以更加重要的是自定义函数
自定义函数和库函数一样,都有函数名,返回值类型和函数参数
但是不一样的是这些都是我们自己来设计,这给程序员一个很大的发挥空间
ret_type fun_name ( para 1 , * ) { statement;//语句项 } ret_type 返回类型 fun_name 函数名 para 1 函数参数
函数参数可以没有,可以是一个,可以是多个
总结一下就是函数有以下四个部分
这四个部分都体现出来,这个函数基本就完成了
函数的参数分为实参和形参
即实际参数 形式参数
真实传给函数的参数,叫实参
实参可以是:常量、变量、表达式、函数等
无论实参是何种类型的量,在进行函数调用的时候,都必须有确定的值,以便把这些值传送给形参
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数
形式参数当函数调用完成之后就自动销毁了,因此形式参数只在函数中才有效
因此,对形参的修改,不会改变实参
函数和函数之间可以根据实际的需求进行组合,也就是互相调用的
嵌套调用就是某个函数调用另外一个函数
把一个函数的返回值作为另一个函数的参数
在使用函数之前,先告诉编译器有这个函数
在未来的工程中,代码是比较多的 函数一般是放在.h文件中声明,在.c文件中实现的
test.h 放置函数的声明
test.c 放置函数的实现
结束符\0 ASCII码为0,即为空字符NULL,占用一个字符位。
所有的字符串操作函数,都会遇0而止
仅有字符数组(即char型数组)
字符数组的长度要比实际存储字符串的长度至少多1 (int型等其他数组的末尾不需要加\0)
注意: 由于gets识别换行符\n作为输入结束,因此若scanf完一个整数后,如需使用gets函数, 需要先用getchar接收整数后的换行符,或应在scanf末尾加入\n
puts函数和printf在输出字符串的时候遇到'\0'和'\n'分别是怎么处理的
c语言中,puts和printf函数有啥区别?
puts和printf函数的区别如下:
格式字符串包含三种类型的对象:
(1)字符串常量 (2)格式控制字符串 (3)转义字符
(1)puts()函数只能输出字符串,不能输出值或执行格式转换。 (2)字符串可以直接写入puts()函数。例如:如:puts("Hello,world!")。 (3)puts与printf相同,puts()函数的作用与printf语句相同。注意:puts将在输出字符串之后自动输出回车
写出一段代码将输入的十进制数分别用八进制和十六进制进行输出
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main() {
int a = 0;
scanf("%d", &a);
printf("%d的八进制为%#o\n", a,a);//%o
printf("%d的十六进制为%#X\n",a,a);//%X
//printf可以使用使用格式控制串“%o”、“%X”分别输出八进制整数和十六进制整数,并使用修饰符“#”控制前导显示
return 0;
}
%d整型输出,%ld长整型输出 %o以八进制数形式输出整数 %x以十六进制数形式输出整数 %u以十进制数输出unsigned型数据(无符号数) %c用来输出一个字符 %s用来输出一个字符串 %f用来输出实数,以小数形式输出 %e以指数形式输出实数 %g根据大小自动选f格式或e格式,且不输出无意义的零
参考资料来源:puts ()_百度百科
参考资料来源:printf(格式化输出函数)_百度百科
+ - * / %
我们可以在VS中运算一下
<< 左移操作符 >> 右移操作符 注意:移位操作符的操作数只能是整数
可以这样写代码
int x = 7 >> 1;
移位操作符移动的是二进制位
对于一个整数是4个字节,一个字节是8个bit位,那么一个整数就是32个bit位
一个整数写出二进制序列的时候,就是32个bit位
所以,负数写成二进制序列的话,最高位一定是1;正数写成二进制序列,最高位一定是0;
整数的二进制表示形式有三种:原码、反码、补码
对于正整数来说,原码、反码、补码相同
对于负数来说 原码:按照数值的正负,直接写出的二进制序列 反码:原码的符号位不变,其他位按位取反(0变成1,1变成0) 补码:反码的二进制+1
0当作无符号数看待
整数在内存中存的都是补码
举个例子:
10: 原码:00000000 00000000 00000000 00001010 反码:00000000 00000000 00000000 00001010 补码:00000000 00000000 00000000 00001010
-10: 原码:10000000 00000000 00000000 00001010 反码: 11111111 11111111 11111111 1111 0101 补码: 11111111 11111111 11111111 1111 0110
我们用图来解释
左移一位即是,二进制序列向左移动一位,在末尾补一个0,形成新的二进制序列 移位的时候移动的是补码
左移n位效果相当于,乘上2的n次方
m只参与运算,m的值不变,这里m移位的结果其实是n的值
所以我们总结下来,向左移位的规则就是:左边丢弃,右边补0
负数同样的道理
移位规则:
首先右移运算分为两种:
逻辑右移还是算数右移,取决于编译器 ;绝大部分编译器采取的是算数右移
右移n位的效果相当于,除以n的k次方
注意:不管是左移还是右移,不要移动负数位,比如:>>-1,<<-1
位操作符有:
& //按位与 | //按位或 ^ //按位异或 注意:操作数必须是整数
这里我们所说的按位,都是按二进制位
都是按照补码进行运算的
举个例子
所以,按位与 & 的运算规则是
按位与的使用,可以得到想得到的位:先移位,再按位与
比如,我想得到3的最低位,那么我就按位与1
如果想得到第n位,那么可以把第n位移到最低位,再按位与1
举个例子
所以,按位或 | 的运算规则是
举个例子
异或运算的特点:
按位异或的使用,根据相同为0,相异为1;比如
a^a=0 0^a=a
我们可以利用异或运算的特点,用不创建临时变量的方法交换两个数的值;看代码
int main()
{
int a, b;
scanf("%d %d", &a, &b);
a = a ^ b;
b = a ^ b;//即 a ^ b ^ b = a ^ 0 = a;
a = a ^ b;//即 a ^ b ^ a = 0 ^ b = b;
printf("%d %d", a, b);
return 0;
}
赋值操作符是一个非常棒的操作符,他可以让你得到一个你之前不满意的值,也就是可以重新赋值
int weight = 120;//体重
weight = 89;//不满意就赋值
double salary = 10000.0;
salary = 20000.0;//使用赋值操作符赋值
赋值操作符可以连续使用,比如:
int a = 10;
int x = 0;
int y = 20;
a = x = y + 1;//连续赋值
这样的写法更加清晰而且易于调试
+= -= *= /= %= >>= <<= &= |= ^=
这些运算符都可以写成复合的效果,比如:
int x = 10;
x = x + 10;
x += 10;//复合赋值
同样的道理,这样写更加简洁
! 逻辑反操作 - 负值 + 正值 & 取地址 sizeof 操作数的类型长度(以字节为单位) ~ 对一个数的二进制按位取反 -- 前置、后置 -- ++ 前置、后置 ++ * 间接访问操作符(解引用操作符) (类型) 强制类型转换
想要把假变成真,把真变成假的时候
如果 a 为真,则!a为假
int a = 10;
&a;//a变量的地址
int arr[10];
&arr;//这是数组的地址
int*p = &a;//可以用指针变量接收地址
解引用操作符 * 和取地址符 & 通常是搭配使用的
*p;//对p进行解引用操作,*p是通过p中存放的地址找到p指向的对象
sizeof 是操作符而不是函数
~的作用是全部取反,无论是符号位还是其他位,比如:
++是一种自增1的操作
自增分为:
前置++:即++a,先+1,后使用
后置++:即a++,先使用,后+1
--是一种自减1的操作
自减分为:
前置 --:即--a,先-1,后使用
后置 --:即a--,先使用,后-1
> 大于 >= 大于等于 < 小于 <= 小于等于 != 不等于 用于测试“不相等” == 等于(表判断) 用于测试“相等”
在编程的过程中,注意不要把 == 和 = 写错了
&& 逻辑与 || 逻辑或
区分逻辑与和按位与
区分逻辑或和按位或
1&2 ------>0 1&&2 ------>1 //逻辑与 1|2 ------>3 1||2 ------>1 //逻辑或
逻辑与&&表示并且的意思:同时为真才为真
举个例子:a&&b&&d++
//这个时候如果a为假,则&&后面不再执行;如果a为真,则判断b,b如果为假,则&&后面的d++不再执行
逻辑或 || 表示或者的意思:一个为真就为真
举个例子:a||b||d++
//这个时候如果a为假,则判断b,b如果为假,则||后面的d++不再执行;如果a为真,则||后面不再执行
条件操作符也称为三目操作符
exp1 ? exp2 : exp3
条件操作符的作用是:
exp1 , exp2 , exp3 , ... expN
逗号表达式,就是用逗号隔开的多个表达式
逗号表达式,从左向右依次执行,整个表达式的结果是最后一个表达式的结果
int main() {
int a = 1;
int b = 2;
int c = (a > b, a = b + 10, a, b = a + 1);
printf("%d", c);
return 0;
}
a = get_val();
count_val(a);
while (a > 0) {
//业务处理
a = get_val();
count_val(a);
}
如果用逗号表达式改写:
while (a = get_val(), count_val(a), a > 0) {
//业务处理
}
"[ ]"就是下标引用操作符,用在数组中
操作数:一个数组名+一个索引值
int arr[10];//创建数组
arr[9] = 10;//使用下标引用操作符
[ ]的两个操作数是arr和9
[ ]的操作数有两个
"( )"就是函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩下的操作数是传递给函数的参数
void test(int a, int b) {
return a + b;
}
int main() {
int a, b;
test(a, b);
return 0;
}
函数名和参数都是()的操作数
. 结构体.成员名 -> 结构体指针->成员名
结构体.成员名
struct Book {
char name[20];
int price;
};
int main() {
struct Book b = { "C语言指南",55 };
printf("%s %d", b.name, b.price);
return 0;
}
结构体指针->成员名
struct Book {
char name[20];
int price;
};
void Print(struct Book* pb)
{
printf("%s %d", pb->name, pb->price);
}
int main() {
struct Book b = { "C语言指南",55 };
Print(&b);
return 0;
}
这两句代码是等价的
printf("%s %d", (*pb).name, (*pb).price);
printf("%s %d", pb->name, pb->price);
表达式求值的顺序一部分是由操作符的优先级和结合性决定
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型
C的整型算数运算总是至少以缺省型类型的精度来进行的
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升
整型提升的意义
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度 因此,即使两个char类型的相加,在CPU执行时实际上也要先转换位CPU内整型操作数的标准长度
通用CPU是难以直接实现两个8bit字节直接相加运算,所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算
整型提升是按照变量的数据类型的符号位来提升的
整型提升针对的是自身大小 小于整型大小的操作数
如果某个操作符的各个操作数属于不同的类型,那么除非其中以一个操作数转换为另一个操作数的类型,否则操作就无法进行
下面的层次体系称为寻常算术转换
long double double float unsigned long int long int unsigned int int
如果某个操作数的类型再上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算
比如,int 和 float 运算,会把 int 转换成 float(向上转换)
但是算术转换要合理,要不然会有一些潜在的问题
复杂表达式的求值有三个影响因素:
两个相邻的操作符先执行哪一个?取决于他们的优先级,如果两者的优先级相同,取决于他们的结合性
优先级 | 运算符 | 名称或含义 | 使用形式 | 结合方向 | 说明 |
---|---|---|---|---|---|
1 | [] | 数组下标 | 数组名[常量表达式] | 左到右 | ----- |
() | 圆括号 | (表达式)/函数名(形参表) | ----- | ||
. | 成员选择(对象) | 对象.成员名 | ----- | ||
-> | 成员选择(指针) | 对象指针->成员名 | ----- | ||
2 | - | 负号运算符 | -表达式 | 右到左 | 单目运算符 |
(类型) | 强制类型转换 | (数据类型)表达式 | ----- | ||
++ | 前置自增运算符 | ++变量名 | 单目运算符 | ||
++ | 后置自增运算符 | 变量名++ | 单目运算符 | ||
-- | 前置自减运算符 | --变量名 | 单目运算符 | ||
-- | 后置自减运算符 | 变量名-- | 单目运算符 [4] | ||
* | 取值运算符 | *指针变量 | 单目运算符 | ||
& | 取地址运算符 | &变量名 | 单目运算符 | ||
! | 逻辑非运算符 | !表达式 | 单目运算符 | ||
~ | 按位取反运算符 | ~表达式 | 单目运算符 | ||
sizeof | 长度运算符 | sizeof(表达式) | ----- | ||
3 | / | 除 | 表达式/表达式 | 左到右 | 双目运算符 |
* | 乘 | 表达式*表达式 | 双目运算符 | ||
% | 余数(取模) | 整型表达式/整型表达式 | 双目运算符 | ||
4 |
| 加 | 表达式+表达式 | 左到右 | 双目运算符 |
- | 减 | 表达式-表达式 | 双目运算符 | ||
5 | << | 左移 | 变量 | 左到右 | 双目运算符 |
>> | 右移 | 变量>>表达式 | 双目运算符 | ||
6 | > | 大于 | 表达式>表达式 | 左到右 | 双目运算符 |
>= | 大于等于 | 表达式>=表达式 | 双目运算符 | ||
< | 小于 | 表达式 | 双目运算符 | ||
<= | 小于等于 | 表达式 | 双目运算符 | ||
7 | == | 等于 | 表达式==表达式 | 左到右 | 双目运算符 |
!= | 不等于 | 表达式!= 表达式 | 双目运算符 | ||
8 | & | 按位与 | 表达式&表达式 | 左到右 | 双目运算符 |
9 | ^ | 按位异或 | 表达式^表达式 | 左到右 | 双目运算符 |
10 | | | 按位或 | 表达式|表达式 | 左到右 | 双目运算符 |
11 | && | 逻辑与 | 表达式&&表达式 | 左到右 | 双目运算符 |
12 | || | 逻辑或 | 表达式||表达式 | 左到右 | 双目运算符 |
13 | ?: | 条件运算符 | 表达式1? 表达式2: 表达式3 | 右到左 | 三目运算符 |
14 | = | 赋值运算符 | 变量=表达式 | 右到左 | ----- |
/= | 除后赋值 | 变量/=表达式 | ----- | ||
*= | 乘后赋值 | 变量*=表达式 | ----- | ||
%= | 取模后赋值 | 变量%=表达式 | ----- | ||
+= | 加后赋值 | 变量+=表达式 | ----- | ||
-= | 减后赋值 | 变量-=表达式 | ----- | ||
<<= | 左移后赋值 | 变量 | ----- | ||
>>= | 右移后赋值 | 变量>>=表达式 | ----- | ||
&= | 按位与后赋值 | 变量&=表达式 | ----- | ||
^= | 按位异或后赋值 | 变量^=表达式 | ----- | ||
|= | 按位或后赋值 | 变量|=表达式 | ----- | ||
15 | , | 逗号运算符 | 表达式,表达式,… | 左到右 | 从左向右顺序运算 |