C语言是通用计算机编程语言,广泛用于底层开发。设计目标:提供一种能以简易的方式编译、处理低级存储器产生少量机器码以及不需要任何运行环境支持的编程语言。
![//写代码
//1.写出主函数(main函数)
//C语言是从主函数的第一行开始执行的
//printf-库函数-在屏幕上打印信息
//printf需要引用头文件stdio.h
#include
int main()//int是 int是函数的返回类型,int是整型//main是函数名
{
printf("谦子木\n");//\n是换行
return 0;
}//{}是函数体
//编译+链接+运行代码
//快捷键:Ctrl+F5 或 菜单→调试→开始执行不调试
//如果程序执行太快,看不到窗口
//则:解决方案资源管理器→右键项目→属性→链接器→系统→子系统改成控制台
/*重点*/
//一个工程中可以有多个.c文件,但多个.c文件中只能有一个主函数](https://img-blog.csdnimg.cn/d796f7f30d4a4c10beabea6ac2ca45a3.png)
计算机语言→通过写程序做桥梁→解决生活中的问题
计算机必须有能力描述生活中的问题
举例:商品价格为45.6元,为小数,在C语言中称为“浮点数”
年龄为整数,C语言称为整型
//2.数据类型
int main()
{
//字符型
char ch = 'a';
//整型
int age = 20;
//短整型
short num = 10;
//单精度浮点型
float weight = 55.5;
//双精度浮点型(精度更高)
double d = 0.0;
return 0;
}
printf的输出格式 | 释义 |
---|---|
%d | 按整型实际长度输出(十进制) |
%md | m为指定的输出字段的宽度。若数据的位数 |
%ld | 长整型 |
%u | 无符号十进制输出整型,长整型%lu;指定字段宽度%mu |
%o | 无符号八进制输出整型,长整型%lo;指定字段宽度%mo |
%x | 无符号十六进制输出整型,长整型%lx;指定字段宽度%mx |
%c | 输出一个字符 |
%s | 输出字符串 |
%ms | 字符串占m列,若数据的位数>m,全部输出, |
%-ms | (有-左对齐) |
%m.ns | 输出占m列,但只取字符串左端n个字符。这n个字符右对齐,左补空格 |
%-m.ns | 和上面相比,变成左对齐。若n>m,则自动取n值,保证n个字符正常输出 |
%f | 单浮点型(对应float)。整数部分全输出,小数部分输出6位 |
%lf | 双浮点型(对应double) |
%m.nf(%-m.nf) | 输出占m列,其中n位小数,右对齐(左对齐) |
%e | 指数形式输出 |
bit-比特位;byte-字节-8bit;
计算机为什么要搞这么多类型:因为每种类型所占空间不同,选择合适的类型可以提高空间利用率
生活中的一些数据:
1.常量:不能改变的量
2. 变量:能被改变的量
//创建的一个变量
//类型 变量的名字=20//20是初始值
int age = 20;//也可以不设置初始值,但不推荐这种方法
double weight = 75.3;
age = age + 1;
weight = weight - 10;
printf("age=%d\n", age);//%d对应整型
printf("weight=%lf\n", weight);//%f对应float %lf对应double
return 0;
}
当局部变量和全局变量名字冲突的情况下,局部变量优先
不建议全局和局部变量的名字一样
int a = 10;//全局变量
int main()
{
//局部变量- {}内部定义的
int a = 100;
printf("%d\n", a);
return 0;
}
这个报错推荐用scanf_s函数,但这个函数是vs编译提供的,不是C语言标准规定的,所以不推荐用
变量在哪里起作用,哪里就是它的作用域
局部变量作用域:就是变量所在的局部范围
全局变量的作用域:整个工程
图一将变量声明在输出前面,可以输出;图二将变量声明在变量后面,不可以输出;图三、图四
变量的生命周期:变量的创建和销毁之间的时间段
局部变量的生命周期:进入局部范围生命开始,出局部范围生命结束
全局变量的生命周期:整个程序的生命周期
//1.字面常量(就是直接常量)
//直接常量的书写形式决定了它的类型
int main()
{
3.14;//实数型常量
10;//整型常量
"abcdef";//字符串常量
'a';//字符常量
return 0;
}
字符串是由“双引号”引起来的一串字符
注:字符串的结束标志是 \0,但字符串的长度并不包括\0 在创建字符数组时尤其要注意
转义字符:就是转变了字符原来的意思
转义字符 | 释义 |
---|---|
" | 用于表示一个字符串内部的双引号 |
\ \ | 用于表示一个反斜杠,防止它被解释为一个转义序列符 |
\n | 换行 |
\b | 退格符 |
\r | 回车 |
\t | 水平制表符,相当于按Tab键 |
? | 在书写连续多个?时,防止被解析为三字母词 |
` | 用于表示字符常量` |
\ddd | ddd表示1~3个八进制数字。如\130表示x |
xdd | dd表示2个16进制数字。如、x30表示字符0 |
假如我们要在屏幕上打印一个目录:c:\code\test.c
若将代码写成:
int main()
{
printf("c:\code\test.c");
return 0;
}
实际运行结果为:
如果想让这串目录正常显示,只需要在引起计算机误会的地方加上一个反斜杠\,来防止译为转义字符
int main()
{
printf("c:\\code\\test.c");
return 0;
}
//程序输出什么?
#include
int main()
{
printf("%d\n",strlen("abcdef"));
//\32被解析为一个转义字符
printf("%d\n",strlen("c:\test\328\test.c"));
return 0;
}
//答:输出6和14
//因为\t是一个字符,\32是一个字符,注意\ddd中的d是从1-3,所以不在里面,没有被转义
//是C++注释风格
/**/是C语言风格,有一缺陷是不能嵌套注释,例如以下情况会报错
/*
/*hhh*/
fff
*/
#include
int main()
{
int coding=0;//输入的值
printf("你会去敲代码吗? (选择1 or 0):>");
scanf("%d",&coding);
if(coding==1)//如果输入的值为1
{
printf("坚持,你会有好offer\n");
}
else//如果输入的值为0
{
printf("放弃,回家卖红薯\n");
}
return 0;
}
//30000代码-找个不错工作
int main()
{
int line=0;//定义变量line
while(line<30000)
{
printf("写代码:%d\n",line);//如果line<30000,执行语句,不满足,则退出循环
line++;
}
if(line==30000)
{
printf("好offer\n");
}
return 0;
}
int main()
{
int num1 = 0, num2 = 0, sum = 0;
printf("输入两个操作数:>");
scanf("%d%d", &num1, &num2);
sum = num1 + num2;
printf("sum=%d\n", sum);
return 0;
}
上述代码,写成函数如下:
int add(int x, int y)//创建整型x和y,来接收传递过来的num1和2//add前面的int是返回值的类型
{
int z = 0;
z = x + y;
return z;//将z的值返回给函数add
}
int main()
{
int num1 = 0, num2 = 0;
printf("输入两个操作数:>");
scanf("%d%d", &num1, &num2);
int sum = add(num1, num2);//将num1和2的值传递给x和y,将函数add的值传递给sum
printf("sum=%d\n", sum);
return 0;
}
要存储1-10的数字,怎么存储?
数组:一组相同类型元素的集合
数组定义:
int arr[10]={1,2,3,4,5,6,7,8,9,10};//定义一个整型数组,这个数组最多放10个元素
char ch[5]={'a','b','c'};//字符型数组,不完全初始化,剩余的默认为0
int main()
{
int a=2;//将a存放为整型变量
int b=a<<1;//定义b为a左移一位
return 0;
}
a的值为2,换算成二进制就是10,又因为a是放在整型里,整型是4个字节,一个字节是8个比特位,所以要写出a的二进制序列,要写出32个比特位,即:
00000000 00000000 00000000 00000010
向左移动一位
0 00000000 00000000 00000000 00000100 //注最左端的移出,最右端补了一个0
补完之后就是100,换算成十进制就是4,所以b=4
int main()
{
int a = 1;
a = a + 5;//相当于:
a += 5;
a = a - 3;//相当于:
a -= 3;
a = a % 3;//相当于:
a %= 3;
return 0;
}
什么是双目操作符:比如a+b; +有2个操作数
单目操作符:只有一个操作数
单目操作符 | 作用 |
---|---|
! | 逻辑取反 ,把真变成假,把假变成真;0为假,非0为真 |
- | 负值 |
+ | 正值 |
& | 取地址 |
sizeof | 操作数的类型长度(以字节为单位) |
~ | 对一个数的二进制按位取反 |
– | 前置、后置– |
++ | 前置、后置++ |
* | 间接访问操作符(解引用操作符) |
(类型) | 强制类型转换 |
int main()
{
// !的语法讲解
int a=112;
printf("%d\n",!a);//输出0
int b=0;
printf("%d\n",!b);//输出1
//应用场景
if(a)
{
//如果a为真,做事
}
if(!a)
{
//如果a为假,做事
}
return 0;
}
int main()
{
// + -的讲解
a=-5;//将负五赋值给a
a=+a;//将a的绝对值赋给a
return 0;
}
int main()
{
//sizeof是一个操作符,不是函数
//它是用来计算类型、变量、数组等的大小的(以字节为单位,一个字节8比特)
int a=10;
printf("%d\n",sizeof(int));//打印出4,因为整型是4的字节
printf("%d\n",sizeof(a));//打印出4,因为整型是4的字节
int arr[10]={0};
printf("%d\n",sizeof(arr));//结果是40,因为每个元素是一个int,4*10=40
//计算的是数组的总大小
printf("%d\n",sizeof(arr[0]));//结果是4,因为计算的是元素0的大小
//计算元素的个数:
int sz=sizeof(arr)/sieof(arr[0]);
printf("%d\n",sz);//输出10,因为一共有10个元素
return 0;
}
int main()
{
// ~是按位取反(二进制);把所有二进制中的数字,1→0,0→1(符号位同样对待)
int a=0;
printf("%d\n",~a);//结果为-1
//整数a=0,则二进制:
//00000000 00000000 00000000 00000000 a
// ~ 是按位取反,则二进制变为:
//11111111 11111111 11111111 11111111 ~a(补码)
//printf输出的是原码,即:
//10000000 00000000 00000000 00000001(即十进制是负一)
return 0;
}
一个整数的二进制有3种:原码、反码、补码
-1的原、反、补(以下计算方式只适用于负数)
原码:10000000 00000000 00000000 00000001(最高位叫符号位,1为负数,0为整数)
反码:11111111 11111111 11111111 11111110 (反码就是原码的符号位不变,其余位,按位取反)
补码:11111111 11111111 11111111 11111111 (补码就是反码+1)
正整数原、反、补码相同
int main()
{
int a=10;
int b=++a;//前置++
int c=10;
int d=c++;//后置++
printf("%d\n",b);//输出11
printf("%d\n",a);//输出11
printf("%d\n",c);//输出11
printf("%d\n",d);//输出10
return 0;
}
int main()
{
//怎么消除这个警告
//强制类型转换
int a = (int)3.14;
printf("%d\n", a);//输出3
return 0;
}
关系操作符 | 名称 |
---|---|
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
!= | 不等于 |
== | 等于 |
名称 | 作用 |
---|---|
&& 逻辑与 | 两个都为真,才为真 |
|| 逻辑或 | 两个都为假,才为假 |
int main()
{
int a=3;
int b=5;
int c=a&&b;
printf("%d\n",c);//输出1,因为a为真,b为真,两个都为真,所以c为真
return 0;
}
含义:若exp1成立,则计算exp2,整个表达式的结果是exp2的结果;若exp1不成立,结果为exp3的结果
int main()
{
int a=0,b=3,max=0;
if(a>b)
max=a;
else
max=b;
//上述if语句和下面一行代码相等
max=a>b?a:b;//a>b若成立,将a的值给max;否则将b的值给max
return 0;
}
含义:逗号隔开的一串表达式,会从左往右依次计算,整个表达式的结果取最右侧表达式的结果
int main()
{
int a=0;
int b=3;
int c=5;
//a=5 c=1 b=3
int d=(a=b+2,c=a-4,b=c+2);
printf("%d\n",d);//结果为3
return 0;
}
[ ]是下标引用操作符
//下标引用操作符
int main()
{
int arr[5]={0};
printf("%d\n",arr[5]);
return 0;
}
( )是函数调用操作符
int main()
{
//调用函数的时候,函数名后面的()就是函数调用操作符
printf("hello\n");
return 0;
}
关键字 | 作用 |
---|---|
char | 字符类型 |
double | 双精度 |
float | 单精度浮点型 |
int | 整型 |
long | 长整型 |
short | 短整型 |
signed | 有符号的,如10 -20 |
unsigned | 无符号的 |
void | 空 |
auto | 是自动的意思;每个局部变量都是auto修饰的 |
break | 在 循环 、Switch语句中会用到 |
case | Switch case语句中会用 |
switch | Switch语句 |
const | 常变量 |
continue | 继续 |
default | 默认 ,在Switch case语句中会用 |
do | do while循环会用 |
while | while循环 |
else | if else语句 |
enum | 枚举 |
extern | 声明外部符号的;比如在同一工程下另一个.c文件中声明全局变量或函数 |
for | for循环 |
goto | goto语句 |
if | if语句 |
register | 寄存器关键字,用register创建的变量建议放在寄存器中 |
return | 返回 |
sizeof | 求大小 |
static | 静态的 |
struct | 结构体 |
typedef | 类型重定义别名,可以将类型重新定义成一个新的名字 |
union | 联合体(共用体) |
volatile | c语言中暂时不讲 |
int main()
{
{
int a=10;//是自动创建(进{}),自动销毁的(出{})——自动变量
auto int a=10;//两串代码相同(auto通常省略)
//auto在新的C语言语法中也有其他用法,暂时不考虑
}
return 0;
}
int main()
{
//大量频繁读取的数据,存放在寄存器中,提升效率
register int num=100;//建议num的值存放在寄存器中
return 0;
}
计算机中数据可以存放在哪里:
typedef unsigned int u_int;//将unsigned int定义成u_int,然后这两个就等价了
int main()
{
unsigned int num=100;//相当于
u_int num=100;
return 0;
}
static修饰局部变量时,改变了局部变量的生命周期(本质上改变了比暗恋的存储类型)
全局变量在整个工程中都可以使用
static修饰全局变量,使得这个全局变量只能在自己所在的源文件(.c)内部可以使用,其他源文件不能使用!
全局变量,在其它源文件内部可以使用,是因为全局变量具有外部链接属性,但是被static修饰之后,就变成了内部链接属性,其他源文件就不能连接到这个静态的全局变量了!
static修饰函数,使得函数只能在自己所在的源文件内部使用,不能在其他源文件内部使用
本质上:static是将函数的外部链接属性变成了内部链接属性!(和static修饰全局变量一样)
define是一个预处理指令
#define MAX 1000
int main()
{
printf("%d\n",MAX);//输出1000
return 0;
}
#define ADD(X,Y) X+Y //注意:宏定义没有分号
int main()
{
printf("%d\n",ADD(2,3));//输出5
return 0;
}
#define ADD(X,Y) X+Y //注意:宏定义没有分号
int main()
{
printf("%d\n",4*ADD(2,3));//输出11,而不是20
//4*2+3=11(因为宏是变量的替换)
return 0;
}
怎么让宏定义按要求输出结果?答:加括号
#define ADD(X,Y) ((X)+(Y)) //注意:宏定义没有分号
int main()
{
printf("%d\n",4*ADD(2,3));//输出20
return 0;
}
内存是电脑上的存储器,计算机中所有的程序的运行都是在内存中进行的。
所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。
为了能够有效地访问到内存的每个单元,就给内存进行了编号,这些编号被称为该内存单元的地址。
理解内存:在现实生活中,给每个空间都编了一个有效的地址,就可以精确地找到一个房间;对于内存也是一样的。
电脑有32位和64位,32位就是32位地址线→是物理线→通电→有1、0两种状态
然后电信号转换成数字信号:1和0组成的二进制序列
32位从全0变成全1,会有2^32个地址
那么一个32位的内存单元多大呢:4294967296 bit /8=536870912 byte /1024=524288 kb/1024=512MB /1024=0.5GB
bit比特*8=byte字节 *1024=kb *1024=MB *1024=GB *1024=TB *1024=PB
由于一个比特位是一个内存空间,管理的内存空间太少,因此最终取一个内存单元是一个字节,然后分配地址
int main()
{
int a = 10;//a在内存中要分配空间的--4个字节
printf("%p\n", &a);//%p专门用来打印地址的
//&a是a的地址,地址也是要存放在内存里的
int* pa = &a;//pa是用来存放地址的,在c语言中pa叫指针变量
// *说明pa是指针变量
// int说明pa执行的对象是int类型
char ch = 'w';
char* pc = &ch;
return 0;
}
我们用pa存放a的地址,是为了能找到a,使用a,所以需要解引用来找到a
int main()
{
int a =10;
int* pa=&a;//将a的地址存放在pa变量中
*pa=20;// * 是解引用操作 *pa就是通过pa里面的地址,找到a
printf("%d\n",a);//输出20(借助pa实现对a的操作)
return 0;
}
在这里可以改成x86(32位平台),输出就变成了4
结论:指针的大小在32位平台是4个字节,64位平台是8个字节。
比如描述学生,学生包含:名字+年龄+性别+学号 这几项信息。
这里只能用结构体来描述。
例如:
//结构体
//创建一个学生
struct Stu
{
char name[20];//人名
int age;//年龄
double score;//分数
};
//创建一个书
struct Book
{
char name[20];//书名
float price;//价格
char id[30];//编号
};
int main()
{
struct Stu s = { "张三",20,85.5 };//结构体的创建和初始化
printf("1:%s%d%lf\n", s.name, s.age, s.score);//结构体变量.成员变量
//%lf打印双精度浮点型
struct Stu* ps = &s;//结构体指针类型:*说明ps是指针变量,ps的类型是s的类型
printf("2:%s%d%lf\n", (*ps).name, (*ps).age,(*ps).score);
//ps是s的地址 *ps解引用后就是s 所以(*ps).name相当于s.name
printf("3:%s%d%lf\n", ps->name, ps->age, ps->score);//这三种表达方式等价
//结构体指针->成员变量
return 0;
}