C语言是一门面向过程的计算机编程语言,最常见用于底层开发。所谓计算机就是由应用软件、操作系统、驱动层、硬件等组成,在操作系统(含操作系统)之下的部分都叫做底层(下层)。
C语言是一门国际上最通用的计算机语言,为了避免因为各开发厂商根据自己的需求而构建不同的C语言体系,所以美国国家标准局制定了一套完整的美国国家标准C语言语法,为ANSI C,作为C语言最初的标准。现在最常用的C语言国际标准为c89和c90,最新的C语言标准为c11;
主流的编译器主要有Clang、GCC、WIN-TC、MSVC等
注:什么是编译?
c/c++是编译型计算机语言,编译是一个对代码进行处理的动作。一个test.c文件通过编译、链接后形成可执行文件test.exe。
推荐使用vs,附:vs2022安装使用教程
第一步:打开VS;
第二步:创建项目;
(1)创建新项目;
(2)选择空项目
①修改项目名称(有条理性)
②修改文件地址(自己创建一个专门的文件夹,路径不要出现中文名)
③完成;
第三步:创建源文件(.c文件)
(1)右击源文件;
(2)添加新建项——选择c++文件,将名称改为xxx.c形式;
第四步:写代码
#include
//main函数是程序的入口,一个工程中main函数有且只有一个
int main(){
printf("hello world");
return 0;
}
注:使用vs编译器时,由于对于vs编译器来说scanf(输入函数)和printf(输出函数)都是不安全的函数,所以在运行过程当中,会出现函数报错的现象,因此需要在代码的最开头使用宏定义(#define _CRT_SECURE_NO_WARNINGS) 来防止运行报错。
解决办法:
通过:Microsoft Visual Studio\2022\Community\Common7\IDE\VC\VCProjectItems找到一个叫做newc++file.cpp文件(直接通过文件夹查找也行),将此文件复制到桌面,通过记事本打开,将#define _CRT_SECURE_NO_WARNINGS粘贴进去,保存后关闭(使用管理员权限)。将修改过的文件重新粘贴回去,替换原来的文件。
原理:
vs每次创建源文件的时候,都会先拷贝一份newc++file.cpp中的内容,所以修改此文件,即可完成输入输出不报错处理;
数据类型 | 名称 | 大小 | 打印 | 数据量 | 数据率 |
char | 字符型 | 1B | %c | bit | bit/s |
short | 短整型 | 2B | %hd | 1Byte=8bit | 1Kb/s=1000b/s |
int | 整型 | 4B | %d | 1KB=1024B | 1Mb/s=1000Kb/s |
long | 长整型 | 4B | %ld | 1MB=1024KB | 1Gb/s=1000Mb/s |
long long | 更长的整型 | 8B | 1GB=1024MB | 1Tb/s=1000Gb/s | |
float | 单精度浮点型 | 4B | %f | 1TB=1024GB | |
double | 双精度浮点型 | 8B | %lf | 数据类型大小 | sizeof(int) |
(1)以上表格打印整数类型均为十进制、无符号形式输出,如果想要了解更多,可以参考C语言类型输出语句;
(2)数据类型:定义变量/常量/数组等等;申请形式:数据类型 变量名/常量名/数组名=xxx(值)。
(3)数据类型有两种,一种是系统内置的数据类型,比如说char,int等;还有一种是自定义的数据类型,比如说枚举常量。
对于固定不变的值,我们直接定义常量:数据类型 常量名=xxx(值);
对于可以改变的值,我们直接定义变量:数据类型 变量名=xxx(值);
变量分为全局变量和局部变量,相同级别的变量不能重复定义;
当全局变量和局部变量名字重复的情况下,局部变量优先级更高;
int main(){
int num1=0;
int num2=0;
scanf("%d %d",&num1,&num2);
int sum=num1+num2;
printf("%d\n",sum);
return 0;
}
(1)作用域是程序设计概念,通常来说,一段程序代码所用的名字并不总是有效的。而限定这个名字的代码范围就是这个名字的作用域;
(2)局部变量的作用域:变量所在的代码块内(离得最近的一个{}中);
(3)全局变量的作用域:整个工程(如果来自不同的文件,需要用“extern 数据类型 变量名”来引用全局变量);
(1)变量的生命周期指的是从变量的创建到销毁之间的一个时间段;
(2)局部变量的生命周期:从进入作用域时生命周期开始,到出作用域时生命周期结束;
(3)全局变量的生命周期:整个程序的生命周期;
直接能从字面上看出来的叫做字面常量
int main(){
printf("0101001");//直接能从字面上看出来的叫做字面常量
return 0;
}
以const修饰的常变量在C语言中只是在语法层面上限制了变量无法直接被修改,但是本质上还是一个变量,只是具有常量的属性,所以被称为常变量
int main(){
const int a=0101001;//用const修饰的常变量一般情况下不可修改;
printf("%d\n",a);
return 0;
}
define定义的标识符常量格式为:#define 常量名 值(无需;)
#define MAX 100
#define STR "abcdefg"
int main(){
printf("%d\n",MAX);
printf("%s\n",STR);
RETURN 0;
}
枚举常量:生活中可以一一列举出来保持不变的值;
enum Color{//三原色
RED,
GREEN,
BLUE
};
int main(){
enum Color a=RED;
return 0;
}
在C语言中,由双引号引起来的一串字符称为字符串字面值/字符串;在C语言中用字符数组存储字符串;
字符串的结束标志是一个\0的转义字符,在计算字符串长度的时候这个转义字符是结束标志,不算做字符串的内容;(计算字符串长度的函数:strlen()——需要引用头文件
int main(){
char arr1[]="abc";
char arr2[]={'a','b','c'};
char arr3[]={'a','b','c','\0'};
printf("%s\n",arr1);//abc
printf("%s\n",arr2);//abc烫烫烫烫烫abc(未接收到\0,因此乱码)
printf("%s\n",arr3);//abc
return 0;
}
在计算机语言当中,底层的逻辑是二进制的语言,但我们通过键盘输入的并不是二进制的语言,是因为编译器通过ASCII码表查询了ASCII码值(输入的二进制数字转换为十进制)所对应的字符,从而读取。
完整的ASCII码表的链接参考:ASCII码表完整版
在c语言中,有一些字符通过 \ 的转义序列符,更改了他们原本所有的含义,从而获得了新的功能,这种字符我们称之为转义字符;
转义字符 | 释义 | ||||
\? | 在书写连续多个问号的时候使用,防止被解析为三字母符 | ||||
\" | 用于表示一个字符串内部的双引号 | ||||
\\ | 用于表示一个反斜杠,防止被解析为一个转义序列符 | ||||
\ddd | ddd表示1~3个八进制数字,如:\130(打印结果是这个八进制数字在ASCII对应的结果) | ||||
\xdd | dd表示2个十六进制数字,如:\x30 | ||||
转义字符 | 释义 | 转义字符 | 释义 | 转义字符 | 释义 |
\a | 警告字符,蜂鸣 | \t | 水平制表符 | \v | 垂直制表符 |
\b | 退格符 | \f | 进纸符 | \n | 换行 |
\' | 用于表示字符常量’ | \r | 回车 |
1. 代码中有不需要的代码可以直接删除,也可以注释掉;
2.代码中有些代码对于自己和阅读者来说比较复杂,可以通过注释来解释和记忆;
3.在C语言中有(/*...*/)和(//)两种方式注释;
C语言是结构化的程序设计语言,分为三种结构:顺序结构,选择结构,循环结构;
C语言中的选择结构有:if...else语句;switch语句;
C语言中的循环结构有:while语句、do...while语句、for循环语句;
函数就是为了简化函数,提高代码的复用率。C语言中的将一段实现同一个功能的代码封装成函数,这样在需要这个功能的时候,可以直接函数调用,而无需书写全部过程,提高了代码的复用率。例如:将“求两个函数的和”封装成一个函数Add,这样以后在求两数之和时,可以直接调用Add函数。
int Add(int x,int y){
return x+y;//将x+y作为Add的结果返回出去;也叫返回值;
}
int main(){
int a,b;
scanf("%d %d",&a,&b);//scanf为输入函数,&为取地址符,输入需要,输出不需要。
int c=Add(a,b);//接收到Add的返回值a+b;
printf("%d\n",c);
return 0;
}
数组是一组相同数据类型元素的集合;
创建数组的方式:数据类型 数组名[数组容量]={元素1,元素2,...,元素n};
数组元素的下标:数组在申请元素的时候是连续的,有下标进行指向,数组的下标是从0开始的,也就是说第一个元素的下标是0,以此类推;
int main(){
int arr[]={a,b,c,d,e,f,g};
for(int i=0;i<7;i++}//for(初始条件;循环条件;附加条件){};i++的意思就是i=i+1;
{
printf("%d\n",arr[i];
}
return 0;
}
算术操作符 | +(加) | -(减) | *(乘) | /(除) | %(取模) |
移位操作符 | >>和<< | 左移和右移,和二进制运算有关 | |||
位操作符 | &、|、^ | 按位与、按位或、按位异或 | |||
赋值操作符 | = | -= | += | /= | %= |
&=(类推) | >>=(类推) | i+=1相当于i=i+1 |
int main(){
int a=7/2;//结果为3,因为在C语言中的,当除号的两端都是整数的时候,执行的是整数除法
float b=7/2.0;//只要两端有一个浮点数,就执行浮点数除法;
int c=7%2;//取模;取模操作符的两个操作数只能是整数;
printf("%d\n",a);//结果为3;
printf("%.1f\n",b);//.1f的意思就是保留一位小数,.2f同理;
printf("%d\n",c);//1
return 0;
}
只有一个操作数的操作符叫做单目操作符;
! | 逻辑反操作 | - | 负值 | + | 正值 | & | 取地址符 |
sizeof | 操作数类型大小 | ~ | 对一个数的二进制按位取反 | ||||
++ | 前置、后置++ | -- | 前置、后置-- | ||||
* | 解引用操作符 | (类型) | 强制类型转换 |
int main(){
int a=10;
int b=a++;//先使用,后++;相当于b=a;a=a+1;
int c=++a;//先++,后使用;相当于a=a+1;c=a;
printf("%d %d\n",a,b);//a=11;b=10
printf("%d %d\n",a,c);//a=12;c=12
return 0;
}
(1)强制类型转换的格式:数据类型 变量名=(需要强制转换的类型)值;如果把浮点数进行强制类型转换成整数,则舍弃小数部分;
(2)sizeof是获取操作数的类型大小,可以对操作数使用,也可以对操作数的数据类型使用。如果对数组使用,则可以获取数组的大小。
(3)可以通过sizeof(arr)获取整个数组的大小,通过sizeof(arr[0])获取单个元素的大小,通过sizeof(arr)/sizeof(arr[0])获取数组元素的个数;
> | 大于 | < | 小于 | != | 不等于 |
>= | 大于等于 | <= | 小于等于 | == | 等于 |
(1)逻辑操作符:(&&)逻辑与操作符;(||)逻辑或操作符;
(2)条件操作符:exp1?exp2:exp3——是一个三目操作符,如果表达式为真,则取exp2的值;如果表达式为假,则取exp3的值;
(3)逗号操作符:用于逗号表达式,逗号表达式就是用逗号隔开的一串表达式,特点是从左向右依次计算,整个表达式的结果是最后一个表达式的结果;
关键字是C语言本身内置的,这些关键字都是语言本身预先设定好的,用户是不能自己创建关键字的;
char | short | int | long | float | double |
for | while | do while | break | continue | goto |
switch | case | default | const(常属性) | static(静态的) | enum(枚举) |
extern:声明外部符号 | signed:有符号的 | unsigned:无符号的 | |||
register:寄存器 | struct:结构体 | union:联合体(共用体) | |||
return:函数返回值 | sizeof:计算大小(用%zu打印) | typedef:类型重命名 | |||
void:代表无(函数的返回值,函数参数) |
变量命名的规则:
(1)变量的命名必须有意义(练习除外),比如提到年龄,一般都要用age;
(2)变量命名只能由字母、下划线和数字组成,不能有特殊字符,且不能以数字开头;
(3)变量的命名不能是关键字:但是可以进行大小写变化,比如For就可以;
typedef是类型定义,主要指的是类型的重新定义。比如在定义无符号数时使用unsigned int太过于麻烦,可以使用(typedef unsigned int uint;)来重新定义类型,之后在定义无符号数时就可以直接用uint来定义了。
同理,type也常用于结构体定义,基本格式是:typedef 结构体名{}重定义名。之后在定义结构体时可以使用重定义名来定义了。
作用:修饰变量和函数
(1)静态局部变量——用static关键字修饰局部变量
局部变量在进入函数时候创建,在出函数的时候销毁;使用static关键字对局部变量进行修饰后,在出了作用域的时候不会被销毁;
本质上:一般情况下局部变量的存储位置是在栈区中,用static关键字修饰局部变量后,改变了局部变量的存储位置,放入静态区中,从而起到保护的作用;
(2)静态全局变量——用static关键字修饰的全局变量
全局变量定义后可以在其他源文件中使用,只要用(extern 数据类型 变量名)的方式进行链接就可以了,这得益于全局变量的外部链接属性;
但是用static修饰全局变量的时候,这个全局变量的外部链接属性就变成了内部链接属性,其他源文件就不能再使用这个全局变量了。
(3)静态函数——用static关键字修饰函数
类比于静态全局变量,用static关键字修饰函数时,函数的外部链接属性变成了内部链接属性,其他源文件无法使用这个函数。
register——寄存器关键字——创建寄存器变量——register 数据类型 变量名=值;
只是建议将此变量放入寄存器中,以在大量使用时提高运行速度。(了解即可,无需掌握)
#define MAX 1000 //define定义标识符常量;
#define ADD(x,y) ((x)+(y))//宏名(宏的参数,是无类型的) (宏体)
#include
int main(){
int sum=ADD(2,3);
printf("sum=%d\n",sum);
return 0;
}
define不是一个关键字,而是一个预处理指令
内存是电脑上非常重要的存储器,计算机中程序的运行都是在内存上运行的。为了有效的进行内存的使用,会把内存划分为一个个较小的内存单元,每个内存单元的大小为1个字节。另外,为了有效的进行访问内存单元,会对内存单元进行编号,也就是所谓的内存单元的地址。
计算机对内存单元进行编号之后,对于我们来说内存的编号 = 地址 = 指针;所以当我们创建一个变量用于存放内存单元的地址,也就是指针的时候,我们就把这个变量称之为指针变量。
int main(){
int a=10;//向内存申请4个字节,存储10;
printf("%p\n",&a);// &是取地址操作符;
int* p=&a;//p就是指针变量,int代表的是指针变量p所指的内存中的值的数据类型,*是指针变量的标志
*p=20;// *是解引用操作符,意思是通过p中存放的地址,找到p所指向的对象,*p就是p指向的对象
printf("%d\n",a);//也就是说,*p和a指的是同一个值,在某种意义上来说是等同的。
return 0;
对于指针变量来说,指针变量所存放的地址是那个变量/数组的首地址。
(1)不管是什么类型的指针,都是在创建指针变量,指针变量就是用来存放地址的。
(2)指针变量的大小取决于一个地址存放的时候需要多大的空间;
比如:在32位机器上,存放地址需要32个bit位,也就是4个byte,所以指针变量的大小是4个字节。换到64位机器上,也就是8个字节,即指针变量大小是8个字节。
结构体是C语言中特别重要的知识点,结构体使得C语言有能力描述复杂类型。
对于人来说,需要姓名、性别、年龄等信息来描述一个人的具体信息,而普通的数据类型无法对这一类的集合进行描述,因此C语言就给予了自定义类型的能力,而自定义类型中有一种叫做:结构体,通过关键字struct来实现;
结构体就是把一些单一类型的值组合在一起的行为;
struct Stu//学生
{//成员
char name[20];
int age;
char sex[10];
char tele[12];
};
void print(struct Stu* ps)
{
printf("%s %d %s %s\n", (*ps).name, (*ps).age, (*ps).sex, (*ps).tele);
printf("%s %d %s %s\n", ps->name, ps->age, ps->sex, ps->tele);
//结构体指针变量->成员名
}
int main()
{
struct Stu s = {"张三",20,"男","18133442636"};
printf("%s %d %s %s\n", s.name, s.age, s.sex, s.tele);
print(&s);
return 0;
}