一、第一个程序简解
1.1代码注释方式
方式1:注释一行
//....
方式2:注释多行
/*
...
*/
方式3:注释多行
#if 0 //当数字为1时,程序为非注释状态
...
#endif
1.2中英文切换
ctrl + space
1.3代码讲解
#include <stdio.h>
#:预处理指令,在gcc编译的第一个阶段就会执行的东西
#include :要包含的头文件,要使用哪个函数,就需要包含声明所在的头文件
<>:用来包含头文件,也可以使用 " "
stdio.h:标准输入输出头文件,printf函数就是在这个头文件中声明的
int main(int argc,char *argv[])
int:整数类型,放在这里代表函数的返回值类型
main:函数名,主函数,是所有函数的入口函数,一个程序只能有一个主函数,而且必须有
():里面存放参数
{}:用来包含这个函数的代码(函数体)
【注意:函数中每一行代码执行的末尾必须加上 “ ; ”】
{
printf("hello world!\n");
return 0;
}
printf:向终端格式化输出内容(此处为字符串)
\n:换行
return:返回值,返回函数执行的结果
0:返回的值,这个值的类型与返回值类型一致
【注意:main函数里面返回0实际上是告诉linux内核这个程序执行完毕了】
1.4 gcc编译会出现的问题及其解决方式
1)正确编译:编译完成后没有任何现象
2)出现警告:警告还是会生成可执行文件,一般的警告可以忽略不管,但是能处理的还是要处理
3)出现错误:出现错误就不会生成可执行文件,必须要处理,可以根据出错的行数找到出错的位置,一般就是在这一行的上下行出现问题了。
eg:如果少加分号,会提示哪一行出问题了
【注意:如果标点符号使用了中文的标点符号也会报错】
二、计算机的数据表示
分类:
数值型数据
非数值型数据
2.1 数值型数据
数值型数据的表示方法:二进制(B),十进制(D),八进制(O),十六进制(H)
[1] 二进制
由0和1表示的数据称之为二进制数据,前导符为:0b
例如:0b1001101001
[2]十进制,前导符为:D
由0-9表示的数据称之为十进制数,我们人类所识别的就是十进制数
例如:256
二进制转十进制:
0b11011 ——>12^0 + 12^1 + 02^2 + 123+1*24 = 27
十进制转二进制
凑数或这除商取余倒着往上数(由低到高)
eg:
[3] 八进制,前导符为:?【疑问:前导符为O还是0】
由0-7组成的数称之为八进制数,前导符为:0
例如:0756
八进制转十进制:
0627:—>78^0 + 28^1 + 68^2 = 407
八进制转二进制:一位八进制用三位二进制表示
0627:—>0b110010111
二进制转八进制:将二进制从右往左每三位转化位八进制
0b 10 100 010 001 110 -->0 24216
[4] 十六进制,前导符为:0x
由0-9和a-f组成的数称之为十六进制数,使用0x作为前导符
例如:0xa6bd
十六进制转十进制:
0xa6bd —>1316^0 + 1116^1 + 616^2 + 10 *16^3
十六进制转二进制:一位十六进制数用四位二进制来表示
0xa6bd —>0b1010011010111101
二进制转十六进制:从右往左取四位转为十六进制
0b 0001 1001 0010 0110—>0x1926
2.2 非数值型数据
理论上,非数值型数据计算机时不能识别的,因为计算机只能识别二进制码,也就是数值型数据,但是在写代码的过程中,还需要非数值型数据,所以别人就规定了,每一个非数值型数据都会用一个数值型数据来表示,表示方法称之为ascll码表,我们将非数值型数据也称之为字符。
使用man ascll可以查询
'\0':----> 0
'\n':---->10
'0' - '9':----> 48 - 57
'A' - 'Z':----> 65 - 90
'a' - 'z':----> 97 - 122
【注意:1.ASCII中的0-31为控制字符;32-126为打印字符;127为Delete(删除)命令。
2.ASCII扩展字符——(为了适应更多字符)128-255,或者-128~-1,其中,-128对应128,依次递增对应。】
【可参考大佬:https://blog.csdn.net/jiayoudangdang/article/details/79828853?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164234491016780274129826%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=164234491016780274129826&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-79828853.pc_search_result_control_group&utm_term=ascll%E7%A0%81%E8%A1%A8&spm=1018.2226.3001.4187】
三、词法符号
任何高级语言都有自定义的词法符号和支持的数据类型,词法符号是语言的基本组成单位,数据类型是数据的基本属性,词法符号是程序设计语言中有若干个字符组成的有意义的最小语法单位。
按照词法符号在程序中的作用可分为:关键词,标识符,分隔符,运算符和标点符号。
【注意:词法符号但凡涉及单词,严格区分大小写。】
3.1 关键词
关键词是由系统预定义的词法符号,有特定的含义,不允许用户重新定义,必须都是小写,可以直接使用
char short int long float double
enum struct union void signed unsigend 12个
auto register const static volatile extern 6个
typedef 1个
sizeof 1个
if else switch case default for
while do goto return break continue 12个
3.2 标识符
标识符就是给代码中一些常用的东西取名字,例如:变量名,函数名,结构体名,宏定义名,取别名,且这些名字最好与要表示的东西的含义一样。
标识符的命令规则:
只能由数字,字母,下划线_组成
【注意:标识符的第一个字符不能是数字,并且不能与关键词相同】
【注意:c语言中,关键词和标识符都严格区分大小写】
3.3 分隔符
分隔符主要用于分隔其它的词法符号
主要包括:
空格符,制表符,执行符号,注释。通过对分隔符的恰当运用,使得代码的外观清晰易读,易于分析语法错误
3.4 运算符
运算符是表示运算的词法符号,c语言有着丰富的运算
按照功能分:
算术运算符,逻辑运算符,关系运算符,位运算符,赋值运算符,自增自减运算符,地址运算符,逗号运算符
sizeof运算符【注意:sizeof既可以作为标识符也可以作为运算符】
3.5 标点符号
逗号,分号,冒号,花括号,圆括号,标点符号的作用和分隔符类似,但是用法有明确的语法规定,有些标点符号出现在表达式中时,当作运算符使用。
四、数据类型
数据类型
1.基本类型:
1)
整形:short,int,long,long long
有符号:signed
无符号:unsigned
2):字符型:char
3):实型:float double
4): 枚举类型:enum
2.构造类型
1)结构体 struct
2)共用体 union
3.指针类型
4.空类型 void
逻辑类型
只有两个量true和lfalse ,表示逻辑真和逻辑假
4.1 逻辑类型bool类型
#include <stdio.h>
#include <stdbool.h>
int main(int argc, const char *argv[])
{
//定义一个bool类型的变量并赋值为10
//注意:c语言默认不支持bool类型,需要使用_Bool或者添加头文件stdbool.h
//bool类型只有两个值0和1,非0即为真,所有所有非0的值都等于1
//bool类型主要用判断语句,判断条件是否成立
bool b = 10;
//%d:有符号整型输出
printf("b = %d\n",b);
bool c = 0;
printf("c = %d\n",c);
bool d = 0.0000000001;
//%d:有符号整型输出
printf("d = %d\n",d);
_Bool e = 23456879;
printf("e = %d\n",e);
return 0;
}
4.2 整数类型
4.2.1 char
char类型占一个字节
有符号:char或者 signed char
取值范围:[-2^7,2 ^7-1] —> [-128 ,- 127]
无符号: unsigned char
取值范围:[0,2^8-1] --> [0,255]
4.2.2 short
short类型占两个字节
有符号:short或者 signed short
取值范围:[-2^16/2,2 ^15-1] —> [-32768 , 32767]
无符号: unsigned short
取值范围:[0 , 2^16-1] --> [0 ,65535]
4.2.3 int
int类型占四个字节
有符号:int或者 signed int
取值范围:[-2^32/2 , 2 ^31-1] —>[ -2,147,483,648 ,2,147,483,647]
无符号: unsigned int
取值范围:[0, 2^32-1] --> [0 ,4,294,967,295]
4.2.4 long
long类型在32位操作系统占四个字节,在64位操作系统占8个字节
有符号:long或者 signed long
取值范围:[-2^64/2 ,2 ^63-1 ]
无符号: unsigned long
取值范围:[0 , 2^64-1]
#include <stdio.h>
int main(int argc, const char *argv[])
{
//定义一个char类型变量并赋值
char a = 100;
printf("a = %d %c\n",a,a);
short b = 20000;
printf("b = %d\n",b);
int c = 34253452;
printf("c = %d\n",c);
//如果是long类型,要用%ld输出
long d = 12345678998765;
printf("d = %ld\n",d);
return 0;
}
4.3 浮点类型
浮点类型也叫做实型,就是带有小数点的数据
float 单精度 占4个字节
double 双精度 占8个字节
#include <stdio.h>
int main(int argc, const char *argv[])
{
//float类型输出变量要使用%f
//float类型默认保留小数点后6位,并且会四舍五入
float a = 3.141592653;
printf("a = %f\n",a);
float b = 5.6;
printf("b = %f\n",b);
//如果一个浮点数数据非常大,末尾小数可能会丢失精度
float c = 234567864.999;
printf("c = %f\n",c);
double d = 24.23456122;
printf("d = %lf\n",d);
return 0;
}
4.4 数据在计算机中的存储
人为设置的数据称为:原码
存储在计算机中的数据称为:补码
为了实现原码和补码的转换,加入了反码
存储数据的顺序:原码—>反码---->补码
取出数据的顺序:补码—>反码---->原码
如果一个数是正数,原码 反码 补码都一样
如果一个数是负数,最高位为符号位,符号位为1表示负数
负数的反码等于原码取反(将这个数转化为二进制之后,原本1变成0,0变成1,),但是符号位不变,补码等于反码加1。
【原码–>补码 即:除了符号位外,全部取反,然后末位加一】
【技巧:存储时看数据,取出时看类型】
eg:
unsigned char = 100;
存储时:
原码:0110 0100
反码:0110 0100
补码:0110 0100
取出时:
补码:0110 0100
反码:0110 0100
原码:0110 0100 = 100
signed char = -10;
存储时:
原码:1000 1010
反码:1111 0101
补码:1111 0110
取出时:
补码:1111 0110
反码:1111 0101
原码:1000 1010
signed char = 129;
存储时:
原码:1000 0001
反码:1000 0001
补码:1000 0001
取出时:
补码:1000 0001
反码:1000 0000
原码:1111 1111 = -127
为什么 -128-1 = 127?
【通过补码的运算,溢出的数字直接丢弃】
【所有的减法计算全部转换成加法运算,eg:a-b 即:a+(-b)】
五、常量
常量是指在程序在运行期间其数值不会发生变化的数据
5.1 整数常量
十进制: %d
八进制:%#o
十六进制:%#x
#include <stdio.h>
int main(int argc, const char *argv[])
{
int num = 1000;
printf("num = %d\n",num);
printf("num = %#o\n",num);
printf("num = %#x\n",num);
int a = 0756;
int b = 0xab8f;
int c = 0b100011110;
printf("a = %d, %#o %#x\n",a,a,a);
printf("b = %d, %#o %#x\n",b,b,b);
printf("c = %d, %#o %#x\n",c,c,c);
return 0;
}
5.2 浮点型常量和指数常量
float:%f
double: %lf
指数常量就类似于科学计数法:
678900---->6.789*10^5---->6.789e+5 //使用%e输出指数常量
指数常量的格式:<+/->m.ne<+/->i;
例如:-3.14*10^-7 ---->-3.14e-7
#include <stdio.h>
int main(int argc, const char *argv[])
{
float a = 345568.123;
printf("a = %f\n",a);
printf("a = %e\n",a);
float b = -1.78e-5;
printf("b = %f,%e\n",b,b);
return 0;
}
5.3 字符常量
字符:将一个单一的非数值型数据用单引号引起来,就将其称之为一个字符。
eg:‘a’ ‘b’ ‘9’
#include <stdio.h>
int main(int argc, const char *argv[])
{
char a = 'w';
printf("a = %d %c\n",a,a);
char b = 97;
printf("b = %d %c\n",b,b);
char c = 'w';
printf("c = %c\n",c);
c = c-32;
printf("c = %c\n",c);
return 0;
}
执行结果:
5.4 字符串常量
将一个或者多个字符通过双引号引起来,将其称之为一个字符串,每一个字符串的结尾都会自动加一个’\0’
例如:“Helloworld” “a” “abc” “1234”;
【注意:
a 是一个变量
‘a’:是一个字符常量,占一个字节
“a”:是一个字符串常量,占两个字节】
#include <stdio.h>
int main(int argc, const char *argv[])
{
//定义一个字符数组保存字符串
char str[32] = "hello world";
//使用%s来输出一个字符串
printf("str = %s\n",str);
//遇到\0表示字符串结束
char str1[32] = "hello wo\0rld";
printf("str1 = %s\n",str1);
return 0;
}
5.5 标识常量 ----宏定义
将一个常量或者常量表达式标识成另一个标识符,使用这个标识符就相当于使用这个常量或者常量表达式,也将其称之为宏定义,这个标识符一般使用大写字母表示,并且要满足标识符的命名规则。
格式:
#define 标识符 常量或者常量表达式
例如:
#define N 32
#include <stdio.h>
#define M 128
#define N 32
#define SUM M+N
#define SUM1 (M+N)
#define SUB(a,b) (a-b)
int main(int argc, const char *argv[])
{
int i = M + 5;
printf("i = %d\n",i);
int a = N +M;
printf("a = %d\n",a);
int b = SUM + 100;
printf("b = %d\n",b);
//128 + 32 * 10
int c = SUM*10;
printf("c = %d\n",c);
//(128 + 32) *10
int d = SUM1 *10;
printf("d = %d\n",d);
int e = SUB(10,5) + 100;
printf("e = %d\n",e);
return 0;
}
练习
一个水分子的质量约为3.0*10^-23g,1L水大约950g,编写一个程序,要求输入水的升数,然后显示这么多升水中包含多少个水分子。
框架:
#include <stdio.h>
int main(int argc, const char *argv[])
{
int num;
scanf("%d",&num);
printf("num = %d\n",num);
return 0;
}
答案:
#include <stdio.h>
#define WATERWEIGHT (3.0e-23)
#define WATERL 950
int main(int argc, const char *argv[])
{
int L;
printf("请输入水的升数:\n");
scanf("%d",&L);
double num = WATERL /WATERWEIGHT * L;
printf("%dL水中有%e个水分子\n",L,num);
return 0;
}
六、变量
6.1 概念
变量,就是我们自己定义的变量名,变量就是可以改变其值的量,变量就是一个标识符,所以需要满足标识符的命令规则。
在程序的运过程中,变量占据一定的内存空间,其大小由变量数据类型来决定。
例如:
int a; (变量a在内存中占四个字节)
char b; (变量b在内存中就分配一个字节)
#include <stdio.h>
int main(int argc, const char *argv[])
{
//定义一个变量
int i;
//变量的初始化,在定义变量的同时赋值
int a = 100;
int b;
//变量赋值,这个变量已经定义好之后再赋值
b = 200;
//注意:在同一个{}内,同一个变量名不能定义两次
//long b = 88;
a = 888;
b = 999;
//变量和变量之间可以赋值
//等号左边叫做左值,等号右边叫做右值,是将右值赋值给左值
//右值不会发生改变
i = a;
return 0;
}
6.2变量定义格式
格式:
存储类型 数据类型 变量;
存储类型:有四种
auto【默认不写就是这个类型】
register
static
extern
数据类型:就是我们之前学习的char,int等
变量名:就是起名字,是一个标识符
6.3 类型转换
原本的变量在使用的额过程中需要改变他的类型,转换之后这个变量的类型以及存储空间都会发生改变。
类型转换的方法:
隐式类型转换:是操作系统或者编译器自动进行转换的
显示类型转换:又称之为强制类型转换,是我们人为直接转换的
【注意:显示类型转换容易出错】
显示的数据类型转换一般形式为:
(数据类型名称)<表达式>
#include <stdio.h>
int main(int argc, const char *argv[])
{
//隐式类型转换:当float类型赋值给int类型的时候,就会将a临时转换成int再赋值给b
//隐式类型转换默认一从低精度往高精度转
float a = 3.14;
int b = a;
printf("b = %d\n",b);
int i= -20;
unsigned int j = 6;
if(i + j > 0)
{
printf("i + j > 0,%u\n",i+j);
}
else
{
printf("i + j <= 0");
}
int m = 10,n = 4;
//printf("%d\n",m/n);
printf("%f\n",(float)m/n);
printf("%f\n",(float)m/(float)n);
return 0;
}
强制类型转换注意事项:
强致类型转换后面的表达式如果存在复杂运算,一定要用小括号括起来;
强制类型转换是一种不安全的转换,一般都是将高级类型转成低级类型,要丢失数据的精度;
强制类型转换并不改变表达式中变量的数据类型和其值。
七、运算符
7.1 算术运算符
双目运算符:运算符的两边各有一个变量(eg:a+b)
+,-,*,/, %
【%:取余,只能用在整数,浮点数据不能取余】
单目运算符:运算符边上只有一个变量(eg:a++)
++, – --
++:自增,每次自增1
– --:自减,每次自减1
【注意:
a++是先进行取值,后进行自增。
++a是先进行自增,后进行取值。 】
练习2:输入一个三位数,求个位,十位,百位的和
eg:789—> 7 + 8 + 9
789 /100 = 7
789 %10 = 9
789 %100 /10 = 8
答案
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
int num;
printf("请输入一个三位数:\n");
scanf("%d",&num);
if(num > 999 || num < 100)
{
exit(0);
}
int ge,shi,bai,sum;
ge = num % 10;
shi = num % 100 /10;
bai = num / 100;
sum = ge + shi + bai;
printf("sum = %d\n",sum);
return 0;
}
练习3:输入两个数,实现这两个数的交换
int m,n;
scanf("%d %d",&m,&n);
m = 100,n = 50;
…
m = 50,n = 100;
答案
#include <stdio.h>
int main(int argc, const char *argv[])
{
printf("请输入两个数:\n");
int m,n;
scanf("%d %d",&m,&n);
printf("交换前:m = %d,n = %d\n",m,n);
#if 1
int tmp;
tmp = m;
m = n;
n = tmp;
#else
m = m+n;
n = m-n;
m = m-n;
#endif
printf("交换后:m = %d,n = %d\n",m,n);
return 0;
}
7.2 关系运算符
> < >= <= == !=
#include <stdio.h>
int main(int argc, const char *argv[])
{
int a = 10,b = 20,c;
//使用关系运算符的表达式的结果是一个bool类型
//表达式为真,则结果为1,表达式为假,结果为0
c = a > b;
printf("c =%d\n",c);
c = a < b;
printf("c =%d\n",c);
if(a > b)
{
printf("hello world!\n");
}
else
{
printf("hello nanjing\n");
}
return 0;
}
7.3 逻辑运算符
逻辑运算符主要是用于连接多个有关系运算符连接的表达式,判断整体表达式为真为假。
!:逻辑反,原本表达式为真变为假,为假则变为真
!(A > B) ====> A <= B
&&:逻辑与,两个表达式都为真才为真,只要有一个为假则整体为假
||:逻辑或,有一个为真则整体为真,除非都为假才为假
判断一个变量a的值是否在10-20的范围内:
数学:10 <=a <=20;
c语言:a >= 10 && a <= 20
判断一个变量a的值是否不在10到20的范围内:
数学:a < 10 或 a > 20
c语言:a < 10 || a > 20
#include <stdio.h>
int f1()
{
printf("this is f1\n");
return 0;
}
int f2()
{
printf("this is f2\n");
return 1;
}
int main(int argc, const char *argv[])
{
/*
int a = 100,b;
//b = 10 <=a <=20;
b = a >=10 && a <= 20;
printf("b = %d\n",b);
*/
int a = 1,b = 0;
if(f1() && f2()) //短路原则:如果条件1不成立,则不会判断条件2
{
printf("hello world!\n");
}
if(a && b) //两个表达式都要成立
{
printf("hello tianjing!\n");
}
if(a ||b) //逻辑或,有一个表达式成立就行
{
printf("a || b成立!\n");
}
return 0;
}
7.4 位运算
-
但凡涉及到位操作,一定先将这个数转化为二进制数,然后再操作
- 位逻辑反
& 位逻辑与,同为1则为1,否则为假, 1&1 = 1 ,1&0 = 0 ,0 &0 = 0
| 位逻辑或,只要有一个为1则为1
^ 位逻辑异或,相同为0,不同为1 1^0 = 1 ,0^0 = 0 ,1^1 = 0
<< 左移
>> 右移
#include <stdio.h>
int main(int argc, const char *argv[])
{
unsigned char m = 0xa7,n = 0x3b;
unsigned char k;
//逻辑反
k = ~m;
//m:1010 0111
//~m:0101 1000 = 0x58
printf("k = %#x\n",k);
k = m & n;
//m: 1010 0111
//n: 0011 1011
//m&n: 0010 0011 = 0x23
printf("k = %#x\n",k);
//左移
k = m << 3;
//m:1010 0111
//m:0011 1000->0x38
printf("k = %#x\n",k);
//右移
k = n >> 3;
//n: 0011 1011
//n >>3:0000 0111-->0x07
printf("k = %#x\n",k);
return 0;
}
7.5 赋值运算符和赋值符合运算符
赋值运算符:=
左值 = 右值
【注意:左值必须是变量,右值可以是变量也可以是常量】
【注意事项:
=:表示赋值
==:表示判断是否相等】
赋值复合运算符:
a = a+b------>a+=b;
a^=b
a>>=b—>a = a >> b
7.6 条件运算符—三目运算符
语法格式:
表达式1?表达式2:表达式3
运行顺序:
先执行表达式1,如果表达式1为真则执行表达式2,如果表达式1为假,则执行表达式3
【条件运算符其实就是一个简单的if…else语句】
if(表达式1)
{
表达式2
}
else
{
表达式3
}
#include <stdio.h>
int main(int argc, const char *argv[])
{
int a = 100,b = 20,c;
c = a > b ? ++a : b++;
printf("a = %d,b = %d,c = %d\n",a,b,c);
return 0;
}
执行结果:
2.7 逗号运算符
语法格式:
a = (表达式1,表达式2,表达式3,表达式4);
执行顺序:
括号里面的表达式从左往右依次执行,最终整个表达式的结果是最后一个表达式的结果
【注意:逗号运算符必须使用括号】
#include <stdio.h>
int main(int argc, const char *argv[])
{
int a = 100,b = 40,c;
//c = (++a,++b,a = a+b,a++);
printf("a = %d,b = %d,c = %d\n",a,b,c);
c = a+++b;
//c = a+++(++b);
printf("a = %d,b = %d,c = %d\n",a,b,c);
return 0;
}
7.8 sizeof运算符
sizeof是一个关键字,也是一个运算符,主要用于获取一个数据类型或者变量所占内存的大小
语法格式:
n = sizeof(数据类型或者变量名)
n:用于保存这个数据类型或者变量名所在内存的字节数
例如:n = sizeof(int) —>n = 4;
#include <stdio.h>
int main(int argc, const char *argv[])
{
_Bool a;
char b;
short c;
int d;
long e;
float f;
double g;
long long h;
printf("sizeof(_Bool) = %ld %ld\n",sizeof(_Bool),sizeof(a));
printf("sizeof(char) = %ld %ld\n",sizeof(char),sizeof(b));
printf("sizeof(short) = %ld %ld\n",sizeof(short),sizeof(c));
printf("sizeof(int) = %ld %ld\n",sizeof(int),sizeof(d));
printf("sizeof(long) = %ld %ld\n",sizeof(long),sizeof(e));
printf("sizeof(float) = %ld %ld\n",sizeof(float),sizeof(f));
printf("sizeof(double) = %ld %ld\n",sizeof(double),sizeof(g));
printf("sizeof(long long) = %ld %ld\n",sizeof(long long),sizeof(h));
return 0;
}
执行结果:
7.9 运算符的优先级
【注:当不确定表达式中多个运算符的优先级时,使用()来解决】
后缀++或--的优先级大于前缀++或--的
*p++后缀++优先级高于*,所以先执行++,再执行*
*++p前缀++和*优先级一样,但是是从右向左结合,所以先执行++,再执行*
a = b = c = d; =的结合规律是从右向左,所以是从右向左依次赋值
a = b == c; 先判断b和c是否相等,然后将结果赋值给a
算术运算符(* / + -)的优先级大于位运算符,位运算符(& | ^)的优先级大于逻辑运算符(&& ||)
a & b + c,先算b+c的结果,然后再和a相与