一、基础知识
1 数据类型
一些关键字
2 常量
常量:程序运行时不会变化的量,包括整型、实型、字符和字符串型。
3 变量
变量:与常量相对,程序运行时可以变化的量。
变量名以一个名字代表一个对应的存储单元地址。
4 整型数据
4.1 符号常量
#define PI 3.14
//表示程序内出现的PI都为3.14
4.2 整型变量
int i;
//利用int定义一个变量即为整型变量
5 浮点型数据
e代表10的幂次 eg:300e-3 即为 0.300
//3. 浮点型
//浮点常量(包括小数和指数)
//b = 300e-3; //e的用法,e代表10的幂次
//浮点变量
float b;
printf("b = %f\n", b);
printf("b_size = %d\n", sizeof(b));//float占用4个字节
6 字符型数据
ASCII码表
//4. 字符型常量
// 以“\”开头表示转义字符
// 'A'字符常量以单引号括起来
//
//5. 字符串型常量以双引号括起来
//"helloworld"
//ASCII码
//一个字符型常量占用1字节大小的空间
//输出 %c 时输出字符,若是 %d 输出数字
char c = 'A';
printf("c+32 = %c\n", c + 32);
printf("%d\n", c+32);
7 字符串型常量
8 printf()函数
printf()为输出函数。
8.1 混合运算
强制类型转换
//强制类型转换
int i = 5;
int j = i / 2;
//float k = i / 2;//若这样写输出的是2.00000
//要想输出2.5
float k = float(i) / 2;
printf("j=%d\n", j);
printf("j=%.1f\n", k);
8.2 printf函数
原理是printf先把输出的数据转化为字符串,再放入标准缓冲区,再输出结果。
语法:
printf默认右对齐,%5d说明输出五位字符且右对齐输出,%-5d就是左对齐
//printf函数
int age = 20;
printf("Hello %s,you are %d\n", "Bob", age);
//printf()函数中所有的输出都是右对齐,左对齐要设置
int aa = 2021, bb = 2022;
printf("right_output %5d\nleft__output %-5d\n", aa, bb);
float score = 99.9;
printf("score=%5.2f\n", score);
9 scanf()函数
通过函数库读取标准输入,再通过对应的函数将结果打印到屏幕上。
//scanf函数 一定要取地址&&&&
int ii = 10;
char cc;
//多种数据混合读入,写在同一个scanf中,空格隔开
scanf("%d %c", &ii,&cc);
//scanf("%d", &ii);//若输入一个按回车,则第二个因为是%c就会读取并输出换行
printf("%d\n", ii);
//fflush(stdin); //清空标准输入缓冲区
//如果在这清空的话,第二个scanf是可以被阻塞的,因为清空了缓冲区的‘\n’,所以下一个scanf不会读入
//%c不会忽略任何内容
//这样写第二个scanf会读取上一个回车%c,如果是 %d 可以正常输出
//scanf("%c", &cc);
printf("cc = %d\n", cc);
fflush(stdin);清空标准输入缓冲区
二、运算符和表达式
单目运算符:! 非,因为至于要 ![变量]
双目运算符:[左变量] 运算符 [右变量]
三目运算符:[条件] ? [结果1] : [结果2] 。?理解为条件是否成立,条件成立为结果1,否则为结果2。
1 算术运算符
//算术运算符 + - * / %
int result = 4 + 5 * 2 - 3 / 2 + 10 % 3;
printf("result = %d\n", result);
2 关系运算符
0表示假,1表示真
//关系运算符 > < == >= <= !=
//关系运算符优先级小于算术运算符
int a;
//若想停止while运算输入一个非整数即可,例如一个字符
while (scanf("%d", &a)) {
if (a > 3 && a < 10) {
//如果写 if(3<a<10) 输入5,则先判断前半部分 5>3 为真,为真时是1,为假时是0;
//若输入 -2 ,则 -2<3 为假,为假时是0,0小于10;
printf("a在3~10之间\n");
}
else {
printf("not\n");
}
}
3 逻辑运算符
//逻辑运算符 !非(单目运算符) &&与 ||或
//!的优先级高于算术运算符,&& || 的优先级低于关系运算符
int year;
while (scanf("%d", &year)) { //判断闰年
if ((year % 4 && year % 100 != 0) || (year % 400 == 0)) {
printf("it is a leap year\n");
}
else {
printf("it is not a leap year\n");
}
}
int k = 100;//!从右到左结合,因为!k=0,!!k = 1
int j = !!k;
printf("!k = %d,j = %d", !k,j);
//短路运算
int i = 1;
i&& printf("u can see me!\n");//如果i为真输出,如果i为假不输出
i|| printf("u can see me!\n");//如果i为真不会执行后面的语句
if (i) {
printf("u can see me!\n");
}
4 赋值运算符
复合赋值运算符:+= -= *= /=
//赋值运算符 右边的值赋给左边
int num1 = 1, num = 1, num2 = 100;
num = num + 1;
num += 1;//也有 *= -= /=
num2 /= 30;
printf("%d", num2);
5 求字节运算符
sizeof() 是一个运算符,并非是函数。sizeof()求常量或变量所占空间大小
//求字节数运算符
int i;
printf("i_size=%d", sizeof(i));
6 运算符优先级
三、选择、循环
1 if-else
为了if-else的对应关系更清楚,最好是每个执行语句都使用 { } 括起来
if判断条件为真就执行某个语句,为假不执行或执行else中的语句
if-else可以嵌套使用,也可以同时使用作为多分支语句
//1.if-else //注意{}的使用
int i;
scanf("%d", &i);
//一个if-else
if (i > 0) {
printf("xixi\n");
}
else {
printf("haha\n");
}
//并行使用,多分支语句
int j,cost;
scanf("%d", &j);
if (j > 0 && j < 100)cost = 0.15;
else if (j > 100 && j < 200)cost = 0.5;
else cost = 1;
//嵌套使用,
if (i > 1) {
if (i < 10) {
printf("i>1&&i<10\n");
}
}//若没有这组{},else的字句属于它最近的if,遵循就近原则,但括起来以后下面的else属于第一个if
else
printf("No\n");
2 while循环
“当型”循环结构
语法形式: while(表达式) {语句} ,表达式的值为真时,执行while内嵌语句,为假跳出循环
//2. 循环while
int sum1 = 1, total = 0;
while (sum1 <= 10) {
total += sum1;
sum1++;
}//使用{}括起来,程序更清晰
printf("%d\n", total);
3 for循环
语法: for(表达式1 ; 表达式2 ; 表达式3) {语句} //注意用‘;’隔开
表达式1通常为初值,也可以省略
表达式2通常为终止循环条件
表达式3为运算形式
eg:for(int i=0;i<10;i++) {}
//3.for循环
for (int i = 0; i < 10; i++) {
printf("%d ", i);
}
printf("\n");
4 continue语句
continue语句主要是跳出当前循环,继续下一层循环
//4.continue 终止当前层的循环,继续下一层循环
for (int i = 0; i < 10; i++) {
if (i % 2 == 0)
continue; //偶数跳过,输出的只是奇数
//for循环跳出后去执行了i++,所以可以继续执行
printf("%d ", i);
}
printf("\n");
//while循环使用continue
int sum1 = 1, total = 0;
scanf("%d", &sum1);
while (sum1 <= 10) {
if (sum1 % 2 == 0) {
sum1++;//while中要小心使用continue,如果没有改变sum1的值,直接跳出循环了
continue;//while中如果跳出当前循环说明sum1为偶数,但是若没有对sum1进行修改,一直都是偶数,再次进到if语句,又会跳出,所以循环终止
}
total += sum1;
sum1++;
}
printf("%d\n", total);
5 break语句
break语句是跳出循环
//5.break
int i = 0, j = 100;
for (i = 0,j = 100; i < 10; i++) {
j += 10;
if (j >= 1000) {
break;//跳出整个循环,若执行break,就直接输出i的值
}
}
printf("%d\n", i);
三、数组
1 一维数组
数组实际就是可以进行进行索引的数据类型
数组定义规则:
//1.一维数组
//数组定义
int a[10] = { 0 };//数组范围要表示出来,对数组初始化
以下形式最新C标准支持,但是不建议
//int n;
//scanf("%d", &n);
//int a[n];
//一维数组初始化
//①每个元素赋初值
int aa[5] = { 0,1,2,3,4 };
//不可以这样写
// int aa[5]; aa[5] = { 0,1,2,3,4 };
//②给部分元素初始化,其余默认为0
int b[5] = { 0,1,2 };
//③全部赋值为0
int c[5] = { 0 };
//④若给全部元素赋值,可以不指定数组的长度
int d[] = { 1,2,3,4,5 };
//循环读入数组
int a[5];
for (int j = 0; j < 5; j++)
scanf("%d", &a[j]);//int型加取地址符
for (int k = 0; k < 5; k++)
printf("%d", a[k]);
2 数组访问越界
//2.数组访问越界,会造成数据异常
int arr[5] = { 1,2,3,4,5 };
int i = 10;
int j = 20;
arr[5] = 6;
arr[6] = 7;
printf("%d\n", j);//这里的j输出7(CLion环境)
//因为输入arr[5]以后,数组占用了5个位置,给i和j赋值,此时i的位置是6,j的位置是7
//因此再给arr[5]arr[6]赋值的时候他们两个位置也分别是6,7,相当于arr[6]再第七个位置覆盖了之前的j
3 数组传递
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
//定义一个函数
//数组传递到子函数后,其接受到的数组的起始地址,并不能传递数组长度
void print(int a[],int n) {//传入数组和数组长度
for (int i = 0; i < sizeof(a)/sizeof(int); i++) {//这里不能利用sizeof(a)来得到大小,因为此处的传进来的a是数组名,实际上是指针,这样输出的结果是错误的,可以在主函数获取长度
printf("%d ", a[i]);
}
a[4] = 20;//在子函数中修改数组的值
printf("\n");
}
int main() {
//3.数组传递
//在函数中调用时直接输入数组名
int arr[5] = { 1,2,3,4,5 };
//可以通过sizeof计算数组长度 sizeof(a)/sizeof(int)
int len = sizeof(arr) / sizeof(int);
print(arr, len);//传入的是数组名
printf("%d", arr[4]);
return 0;
}
4 字符数组
存入字符的数组
//4.字符数组
char arr[6] = { 'H','E','L','L','O' };
//
char arr1[6] = "Hello";//如果这样写arr1[5]的话,要写arr1[6],因为后面还有'\0'也要有一个位置
printf("%s\n", arr1);
//%s 读取到'\0'结束。此时若hello后面会输出乱码,因为数组大小并没有预存'\0'的位置,数组大小开大一位给'\0'留位置即可
printf("%s\n", arr);
//在while循环中输出字符数组
int i = 0;
while (arr[i]) {//表明遇到结束符时就停止循环
printf("%c ", arr[i]);
i++;
}
printf("\n");
//scanf读取字符数组,和读取别的的类似
//scanf读取字符串时,会自动往字符数组中放入结束符
char arr2[5];
char arr3[5];
//scanf通过%s读取字符串,%s读取字符串时忽略空格和回车。%c比较特殊
scanf("%s", arr2);//字符数组存储了数组的起始地址,不需要取地址
printf("%s\n", arr2);
scanf("%s", arr3);
printf("%s\n", arr3);
scanf("%s %s", arr2,arr3);//
printf("%s %s\n", arr2,arr3);
5 一些字符函数(str函数)
int main() {
//①gets()函数
char a[20];
//gets可以接受空格
gets_s(a);//放入数组名即可//VS中需要写gets_s
printf("%s\n", a);
//②puts可以直接输出一个字符串,传入字符数组名
puts(a);
//③strlen函数,求字符串长度,实际上就是读取到'\0'时停止
char b[20];
gets_s(b);
int num1 = strlen(b);//头文件为string
int num2 = mylen(b);//自己写的计算数组长度函数
printf("%d %d\n", num1, num2);
//④strcat函数,拼接两个字符串
char a1[20];
char a2[20];
char a3[20];
gets_s(a1);
gets_s(a2);
strcat(a1, a2);// 把a2的拼接到a1后面,相当于扩充a1
puts(a1);
//⑤strcpy函数,copy字符串
strcpy(a2, a1);//表示把a1的字符串复制给a1
puts(a2);
//⑥strcmp函数,比较字典序大小
//输出一个常量,若为0相等,若为-1 后者大于前,若为1 前者大于后者
//是按ascii码比较的
char c[20];
char d[20];
gets_s(c);//he
gets_s(d);//ae
//比如 h的ascii码大于 a的ascii码,就会输出整数
int num3 = strcmp(c, d);
printf("%d\n", num3);
return 0;
}
6 二维数组
假设是数组a[5][5],可以理解成这个数组的每一个元素又是一个数组
即数组的元素还可以是数组,构造出数组的数组
#include <stdio.h>
int main()
{
//①二维数组初始化
//int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };//二维数组的初始化
//int arr[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} }
int arr[3][4] = { 0 };//初始化为0
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
scanf("%d", arr[i][j]);
}
}
int brr[4][3] = {};
//②将arr[3][4]转化成arr[4][3]
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
brr[j][i] = arr[i][j];
}
}
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 3; j++)
{
printf("%d ", brr[i][j]);
}
printf("\n");
}
}
四、函数
1 声明与定义
指子函数,写在主函数外部,可以在主函数中调用,一般用来实现某一个常用的功能。
函数间的调用关系
//
//func.h
#include<stdio.h>
#include<stdlib.h>
int printstar(int i);//函数声明
void print_msg();
//main.c
#define _CRT_SECURE_NO_WARNINGS
#include"func.h"//这里引用自己设置的头文件,用""表示,标准库用<>
int main() {
int a;
scanf("%d", &a);
a = printstar(a);
print_msg();
printstar(a);
return 0;
}
//func.c
#include"func.h"
//函数定义
int printstar(int i) {
printf("***********************\n");
printf("printstar %d\n", i);
return i + 3;
}
void print_msg() {//可以调用printstar
printf("how are u\n");
printstar(3);
}
2 分类与调用
用户角度:
① 标准函数:库函数,比如scanf,printf
②自定义的函数:比如上面的printstar函数形式:
①无参函数:不需要传递参数的函数,比如print_msg
②有参函数:需要传递参数的函数,比如printstar
3 递归调用
递归:函数调用自身的操作,没有结束条件的话会产生死循环
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
//递归 求阶乘
//调用自己
int f(int n) { //上台阶的问题,需要寻找最后的条件,要想到达第n个台阶,要么从n-1,要么从n-2向上走
//②确定结束条件
if (1 == n) {
return 1;
}
else if (n == 2) {
return 2;
}
//最主要是找到一个合适的输出结果和前面一步的关系
return f(n - 1) + f(n - 2);// ①确定与下一个式子的关系
}
int main() {
int n, result;
scanf("%d", &n);
result = f(n);
printf("%d", result);
return 0;
}
4 变量和参数
4.1 全局变量、局部变量
全局变量简单理解就是放在主函数外定义的变量,任何函数都可见。也称作外部变量(定义在函数外部)
局部变量就是一个函数范围内的变量,只在一个函数内可用。也称作内部变量(一个函数内部)
若全局变量和局部变量同名,采取就近原则,即实际获取和修改的就是局部变量的值,因为局部变量比全局变量更近
4.2 实参和形参
实参:调用时传递给函数的参数. 实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值。
形参(parameter):
形参:不是实际存在变量。是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数.在调用函数时,实参将赋值给形参。因而,必须注意实参的个数,类型应与形参一一对应,并且实参必须要有确定的值。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int global_i;//此变量为全局变量
int f(int a) { //这里a是形参,调用时才会分配空间
//②确定结束条件
if (1 == a) {
return 1;
}
else if (a == 2) {
return 2;
}
return f(a - 1) + f(a - 2);// ①确定与下一个式子的关系
}
int main() {
int n, result;//局部变量
scanf("%d", &n);
result = f(n);//此处的n是实参
printf("%d", result);
return 0;
}