C语言基本概念
1、主函数(main)
每个 C 语⾔程序不管有多少⾏代码,都是从 main 函数开始执⾏的, main 函数是程序的⼊⼝,main 函数也被叫做:主函数 (每个程序中主函数有且只能有一个);main 前面的 int 表示main函数执行结束的时候返回一个整型类型的值。
2、数据类型
内置类型
//1.字符类型
char [signed] char unsigned char
//2.整型类型
//短整型
short [int] [signed] short [int] unsigned short [int]
//整型
int [signed] int unsigned int
//⻓整型
long [int] [signed] long [int] unsigned long [int]
//更⻓的整型
long long [int] [signed] long long [int] unsigned long long [int]
//3.浮点型
float double long double
//4.bool(布尔)类型
_Bool
_Bool类型使用方法
C 语⾔原来并没有为布尔值单独设置⼀个类型,⽽是使⽤整数 0 表⽰假,⾮零值表⽰真。
布尔类型的使⽤得包含头⽂件 stdbool.h 布尔类型变量的取值是:true或者false.
演示代码
#include<stdio.h>
#include<stdbool.h>
void false1()
{
_Bool test = false;//为假不打印
if (test)
printf("world\n");
}
int main()
{
// true表示为真;false表示为假;为真则打印,为假则不打印
_Bool flag = true;//为真打印
if (flag)
printf("hello CSDN\n");
false1();
return 0;
}
运行结果
3、signed 和 unsigned
C 语⾔使⽤ signed 和 unsigned 关键字修饰字符型
和整型类型
的。
signed 关键字,表⽰⼀个类型带有正负号,包含负值;
unsigned 关键字,表⽰该类型不带有正负号,只能表⽰零和正整数。
对于 int
类型,默认是带有正负号的,也就是说 int 等同于 signed int 。由于这是默认情况,关键字 signed ⼀般都省略不写。
signed int a; // 等同于int a;
int 类型也可以不带正负号
,只表⽰⾮负整数。这时就必须使⽤关键字 unsigned 声明变量。
unsigned int a; // 表示非负整数类型
16位的 signed short int 的取值范围是:-32768 ~ 32767,最⼤是32767;⽽unsigned short int 的取值范围是:0 ~ 65535;
32位的 signed int 的取值范围-2,147,483,648 ~ 2,147,483,647;unsigned short int的取值范围是:0 ~ 4,294,967,295。
下⾯的定义是编译器VS2022环境中,limits.h中相关定义。
#define SHRT_MIN (-32768) //有符号16位整型的最⼩值
#define SHRT_MAX 32767 //有符号16位整型的最⼤值
#define USHRT_MAX 0xffff //⽆符号16位整型的最⼤值
#define INT_MIN (-2147483647 - 1) //有符号整型的最⼩值
#define INT_MAX 2147483647 //有符号整型的最⼤值
unsigned int ⾥⾯的 int 可以省略,所以上⾯的变量声明也可以写成下⾯这样。
unsigned a;
字符类型 char 也可以设置 signed 和 unsigned 。
注意:
C 语⾔规定 char 类型默认是否带有正负号,由当前系统决定。char 不等同于 signed char ,它有可能是 signed char (带有正负号),也有可能是unsigned char (不带有正负号)。
signed char c; // 范围为 -128 到 127
unsigned char c; // 范围为 0 到 255
4、数据类型的取值范围
limits.h ⽂件中说明了整型类型的取值范围。
float.h这个头⽂件中说明浮点型类型的取值范围。
整数类型 | 详情 |
---|---|
SCHAR_MIN , SCHAR_MAX | signed char 的最⼩值和最⼤值。 |
SHRT_MIN , SHRT_MAX | short 的最⼩值和最⼤值。 |
INT_MIN , INT_MAX | int 的最⼩值和最⼤值。 |
LONG_MIN , LONG_MAX | long 的最⼩值和最⼤值。 |
LLONG_MIN , LLONG_MAX | long long 的最⼩值和最⼤值。 |
UCHAR_MAX | unsigned char 的最⼤值。 |
USHRT_MAX | unsigned short 的最⼤值。 |
UINT_MAX | unsigned int 的最⼤值。 |
ULONG_MAX | unsigned long 的最⼤值。 |
ULLONG_MAX | unsigned long long 的最⼤值。 |
5、变量
① 变量的创建
类型它是⽤来创建变量的。什么是变量呢?C语⾔中把经常变化的值称为变量
,不变的值称为常量
。
变量创建的语法形式是这样的:
//变量的组成方式
data_type name;
| |
| |
数据类型 变量名
int age; //整型变量
char ch; //字符变量
double weight; //浮点型变量
变量在创建的时候就给⼀个初始值,就叫初始化
。
int age = 18;
char ch = 'w';
double weight = 48.0;
unsigned int height = 100;
② 变量的分类
1.全局变量:在⼤括号外部定义
的变量就是全局变量
全局变量的使⽤范围更⼴,整个⼯程中想使⽤,都是有办法使⽤的。
2.局部变量:在⼤括号内部定义
的变量就是局部变量
局部变量的使⽤范围是⽐较局限,只能在⾃⼰所在的局部范围内使⽤的。
演示代码
#include <stdio.h>
int global = 2024;//全局变量
int main()
{
int local = 2023;//局部变量
printf("%d ——> ", local);
printf("%d\n", global);
return 0;
}
运行结果
如果局部和全局变量,名字相同呢?
演示代码
#include <stdio.h>
int local = 2024;//全局变量
int main()
{
int local = 2023;//局部变量
printf("%d ——> ", local);
printf("%d\n", local);
return 0;
}
运行结果
(局部变量和全局变量同名的时候,局部变量优先使⽤)
6、算术操作符:+、-、*、/、%
在写代码时候,⼀定会涉及到计算。
注:
操作符也被叫做:运算符,是不同的翻译,意思是⼀样的。
Ⅰ、操作符 + 和 -
+ 和 - ⽤来完成加法和减法。
+ 和 - 都是有2个操作数的,位于操作符两端
的就是它们的操作数,这种操作符也叫双⽬操作符
。
演示代码
#include <stdio.h>
int main()
{
int x = 4 + 22;
int y = 61 - 23;
printf("%d\n", x);
printf("%d\n", y);
return 0;
}
Ⅱ、操作符 *
运算符 * ⽤来完成乘法。
演示代码
#include <stdio.h>
int main()
{
int num = 5;
printf("%d\n", num * num); // 输出 25
return 0;
}
Ⅲ、操作符 /
运算符 / ⽤来完成除法。
除号的两端如果是整数,执⾏的是整数除法,得到的结果也是整数。
演示代码
#include <stdio.h>
int main()
{
float x = 6 / 4;
int y = 6 / 4;
printf("%f\n", x); // 输出 1.000000
printf("%d\n", y); // 输出 1
return 0;
}
演示中,尽管变量 x 的类型是 float (浮点数),但是 6 / 4 得到的结果是 1.0 ,⽽不是1.5 。
原因就在于 C 语⾔⾥⾯的整数除法是整除,只会返回整数部分,丢弃⼩数部分
。
如果希望得到浮点数的结果,两个运算数必须⾄少有⼀个浮点数
,这时 C 语⾔就会进⾏浮点数除法。
演示代码
#include <stdio.h>
int main()
{
float x = 6.0 / 4; // 或者写成 6 / 4.0
printf("%f\n", x); // 输出 1.500000
return 0;
}
上⾯运用浮点整除法, 6.0 / 4 得到的结果就是 1.5 。
特例:
#include <stdio.h>
int main()
{
int score = 5;
score = (score / 20) * 100;
printf("%d", score);// 案例结果为0
return 0;
}
上⾯的代码,你可能觉得经过运算, score 会等于 25 ,但是实际上 score 等于 0 。这是因为score / 20 是整除,会得到⼀个整数值 0 ,所以乘以 100 后得到的也是 0 。
为了得到预想的结果,可以将除数 20 改成 20.0 ,让整除变成浮点数除法。
修改:
#include <stdio.h>
int main()
{
int score = 5;
score = (score / 20.0) * 100;
printf("%d", score);// 案例结果为25
return 0;
}
Ⅳ、操作符%
运算符 % 表⽰求模运算,即返回两个整数相除的余值。这个运算符只能⽤于整数,不能⽤于浮点数。
演示代码
#include <stdio.h>
int main()
{
int x = 6 % 4; // 2 6 除 4 还剩下的数,就为 2;
return 0;
}
负数求模的规则是,结果的正负号由第⼀个运算数的正负号决定。
演示代码
下⾯⽰例中,第⼀个运算数的正负号( 11 或 -11 )决定了结果的正负号。
#include <stdio.h>
int main()
{
printf("%d\n", 11 % -5); // 1
printf("%d\n",-11 % -5); // -1
printf("%d\n",-11 % 5); // -1
return 0;
}
7、赋值操作符:= 和 复合赋值
在变量创建的时候给⼀个初始值叫初始化,在变量创建好后,再给⼀个值,这叫赋值。
int a = 100;//初始化
a = 200; //赋值,这⾥使⽤的就是赋值操作符
赋值操作符 “ = ” 是⼀个随时可以给变量赋值的操作符。
⑴ 连续赋值
赋值操作符也可以连续赋值,例:
int a = 3;
int b = 5;
int c = 0;
c = b = a+3;//连续赋值,从右向左依次赋值的 a = 3 ; b = 6; c = 6
C语⾔虽然⽀持连续赋值,但是写出的代码不容易理解,建议拆开书写,这样⽅便观察代码的执⾏细节。
下列代码的写作方式,在调试的是,每⼀次赋值的细节都是可以很⽅便的观察的。
⑵ 复合赋值
在写代码时,我们经常可能对⼀个数进⾏⾃增、⾃减的操作,如下代码:
int a = 10;
a = a+3; // 此时为 13
a = a-2; // 此时为 11
//同时也能这样书写
int a = 10;
a += 3;
a -= 2;
C语⾔中提供了复合赋值符,⽅便我们编写代码,这些赋值符有:
加减复合赋值符 | + = - = |
---|---|
乘除复合赋值符 | * = /= %= |
位移复合赋值符 | >>= <<= |
按位复合赋值符 | &= (按位与操作符) |= (按位或操作符) ^= (按位异或操作符) |
8、单⽬操作符:++、–、+、-
⑴ ++和–
++是⼀种⾃增的操作符,⼜分为前置++和后置++,- - 是⼀种⾃减的操作符,也分为前置- -和后置- -。
Ⅰ、前置++
计算⼝诀:先+1,后使⽤;
演示代码
#include<stdio.h>
int main()
{
int a = 10;
int b = ++a;//++的操作数是a,是放在a的前⾯的,就是前置++
printf("a=%d b=%d\n",a , b);// a = 11 ; b = 11
return 0;
}
a原来是10,先+1,后a变成了11,再使⽤就是赋值给b,b得到的也是11,所以计算技术后,a和b都是11,相当于以下代码:
演示代码
#include<stdio.h>
int main()
{
int a = 10;
a = a+1;
b = a;
printf("a=%d b=%d\n",a , b);
return 0;
}
Ⅱ、后置++
计算⼝诀:先使⽤,后+1
演示代码
#include<stdio.h>
int main()
{
int a = 10;
int b = a++;//++的操作数是a,是放在a的后⾯的,就是后置++
printf("a=%d b=%d\n",a , b);// a = 11 ; b = 10
return 0;
}
a原来是10,先使⽤,就是先赋值给b,b得到了10,然后再+1,然后a变成了11,所以直接结束后a是11,b是10,相当于这样的代码:
演示代码
#include<stdio.h>
int main()
{
int a = 10;
int b = a;
a = a+1;
printf("a=%d b=%d\n",a , b);
return 0;
}
Ⅲ、前置- -(与前置++类似)
计算⼝诀:先-1,后使⽤
演示代码
#include<stdio.h>
int main()
{
int a = 10;
int b = --a;//--的操作数是a,是放在a的前⾯的,就是前置--
printf("a=%d b=%d\n",a , b);// a = 9 ; b = 9
}
a原来是10,先-1,后a变成了9,再使⽤就是赋值给b,b得到的也是9,所以计算技术后,a和b都是9,相当于以下代码:
演示代码
#include<stdio.h>
int main()
{
int a = 10;
a = a-1;
b = a;
printf("a=%d b=%d\n",a , b);
return 0;
}
Ⅳ、后置- -(与后置++类似)
计算⼝诀:先使⽤,后-1
演示代码
#include<stdio.h>
int main()
{
int a = 10;
int b = a--;//--的操作数是a,是放在a的后⾯的,就是后置--
printf("a=%d b=%d\n",a , b);// a = 9 ; b = 10
return 0;
}
a原来是10,先使⽤,就是先赋值给b,b得到了10,然后再-1,然后a变成了9,所以直接结束后a是9,b是10,相当于这样的代码:
演示代码
#include<stdio.h>
int main()
{
int a = 10;
int b = a;
a = a-1;
printf("a = %d b = %d\n",a , b);
return 0;
}
⑵ +和-
这⾥的+是正号,-是负号,都是单⽬操作符
。
运算符 + 对正负值没有影响,是⼀个完全可以省略的运算符,但是写了也不会报错。
#include<stdio.h>
int main()
{
int a = +10; //等价于 int a = 10;
printf("a = %d \n",a);
return 0;
}
运算符 - ⽤来改变⼀个值的正负号,负数的前⾯加上 - 就会得到正数,正数的前⾯加上 - 会得到负数。(简单的数学计算法则)
#include<stdio.h>
int main()
{
int a = 10;
int b = -a;
int c = -10;
printf("b = %d c = %d\n", b, c);//这⾥的 b = c = -10
int a = -10;
int b = -a;
printf("b = %d\n", b); //这里的 b = 10
return 0;
}
9、强制类型转换
在操作符中还有⼀种特殊的操作符是强制类型转换,表示如下:
类型 a = (类型) 常量
演示代码
int main()
{
int a = 3.14;//这样编译会报错,因为 a 是整型,但3.14是一个double类型的双精度浮点数,类型不一;
return 0;
}
为了消除报错,这里进行强制转换
int main()
{
int a = (int) 3.14;//意思是将3.14强制类型转换为 int 类型,这种 强制类型转换 只取整数部分
return 0;
}
10、语句和语句分类
Ⅰ、空语句
空语句是最简单的,⼀个分号就是⼀条语句,同时也是空语句。一般是函数需要一条语句,但是该语句不会有任何动作才会使用空语句。
演示代码
#include<stdio.h>
int main()
{
;// 这就被称为空语句
return 0;
}
运行结果
(因为是空语句,所以无内容显示)
Ⅱ、表达式语句
表达式语句就是在表达式的后边加上分号。
演示代码
#include<stdio.h>
int main()
{
printf("hello world\n");
printf("hello CSDN");// 被称为表达式语句
return 0;
}
运行结果
Ⅲ、函数调⽤语句
函数调⽤的时候,也会加上分号,就是函数调⽤语句。
演示代码
#include <stdio.h>
void print() //结构体后续会做介绍
{
printf("CSDN\n");
return;
}
int sum(x,y)
{
return x + y;
}
int main()
{
printf("hello ");
print(); // 函数调用语句
int math = sum(3, 5); //参数调用语句
printf("3+5=%d",math);
return 0;
}
运行结果
Ⅳ、复合语句
复合语句其实就是前⾯讲过的代码块,成对括号中的代码就构成⼀个代码块,也被称为复合语句。
演示代码
#include <stdio.h>
#include <stdio.h>
void print(int arr[], int sz) //函数的⼤括号中的代码也构成复合语句
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int i = 0;
int arr[10] = { 0 };
for (i = 0; i < 10; i++) //for循环的循环体的⼤括号中的就是复合语句
{
arr[i] = 10 - i;
printf("%d ", arr[i]);
}
printf("\n");
print(arr, 10);
return 0;
}
运行结果
Ⅴ、控制语句
控制语句⽤于控制程序的执⾏流程,以实现程序的各种结构⽅式(C语⾔⽀持三种结构:顺序结构、选
择结构、循环结构),它们由特定的语句定义符组成,C语⾔有九种控制语句。可分成以下三类:
- 条件判断语句也叫分⽀语句:if语句、switch语句;
- 循环执⾏语句:do while语句、while语句、for语句;
- 转向语句:break语句、goto语句、continue语句、return语句。
11、printf和scanf;输出函数 / 输入函数的介绍
⑴ printf(输出函数)
它的功能是在标准输出设备(⼀般指屏幕)上进⾏信息的打印;同时 puts 函数也能做到输出的效果,但 puts 函数不具备转换说明的功能。所以只能用于文字说明,puts 函数语段末尾自动会加上 换行符 /n。
下面展示一些 printf函数 / puts 函数的输出案例
。
演示代码
#include <stdio.h>
int main()
{
// printf 函数
printf("hello world\n");
//printf() 不会在⾏尾⾃动添加换⾏符,运⾏结束后,光标就停留在输出结束的地⽅,不会⾃动换⾏。
//所以可以添加 “\n” 让它进行换行操作。为了让光标移到下⼀⾏的开头。
printf("56 + 44 的整数和为:%d\n", 56 + 44);
// puts 函数
puts("hello world"/*puts 函数默认字符串末尾有换行符 /n*/);
puts("56 + 44的整数和为:%d",/*puts 函数不具备转换说明的功能*/ 56 + 44);
return 0;
}
运行结果
如果⽂本内部有换⾏,也是通过插⼊换⾏符来实现,如下列代码:
演示代码
#include <stdio.h>
int main(void)
{
printf("Hello\nCSDN\n");
printf("\n");
printf("Hello\n");
printf("CSDN\n");
return 0;
}
运行结果
注:
printf() 是在标准库的头⽂件 stdio.h 定义的。使⽤这个函数之前,必须在源码⽂件头部引⼊这个头⽂件。
① 占位符
printf() 可以在输出⽂本中指定占位符;所谓 “占位符”,就是这个位置可以⽤其他值代⼊。
演示代码
#include <stdio.h>
int main()
{
printf("The is %d year\n", 2024);// 输出 The is 2024 year
return 0;
}
上⾯⽰例中,The is %d year 是输出⽂本,⾥⾯的 %d 就是占位符,表⽰这个位置要⽤其他值来替换。占位符的第⼀个字符⼀律为百分号 % ,第⼆个字符表⽰占位符的类型, %d 表⽰这⾥代⼊的值必须是⼀个整数。
printf() 的第⼆个参数就是替换占位符的值,上⾯的例⼦是整数 2024 替换 %d 。执⾏后的输出结果就是 The is 2024 year。
常⽤的占位符除了 %d ,还有 %s 表⽰代⼊的是字符串。
演示代码
#include <stdio.h>
int main()
{
printf("%s to CSDN\n", "welcome");
return 0;
}
上⾯⽰例中, %s 表⽰代⼊的是⼀个字符串,所以 printf() 的第⼆个参数就必须是字符串,这个例⼦是 welcome 。执⾏后的输出就是 welcome to SCDN 。
同时输出⽂本⾥⾯可以使⽤多个占位符。
演示代码
#include <stdio.h>
int main()
{
printf("%s year is %d\n", "This", 2024);
return 0;
}
上⾯⽰例中,输出⽂本 %s year is %d 有两个占位符,第⼀个是字符串占位符 %s ,第⼆个是整数占位符 %d ,分别对应 printf() 的第⼆个参数( This )和第三个参数( 2024 )。执⾏后的输出就是 This year is 2024 。
printf() 参数与占位符是⼀⼀对应关系,如果有 n 个占位符, printf() 的参数就应该有 n + 1 个。如果参数个数少于对应的占位符, printf() 可能会输出内存中的任意值
。
② 占位符列举
printf() 的占位符有许多种类,与 C 语⾔的数据类型相对应。下⾯按照字⺟顺序,列出了一些常⽤的占位符。
占位符 | 含义 |
---|---|
%a | ⼗六进制浮点数,字⺟输出为⼩写。 |
%A | ⼗六进制浮点数,字⺟输出为⼤写。 |
%c | 字符。 |
%d | ⼗进制整数。 |
%e | 使⽤科学计数法的浮点数,指数部分的 e 为⼩写。 |
%E | 使⽤科学计数法的浮点数,指数部分的 E 为大写。 |
%i | 整数,基本等同于 %d 。 |
%f | ⼩数(包含 float 类型和 double 类型)。 |
%g | 6个有效数字的浮点数。整数部分⼀旦超过6位,就会⾃动转为科学计数法,指数部分的 e为⼩写。 |
%G | 6个有效数字的浮点数。整数部分⼀旦超过6位,就会⾃动转为科学计数法,指数部分的 E为大写。 |
%hd | ⼗进制 short int 类型。 |
%ho | ⼋进制 short int 类型。 |
%hx | ⼗六进制 short int 类型。 |
%hu | unsigned short int 类型。 |
%ld | ⼗进制 long int 类型。 |
%lo | ⼋进制 long int 类型。 |
%lx | ⼗六进制 long int 类型。 |
%lu | unsigned long int 类型。 |
%lld | ⼗进制 long long int 类型。 |
%llo | ⼋进制 long long int 类型。 |
%llx | ⼗六进制 long long int 类型。 |
%llu | unsigned long long int 类型。 |
%Le | 科学计数法表⽰的 long double 类型浮点数。 |
%Lf | long double 类型浮点数。 |
%n | 已输出的字符串数量。该占位符本⾝不输出,只将值存储在指定变量之中。 |
%o | ⼋进制整数。 |
%p | 指针。 |
%s | 字符串。 |
%u | ⽆符号整数(unsigned int)。 |
%x | ⼗六进制整数。 |
%zd | size_t 类型。 |
%% | 输出⼀个百分号。 |
③ 输出格式
printf() 可以定制占位符的输出格式。
Ⅰ、限定宽度
printf() 允许限定占位符的最⼩宽度。
演示代码
#include <stdio.h>
int main()
{
printf("%5d\n", 123); // 输出为 " 123";输出内容 123 的前⾯添加了空格。
printf("%-5d\n", 123); // 输出为 "123 ";输出内容 123 的后⾯添加了空格。
return 0;
}
上⾯⽰例中, %5d 表⽰这个占位符的宽度⾄少为5位。如果不满5位,对应的值的前⾯会添加空格。输出的值默认是右对⻬
,即输出内容前⾯会有空格;如果希望改成左对⻬,在输出内容后⾯添加空格,可以在占位符的 % 的后⾯插⼊⼀个 - 号。
对于⼩数,这个限定符会限制所有数字的最⼩显⽰宽度。
演示代码
#include <stdio.h>
int main()
{
printf("%12f\n", 123.45);// 输出 " 123.450000"
return 0;
}
上⾯⽰例中, %12f 表⽰输出的浮点数最少要占据12位。由于⼩数的默认显⽰精度是⼩数点后6位
,所以 123.45 输出结果的头部会添加2个空格。
Ⅱ、总是显⽰正负号
默认情况下, printf() 不对正数显⽰ + 号,只对负数显⽰ - 号。如果想让正数也输出 + 号,可以在占位符的 % 后⾯加⼀个 + 。
演示代码
#include <stdio.h>
int main()
{
printf("%+d\n", 12); // 输出 +12
printf("%+d\n", -12); // 输出 -12
return 0;
}
上⾯⽰例中, %+d 可以确保输出的数值,总是带有正负号。
Ⅲ、限定⼩数位数
输出⼩数时,有时希望限定⼩数的位数。希望⼩数点后⾯只保留两位,占位符可以写成 %.2f 。
演示代码
#include <stdio.h>
int main()
{
printf("Number is %.2f\n", 0.5);// 输出 Number is 0.50
//如果希望⼩数点后⾯输出3位( 0.500 ),占位符就要写成 %.3f 。
return 0;
}
这种写法可以与限定宽度占位符,结合使⽤。
演示代码
#include <stdio.h>
int main()
{
printf("%6.2f\n", 0.5);// 输出为 " 0.50"
//%6.2f 表⽰输出字符串最⼩宽度为6,⼩数位数为2。所以,输出字符串的头部有两个空格。
return 0;
}
最⼩宽度和⼩数位数这两个限定值,都可以⽤ * 代替,通过 printf() 的参数传⼊。
演示代码
#include <stdio.h>
int main()
{
printf("%*.*f\n", 6, 2, 0.5);//%*.*f 的两个星号通过 printf() 的两个参数 6 和 2 传⼊。
// 等同于printf("%6.2f\n", 0.5);
return 0;
}
Ⅳ、输出部分字符串
%s 占位符⽤来输出字符串,默认是全部输出。如果只想输出开头的部分,可以⽤ %.[m]s
指定输出的⻓度,其中 [m] 代表⼀个数字,表⽰所要输出的⻓度。
演示代码
#include <stdio.h>
int main()
{
printf("%.5s\n", "hello world");
//占位符 %.5s 表⽰只输出字符串“hello world”的前5个字符,即“hello”。
return 0;
}
⑵ scanf(输入函数)
当我们有了变量,我们需要给变量输⼊值就可以使⽤ scanf 函数,如果需要将变量的值输出在屏幕上的时候可以使⽤ prinf 函数,下⾯看⼀个例⼦:
演示代码
#include <stdio.h>
int main()
{
int score = 0;
printf("请输入成绩:");
scanf("%d", &score);
printf("成绩是:%d\n", score);
return 0;
}
运行结果
④ scanf基本⽤法
- scanf() 函数⽤于读取⽤⼾的键盘输⼊。
- 程序运⾏到这个语句时,会停下来,等待⽤⼾从键盘输⼊。
- ⽤⼾输⼊数据、按下回⻋键后, scanf() 就会处理⽤⼾的输⼊,将其存⼊变量。
- 它的原型定义在头⽂件
stdio.h
。 - scanf() 的语法跟 printf() 类似。
scanf("%d", &i); //可看出printf与scanf的相似性
上⾯⽰例中, scanf() 的第⼀个参数 %d ,表⽰⽤⼾输⼊的应该是⼀个整数。 %d 就是⼀个占位符, % 是占位符的标志, d 表⽰整数。第⼆个参数 &i 表⽰,将⽤⼾从键盘输⼊的整数存⼊变量i 。
I. 它的第⼀个参数是⼀个格式字符串,⾥⾯会放置占位符(与 printf() 的占位符基本⼀致),告诉编译器如何解读⽤⼾的输⼊,需要提取的数据是什么类型。
II. 这是因为 C 语⾔的数据都是有类型的, scanf() 必须提前知道⽤⼾输⼊的数据类型,才能处理数据。
III. 它的其余参数就是存放⽤⼾输⼊的变量,格式字符串⾥⾯有多少个占位符,就有多少个变量。
注意:
变量前⾯必须加上 & 运算符(指针变量除外),因为 scanf() 传递的不是值,⽽是地址,即将变量 i 的地址指向⽤⼾输⼊的值。
如果这⾥的变量是指针变量(⽐如字符串变量),那就不⽤加 & 运算符。
以下是⼀次将键盘输⼊读⼊多个变量的例⼦。
scanf("%d%d%f%f", &i, &j, &x, &y);
- 上⾯⽰例中,格式字符串 %d %d %f %f ,表⽰⽤⼾输⼊的前两个是整数,后两个是浮点数,⽐如 1;-20;3.4;-4.0e3 。这四个值依次放⼊ i 、 j 、 x 、 y 四个变量。
- scanf() 处理数值占位符时,会⾃动过滤空⽩字符,包括空格、制表符、换⾏符等。
- 所以,⽤⼾输⼊的数据之间,有⼀个或多个空格不影响 scanf() 解读数据。另外,⽤⼾使⽤回⻋键,将输⼊分成⼏⾏,也不影响解读。
输入的两种情况
//连续输入
1 -20 3.4 -4.0e3 //连续输入中间得用空格隔开(建议使用连续输入)
//分行输入
1
-20
3.4
-4.0e3 //分行输入,输入一次回车一次
//⽤⼾分成四⾏输⼊,得到的结果与连续输⼊是完全⼀样的。每次按下回⻋键以后,scanf() 就会开始解读,
//如果第⼀⾏匹配第⼀个占位符,那么下次按下回⻋键时,就会从第⼆个占位符开始解读。
scanf() 处理⽤⼾输⼊的原理是,⽤⼾的输⼊先放⼊缓存,等到按下回⻋键后,按照占位符对缓存进⾏解读。
解读⽤⼾输⼊时,会从上⼀次解读遗留的第⼀个字符开始,直到读完缓存,或者遇到第⼀个不符合条件的字符为⽌。
#include <stdio.h>
int main()
{
int x;
float y;
//模拟⽤⼾输⼊ " -13.45e12# 0"
scanf("%d", &x);
scanf("%f", &y);
printf("%d %f",x,y);
return 0;
}
上⾯⽰例中, scanf() 读取⽤⼾输⼊时, %d 占位符会忽略起⾸的空格,从 - 处开始获取数据,读取到 -13 停下来,因为后⾯的 " . "
不属于整数的有效字符。这就是说,占位符 %d 会读到 -13 。
第⼆次调⽤ scanf() 时,就会从上⼀次停⽌解读的地⽅,继续往下读取。这⼀次读取的⾸字符是 . ,由于对应的占位符是 %f ,会读取到 .45e12 ,这是采⽤科学计数法的浮点数格式。
后⾯的#
不属于浮点数的有效字符,所以会停在这⾥。
由于 scanf() 可以连续处理多个占位符,所以也能像下列一样书写
#include <stdio.h>
int main()
{
int x;
float y;
// ⽤⼾输⼊ " -13.45e12# 0"
scanf("%d%f", &x, &y);
return 0;
}
⑤ scanf的返回值
scanf() 的返回值是⼀个整数,表⽰成功读取的变量个数。
如果没有读取任何项,或者匹配失败,则返回 0 。如果在成功读取任何数据之前,发⽣了读取错误或者遇到读取到⽂件结尾,则返回常量 EOF。
演示代码
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
float f = 0.0f;
int r = scanf("%d %d %f", &a, &b, &f);//用来统计输入个数
printf("a=%d b=%d f=%f\n", a, b, f);
printf("r = %d\n", r);
return 0;
}
模拟输入,输出测试
正常输入数据,输出的 r 表示正确读值的个数为 3。
输入两次数据,输出的 r 表示正确读值的个数为 2;同时按下ctrl + Z
能够终止一次输入。
输入0次数据,输出的 r 表示个数为 -1 (相当于返回EOF)。
⑥ 占位符
scanf() 常⽤的占位符如下,与 printf() 的占位符基本⼀致。
占位符 | 含义 |
---|---|
%c | 字符。 |
%d | 整数。 |
%f | float 类型浮点数。 |
%lf | double 类型浮点数。 |
%Lf | long double 类型浮点数。 |
%s | 字符串。 |
%[ ] | 在⽅括号中指定⼀组匹配的字符(⽐如 %[0-9] ),遇到不在集合之中的字符,匹配将会停⽌。 |
- 上⾯所有占位符之中,除了 %c 以外,都会⾃动忽略起⾸的空⽩字符。
%c 不忽略空⽩字符
,总是返回当前第⼀个字符,⽆论该字符是否为空格。 - 如果要强制跳过字符前的空⽩字符,
可以写成 scanf(" %c", &ch) ,即 %c 前加上⼀个空格
,表⽰跳过零个或多个空⽩字符。 - 下⾯要特别说⼀下占位符 %s ,它其实不能简单地等同于字符串。它的规则是,从当前第⼀个⾮空⽩字符开始读起,直到遇到空⽩字符(即空格、换⾏符、制表符等)为⽌。因为 %s 不会包含空⽩字符,所以⽆法⽤来读取多个单词,除⾮多个 %s ⼀起使⽤。这也意味着,scanf() 不适合读取可能包含空格的字符串,⽐如书名或歌曲名。另外, scanf() 遇到 %s 占位符,会在字符串变量末尾存储⼀个
空字符 \0
。 - scanf() 将字符串读⼊字符数组时,
不会检测字符串是否超过了数组⻓度
。所以,储存字符串时,很可能会超过数组的边界,导致预想不到的结果。为了防⽌这种情况,使⽤ %s 占位符时,应该指定读⼊字符串的最⻓⻓度,即写成 %[m]s ,其中的 [m] 是⼀个整数,表⽰读取字符串的最⼤⻓度,后⾯的字符将被丢弃。
演示代码
#include <stdio.h>
int main()
{
char name[11];
scanf("%10s", name);
//name 是⼀个⻓度为11的字符数组, scanf() 的占位符 %10s 表⽰最多读取⽤⼾输⼊的10个字符;
//后⾯的字符将被丢弃,这样就不会有数组溢出的⻛险了。
return 0;
}
⑦ 赋值忽略符
有时,⽤⼾的输⼊可能不符合预定的格式。
演示代码
#include <stdio.h>
int main()
{
int year = 0;
int month = 0;
int day = 0;
scanf("%d-%d-%d", &year, &month, &day);
//如果⽤⼾输⼊ 2020-01-01 ,就会正确解读出年、⽉、⽇。
//问题是⽤⼾可能输⼊其他格式,⽐如 2020/01/01 ,这种情况下, scanf() 解析数据就会失败。
return 0;
}
为了避免这种情况, scanf() 提供了⼀个赋值忽略符"(assignment suppression character) *
"。只要把*
加在任何占位符的百分号后⾯,该占位符就不会返回值,解析后将被丢弃。
演示代码
#include <stdio.h>
int main()
{
int year = 0;
int month = 0;
int day = 0;
scanf("%d%*c%d%*c%d", &year, &month, &day);
//%*c 就是在占位符的百分号后⾯,加⼊了赋值忽略符 * ;
//表⽰这个占位符没有对应的变量,解读后不必返回。
return 0;
}
⑶ 2022 VS Studio编译器使用 scanf 函数
VS Studio编译器不建议我们使用scanf函数
,建议我们使用它们编译器提供的scanf_s
函数来进行使用
若是想使用
scanf
函数,VS Studio编译器也提供了解决方案;在代码第一行写入#define _CRT_SECURE_NO_WARNINGS 1
(必须写在代码第一行)
进行#define _CRT_SECURE_NO_WARNINGS 1
宏定义后就能发现scanf函数
能够正常使用了
12、转义字符
前⾯的代码中你看到 \n 后续代码中看到 \0 时,很纳闷那是啥。其实在字符中有⼀组特殊的字符是转义字符;
转义字符顾名思义:转变原来的意思的字符。
⽐如:我们有字符 n ,在字符串中打印的时候⾃然能打印出这个字符
#include <stdio.h>
int main()
{
printf("abcndef");
return 0;
}
运行结果
然后我们试试 n 前面加上 \
#include <stdio.h>
int main()
{
printf("abc\ndef");
return 0;
}
运行结果
我们可以看到修改的前后代码输出的结果,截然不同的,这就是转义字符的用处;
\n 是⼀个转义字符表⽰换⾏的意思, n 本来是⼀个普通的字符,被 \ 转义为换⾏的意思。
C语言学习中还有以下转义字符
转义字符 | 解释 |
---|---|
\? | 在书写连续多个问号时使⽤,防⽌他们被解析成三字⺟词,在新的编译器上没法验证了。(可作省略) |
\’ | ⽤于表⽰字符常量 ’ 。 |
\" | ⽤于表⽰⼀个字符串内部的双引号 " 。 |
\\ | ⽤于表⽰⼀个反斜杠,防⽌它被解释为⼀个转义序列符。 |
\a | 警报,这会使得终端发出警报声或出现闪烁,或者两者同时发⽣。 |
\b | 退格键,光标回退⼀个字符,但不删除字符。 |
\f | 换⻚符,光标移到下⼀⻚。在现代系统上,这已经反映不出来了,⾏为改成类似于 \v。 |
\n | 换⾏符。 |
\r | 回⻋符,光标移到同⼀⾏的开头。 |
\t | 制表符,光标移到下⼀个⽔平制表位,通常是下⼀个8的倍数。 |
\v | 垂直分隔符,光标移到下⼀个垂直制表位,通常是下⼀⾏的同⼀列。 |
\ddd | ddd表⽰1~3个⼋进制的数字。 如: \130 表⽰字符X。 |
\xdd | dd表⽰2个⼗六进制数字。 如: \x30 表⽰字符0。 |
\0 | null 字符,代表没有内容, \0 就是 \ddd 这(⼋进制)类转义字符的⼀种,⽤于字符串的结束标志,其ASCII码值是0. |
演示代码
#include <stdio.h>
int main()
{
printf("%c\n", '\'');
printf("%s\n", "\"");
printf("c:\\come\\back\\test.c\n");
printf("\a");
printf("%c\n", '\130'); //130是8进制,转换成10进制是88,以88作为ASCII码值的字符是
printf("%c\n", '\x30'); //x30中的30是16进制,转换成10进制是48,以48作为ASCII码值的
printf("%c\n", '\0');
return 0;
}
13、ASCII 编码
打印字符展示
#include <stdio.h>
int main()
{
int i = 0;
for (i = 32; i <= 127; i++)
{
printf("%c ", i);
if (i % 16 == 15)/*可以这么理解 i % 16==0,但是空格这个字符在顶行显示后就换行了,空格展示不明显;
所以 i % 16 == 16-1(这里可以把16理解为0,向0的左边移动了一个位置这样空格字符就能展示出来了)*/
printf("\n");
}
return 0;
}
运行结果
我们学习与工作不需要记住所有的ASCII码表中的数字,使⽤时查看就可以,不过我们最好能掌握⼏组特殊的数据:
- 字符 A~Z 的 ASCII 码值从 65~90
- 字符 a~z 的 ASCII 码值从 97~122
- 对应的⼤⼩写字符( a 和 A )的 ASCII 码值的差值是32
- 数字字符0~9的ASCII码值从 48~57
- 换⾏ \n 的 ASCII 值是:10
- 在这些字符中 ASCII 码值从 0~31 这32个字符是不可打印字符,⽆法打印在屏幕上观察
14、字符串 和 \0
在C语言中使用双引号括起来的一串字符就被称为字符串,例如:“abcdefghi”,这就被称为字符串。
字符串的打印格式使用 %s 来进行指定
#include <stdio.h>
int main()
{
printf("%s\n", "hello C");
printf("hello c");
return 0;
}
运行结果
/0 是字符串的结束标志所以我们在使⽤库函数 printf() 打印字符串或者strlen() 计算字符串⻓度的时候,遇到 \0 的时候就⾃动停⽌了。
使用C语言代码验证⼀下 \0 的功能
#include <stdio.h>
int main()
{
char arr1[] = { 'a', 'b', 'c' };//arr1数组中存放3个字符
char arr2[] = "abc"; //arr2数组中存放字符串
printf("%s\n", arr1);
printf("%s\n", arr2);
return 0;
}
调试截图
可以看到 arr2 数组中写入了“ \0 ”,arr1 数组中并没有写入“ \0 ”
运行结果截图
由此可见字符串的结束标志是“ \0 ”,因为arr1数组中并没有“ \0 ”为结束标志,它就会沿地址往下查找找到“ \0 ”就结束 ;
arr1 数组后续的结果是因为往下查找到的数组地址的二进制而打印出来的结果。
arr1数组加上“ \0 ” 在进行演示
#include <stdio.h>
int main()
{
char arr1[] = { 'a', 'b', 'c','\0' };//arr1数组中存放3个字符
char arr2[] = "abc"; //arr2数组中存放字符串
printf("%s\n", arr1);
printf("%s\n", arr2);
return 0;
}
运行结果截图
15、C语言中的代码注释方式
注释是对代码的说明,编译器会忽略注释,注释对实际代码没有影响。注释是给程序员⾃⼰,或者其他程序员看的。
- 整行代码注释
// 的注释形式:从双斜杠到⾏尾都属于注释。这种注释只能是单⾏,可以放在⾏⾸,也可以放在⼀⾏语句的结尾。
代码演示
#include<stdio.h>
int main()
{
// 这是⼀⾏注释
int x = 1; // 这也是注释
return 0;
}
不管是哪⼀种注释,都不能放在双引号⾥⾯。双引号⾥⾯的注释符号,会成为字符串的⼀部分,解释为普通符号,失去注释作⽤。
代码演示
#include<stdio.h>
int main()
{
printf("// hello /* world */ ");
}
- 代码块注释,/**/ 的形式
这种⽅法是将注释放在/*...*/
之间,内部可以分⾏。
代码演示
#include<stdio.h>
int main()
{
/* 注释 */
/*
这是⼀⾏注释
*/
}
这种注释可以插在⾏内。
代码演示
#include<stdio.h>
int main()
{
int fopen(char* s /* file name */, int mode);
}
上⾯⽰例中, /* file name */
⽤来对函数参数进⾏说明,跟在它后⾯的代码依然会有效执⾏。
这种注释⼀定不能忘记写结束符号 */ ,否则很容易导致错误。
代码演示
#include<stdio.h>
int main()
{
printf("a "); /* 注释⼀
printf("b ");
printf("c "); /* 注释⼆ */
printf("d ");
}
第⼀⾏和第三⾏代码的尾部,有两个注释。但是,第⼀⾏注释忘记写结束符号,导致注释⼀延续到第三⾏结束。
/**/ 的这个注释也不⽀持嵌套注释, /*
开始注释后,遇到第⼀个 */
就认为注释结束了。
代码演示
#include<stdio.h>
int main()
{
/*
printf("a ");
printf("b ");
printf("c "); /* 注释⼆ */
printf("d ");
*/
}
注:
编译时,注释会被替换成⼀个空格,所以 min/* 这⾥是注释*/Value 会变成 min Value ,⽽不是 minValue 。
16、关键字介绍
C语⾔中有⼀批保留名字的符号,⽐如: int 、 if 、 return ,这些符号被称为保留字或者关键字。
⾃⼰在创建标识符的时候是不能和关键字重复的,关键字也是不能⾃⼰创建的。
下面展示一些 32个C语言中常见的关键字
。
auto break case char const continue default do double
else enum ex float for goto if int long
register return short signed sizeof struct switch typedef union
unsigned void volatile while
注:
在C99标准中加⼊了 inline 、 restrict 、 _Bool 、 _Comploex 、 _Imaginary 等关键字。