大家好呀!👋这个是付青云同学的博客,是一名大一在校生哦!😁😁
目前一直在学习C语言。🐸
写博客是为了来记录我的学习过程,同时也希望通过博客能够帮助到需要帮助的人。
如果我的博客可以帮助到你,不妨给我一个关注哦😁
初识C语言
这里使用的软件是vs2019
头文件调用
#include <stdio.h> — 尖括号里面的东西就是调用方法
main函数
主函数:代码从主函数开始运行
我的第一个代码
#include <stdio.h>
int main()
{
printf("hello world");//printf函数是用来打印的
return 0;
}
#define
定义常量和宏(不是关键字)
1.预处理指令
2.定义宏
#define ADD(X,Y) X+Y
格式输出
%d ——打印一个整数
数据类型
关于有符号的char
取值范围-127~128
转义字符
\ddd:
八进制的三位数字转化为十进制的ASCII码对应的字符 \130=X
\xdd:
十六进制的两位数字转化为十进制的ASCII码对应的字符 \x21=0
关于ASCII码
例:A - 65 B - 66 ‘0’ - 48
每一个字符都有一个值与它对应
size_t=unsigned int //无符号整型
分支和循环语句
所谓语句:就是在c语言中由一个分号(;)隔开的就是一条语句
printf("小付");
3 + 5;
;//空语句
分支语句(选择结构)
if
结构:if(表达式1)
语句1;
else if(表达式2)
语句2;
else
语句3;
注意:
错误写法:
正确写法:
switch()
结构:swicth(表达式)
{
语句项;//case语句(case 整形常量表达式: 语句;)
brake;(出口)
}
case语句
注意:如果没有break的话,程序将会按顺序运行下去
break
用于永久的终止分支或循环
default语句
当全不满足条件时进入此语句
循环语句
while
结构:
int x=0;
while(x)//括号内为循环条件
{
;//循环语句
}
代码示例
#include <stdio.h>
int main()
{
int line = 0;
while(line < 30000)
{
printf("疯狂写代码中...%d\n",line);
line++;
}
if(line == 30000)//注意比较相等要用两个等于号
{
printf("秃发的小付\n");
}
return 0;
}
#include <stdio.h>
int main()
{
int arr[]={ 1, 2, 3, 4, 5, 6, 7, 8, 9};
int i = 0;
while(i<10)
{
printf("%d ", arr[i]);//打印此数组
i++;
}
return 0;
}
利用while清理缓冲区中的多个字符
char password[20]={ 0 };
printf("请输入密码:<");
scanf("%d",password);//因为password为数组,默认引用的就是首元素地址,所以这里不用&
printf("请确认密码:>");
//清理缓冲区
//getchar();处理'\n'
//清理缓冲区多个字符
int tmp = 0;
while((tmp = getchar()) != '\n')
{
;
}
表示只允许输出数字
int ch = 0;
while((ch = getchar()) != EOF)
{
if(ch < '0' || ch > '9')//||表示或者
{
continue;
}
putchar(ch);
}
EOF
end of file
文件结束标志
for
一般形式
for(表达式1; 表达式2; 表达式3)
{
语句块;
}
可多个变量
运行过程
- 先执行“表达式1”。
- 再执行“表达式2”,如果它的值为真(非0),则执行循环体,否则结束循环。
- 执行完循环体后再执行“表达式3”。
- 重复执行步骤 2 和 3,直到“表达式2”的值为假,就结束循环。
“表达式1”仅在第一次循环时执行,以后都不会再执行,可以认为这是一个初始化语句。“表达式2”一般是一个关系表达式,决定了是否还要继续下次循环,称为“循环条件”。“表达式3”很多情况下是一个带有自增或自减操作的表达式,以使循环条件逐渐变得“不成立”。
如图:
do while
语法:
do {
循环语句
} while(表达式);
特点
至少循环一次
goto语句
again:
{
;
}
goto again;
extern声明
函数声明
const
修饰变量,使这个变量为常变量
意思是:不能被修改,但本质上还是变量
例如
const int num = 10;
num=20;//err
const int* p = #//const修饰指针变量的时候:
//const如果放在*的左边,修饰的是*p,表示指针指向的内容,是不能通过指针来改变的
*p = 20;//err
int n = 100;
p = &n;//ok
int* const p = #
//const如果放在*的右边,修饰的是指针变量p,表示指针变量不能改变
//但是指针的内容可以被改变
*p = 20;//ok
int n = 100;
p = &n;//err
//但如果没有被const修饰,则可以通过指针改变num
#define
定义标识符常量
枚举常量
可以一一枚举的常量
操作符
属性
复杂表达式的求值有三个影响的因素:
- 操作符的优先级
- 操作符的结合性
- 是否控制求值顺序
关于操作符的优先级
算数操作符
+
-
*
/
%
移位操作符
>>
右移操作符:
- 算数右移:右边丢弃,左边补原符号位
- 逻辑右移:右移丢弃,左边补0
<<
左移操作符:
- 算数左移:左边丢弃,右边补原符号位
- 逻辑左移:左移丢弃,右边补0
位操作符
&:按位与
对应的二进制位
全1为1,有0为0
|:按位或
有1为1,全0为0
^:按位异或
对应的二进制进行异或
规则:相同为0,相异为1
不使用第三个变量,交换两个变量的值
赋值操作符
= += -= /= *= >>= <<= &= |= ^=
a = x = y + 1;//连续赋值(从左到右,取最后一个值)
单目操作符
!:逻辑反操作符
-
+
&
sizeof()
求操作数的类型长度(以字节为长度)
计算类型或者变量所占空间的大小
返回的是无符号整型
如果变量为负数,会将该变量转换成正数
~
对一个数的二进制位按位取反
++
++a;
先自加,再运算
a++;
先运算,再自加
–
–a;
先自减,再运算
a–;
先运算,再自减
*
间接访问操作符(解引用操作符)
(类型)
强制类型转换
关系操作符
< > <= >= != ==
逻辑操作符
||:或者
如果左边为真,则全为真,后面的语句不用计算
&&:并且
如果左边为假,则全为假,后面的语句不用计算
条件操作符(三目操作符)
exp1 ? exp2 : exp3
int main()
{
int a = 3;
int b = 0;
if(a > 5)
b = 1;
else
b = -1;
//三目操作符
b = (a > 5 ? 1 : -1);
return 0;
}
逗号表达式
exp1, exp2, exp3, …expN
int main()
{
int a = 3;
int b = 5;
int c = 0;
//逗号表达式:要从左到右依次计算,但是整个表达式的结果是最后一个表达式的结果
int d = (c = 5, a = c + 3, b = a - 4, c += 5);
return 0;
}
下标引用操作符
[ ]
操作数:一个数组名+一个引索值
int arr[10];//创建数组
arr[9] = 10;// [] - 就是下标引用操作符
//[ ]的两个操作数是arr和9
函数调用操作符
()
int ret = Add(a,b);//( )为函数调用操作符
//接受一个或者多个操作数:第一个操作数为函数名,剩余的操作数就是传递给函数的参数
结构成员操作符
.
结构体变量.成员变量
->
结构体指针->成员名
//书:书名,书号,定价
//创建一个自定义类型
struct Book
{
//结构体的成员(变量)
char name[20];
char id[20];
int price;
};
int main()
{
int num = 10;
struct Book b = {"C语言","A21110109",55};
struct Book * pb = &b;
//结构体变量.成员名
printf("书名:%s\n",b.name);
printf("书号:%s\n",b.id);
printf("定价:%d\n",b.price);
//更简洁的用法
printf("书名:%s\n",pb->name);
printf("书号:%s\n",pb->id);
printf("定价:%d\n",pb->price);
}
结构体
什么是结构体
结构是一些值的集合,这些值成为成员变量,结构的每个成员可以是不同类型的变量
struct
结构体的声明
结构体的初始化
表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型
隐式类型转换
C的整型算数运算总是以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称之为整型提升
关于整型提升
整型提升是按照变量的数据类型的符号位来提升的
负数的整型提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:11111111
因为 char 为有符号的 char
所以整型提升的时候,高位补充符号位,即为1
提升后的结果为:11111111111111111111111111111111
正数的整型提升
char c2 = -1;
变量c2的二进制位(补码)中只有8个比特位:00000001
因为 char 为有符号的 char
所以整型提升的时候,高位补充符号位,即为0
提升后的结果为:00000000000000000000000000001
无符号整型提升
高位补0
char a = 3;
char b = 127;
char c = a + b;//c=-126
//000000000000000000000000000000000011 - a
//000000000000000000000000000001111111 - b
//000000000000000000000000000100000010 - c
//发现a和b都是char类型的,都没有达到有个int的大小
//这里就会发生整型提升
//111111111111111111111111111100000010 - 补码
//111111111111111111111111111100000011 - 反码
//100000000000000000000000000011111110 - 原码=-126
整型提升的意义
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
通用CPU(general-purposeCPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。
int main()
{
char c = 1;
printf("%u\n",sizeof(c)); //1
printf("%u\n",sizeof(+c));//4
printf("%u\n",sizeof(-c));//4
printf("%u\n",sizeof(!c));//4
//+ -参与运算,所以整型提升了
return 0;
}
%u - 无符号整型
计算
int main()
{
int a = -20;
//10000000000000000000000000010100
//11111111111111111111111111101011
//11111111111111111111111111101100
//11101100
unsigned int b = 10;
//00000000000000000000000000001010 - 原反补相同
printf("%d\n", a + b);
//11111111111111111111111111101100
//00000000000000000000000000001010
//11111111111111111111111111110110 - 补码
//11111111111111111111111111110101 - 反码
//10000000000000000000000000001010 - 原码=-10
return 0;
}
算数转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换位另一个操作数的类型,否则操作就无法进行。
寻常算数转换
略…
函数
库函数
引头文件
参照文档,学习库函数
库函数查询网站
strcpy(string copy)
复制字符串
memset(memory set)
将缓冲区设置位指定字符
scanf()
输入函数
括号里面表示要输入的格式
scanf_s — 此函数为VS编译所提供的,不是C语言标准规定
注意
在vc2010之后,使用此函数将会警告不安全,若要取消警告则要在代码开头加上:
#define _CRT_SECURE_NO_WARNINGS
Sleep()
单位ms
需要引头文件 #include <windows.h>
sqrt()
开平方函数
引用头文件math.h
pow()
次方函数
strcmp
- 比较两个字符串
- 引用头文件 <string.h>
- strcmp(s1,s2)是字符串比较函数,比较规则是俩个字符串自左向右逐个字符相比(按ASCII码值大小),以第一个不相同的大小作为比较结果。当s1等于s2时,返回0;当s1大于s2时,为正整数;当s1小于s2时,为负整数。
strlen()
引头文件string.h
求字符串长度
此字符长度为14,\t为一个,\32为一个,本当\328为一个,但8不在八进制内
system()
执行系统命令
system(“cls”)
清空屏幕
关机命令
shutdown -s -t 60
取消关机:shutdown -a
void menu
菜单
getchar()
读取一个字符—从键盘获取字符
当读取失败的输出EOF
gets()
读取一行的字符
putchar()
输出一个字符串
assert()
引头文件<assert.h>
断言函数
如果不满足条件,则停止运行并报错
isalpha()
判断一个字符是不是字母
如果是返回非零,反之返回0
printf()
- 注:printf 中的双引号和后面的分号必须是在英文输入法下。双引号内的字符串可以是英文,也可以是中文。
- 此函数的返回值是打印在屏幕上的字符的个数
printf("%d",printf("%d",printf("%d",43)));//4321
- printf(“输出控制符”,输出参数);
这句话的意思是将变量 i 以十进制输出。那么现在有一个问题:i 本身就是十进制,为什么还要将 i 以十进制输出呢?因为程序中虽然写的是 i=10,但是在内存中并不是将 10 这个十进制数存放进去,而是将 10 的二进制代码存放进去了。计算机只能执行二进制 0、1 代码,而 0、1 代码本身并没有什么实际的含义,它可以表示任何类型的数据。所以输出的时候要强调是以哪种进制形式输出。所以就必须要有“输出控制符”,以告诉操作系统应该怎样解读二进制数据。 - printf
(“输出控制符1 输出控制符2…”, 输出参数1, 输出参数2, …);
随机数函数
rand
生成一个随机数
范围:0~7ffff(32707)
time()
时间戳
srand((unsigned int)time(NULL))//真随机数
自定义函数
形式参数
函数中的指针变量变量参数
实际参数
函数实际的变量参数
利用函数求两个整数的最大值
函数的调用
传值调用
函数的形参和实参分别占有不同的内存块,对形参的修改不会影响实参
意思就是:如果只是引用函数外部的值,就是传值调用
传址调用
- 传址调用是把函数外部创造变量的内存地址传递给参数的一种调用函数的方式
- 这种传参方式可以让函数外边的变量建立起真的联系,也就是函数内部可以直接操作函数外部的变量
元素个数一定要在自定义函数外求出 - 数组传参,实际传递的不是数组本身,仅仅是传过去数组首元素的地址
函数的嵌套调用和链式访问
嵌套调用
链式访问
把一个函数的返回值作为一个函数的参数
printf("%d",printf("%d",printf("%d",43)));
//输出结果为:4321
函数的声明和定义
函数的声明一般放在头文件中
函数递归
程序调用自身的编程技巧称为递归( recursion) 。递归做为一种算法在程序设计语言中广泛应用。一个过程或函数在其定 义或说明中有直接或间接调用自身的一-种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可
描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
递归的主要思考方式在于:把大事化小
递归的两个必要条件
- 存在限制条件,当满足这个限制条件时,不再继续
- 每次调用递归之后越来越接近这个限制条件
注意:
在写递归代码的时候:
- 不能死递归,都要有跳出条件,每次递归逼近跳出条件
- 递归层次不能太深
数组
一组相同类型元素的集合
分类
整型数组
字符数组
一维数组
内存
- 一维数组在内存上是连续存放的
- 随着下标增长,地址由高到低变化
二维数组
创建
初始化
创建时赋值
初始化时,行可以省略,列则不能
注意!
二维数组的数组名表示首元素的地址
而二维数组首元素的地址是:第一行!
内存
二维数组在内存中也是连续的
数组作为函数参数
数组名是数组首元素地址
但是有两个例外:
- sizeof(数组名)-- 数组名表示整个数组 – 计算的整个数组的大小单位是字节
- &数组名 – 数组名表示整个数组 – 取出的是整个数组的地址
指针
先到这里吧,码不动了.
后面的内容在下一篇