讲解两个小点:scanf和scanf_s的区别,字符串输入
scanf是C语言的标准输入输出,scanf_s是微软为C语言提供的安全增强版本
两种用法:
#include <stdio.h> int main() { // 1. 读取数值类型(int/float/double) int num; printf("输入一个整数:"); scanf("%d", &num); // 数值类型需传变量地址(&) printf("你输入的整数是:%d\n", num); // 2. 读取字符串(风险点:无长度限制,输入过长会溢出) char str[10]; printf("输入一个字符串(scanf):"); scanf("%s", str); // 字符串无需&,数组名本身是地址 printf("scanf读取的字符串:%s\n", str); return 0; }#include <stdio.h> #include <stdlib.h> // 可选,用于处理错误码 int main() { // 1. 读取数值类型(和scanf完全一致) int num; printf("输入一个整数:"); scanf_s("%d", &num); printf("你输入的整数是:%d\n", num); // 2. 读取字符串(必须传「数组长度」作为额外参数) char str[10]; printf("输入一个字符串(scanf_s):"); // 格式:scanf_s("%s", 字符串变量, 字符串长度); int ret = scanf_s("%s", str, (unsigned)_countof(str)); // _countof获取数组长度 if (ret != 1) { // 检查是否读取成功 printf("输入错误/字符串过长!\n"); exit(1); } printf("scanf_s读取的字符串:%s\n", str); // 3. 读取单个字符(也需传长度,固定为1) char ch; printf("输入一个字符:"); scanf_s("%c", &ch, 1); // 字符必须传长度1 printf("你输入的字符是:%c\n", ch); return 0; }字符串输入:
c语言中原本没有字符串类型,字=字符串是以\0结尾的字符数组,本质上都是用户输入的字符输入到字符数组中,并自动补充\0的(或者是手动补充)
基础的字符串输入方式scanf
#include <stdio.h> int main() { // 定义字符数组,长度要留1位给 '\0' char str[50]; printf("请输入一个单词(无空格):"); // 注意:str 前面不需要加 &,因为数组名本身就是地址 scanf("%s", str); printf("你输入的内容是:%s\n", str); return 0; }当输入的是hello输出的是hello
而当输入hello world的时候输出的还是hello。
读取含空格的字符串fgets();
#include <stdio.h> int main() { char str[50]; printf("请输入一句话(可含空格):"); // fgets(字符数组, 最大读取长度, 输入源) // stdin 表示从控制台输入 fgets(str, sizeof(str), stdin); printf("你输入的内容是:%s", str); return 0; }这时输入hello world的时候就输出的是hello world
fgets函数会将用户输入的\n也读取进来,比如(hello world+回车)数组中就是hello world\n\0
#include <stdio.h> int main() { int age; // 存储年龄(数字) char hobby[50]; // 存储爱好(字符串,可含空格) // 1. 先用scanf读取数字 printf("请输入你的年龄:"); scanf("%d", &age); // 输入数字后按回车,回车符会留在输入缓冲区 // 2. 再用fgets读取字符串 printf("请输入你的爱好(可含空格):"); fgets(hobby, sizeof(hobby), stdin); // 直接读取到缓冲区里的回车符,不会等你输入 // 输出结果 printf("你的年龄:%d\n", age); printf("你的爱好:%s", hobby); // 这里会直接输出空行,因为hobby里只有回车符 return 0; }输入年龄20 后再次输入爱好games;
打印的结果是20 和空
为什么会有这样的结果呢?
当我们输入20的时候会将回车也输入进去fgets会将缓冲区中的\n读取,不会等待输入爱好,所以hobby输出的是空行,解决这个问题的方法就是在scanf后面加上getchar();将换行符读取了。
getchar();这个函数会读取缓冲区中的一个字符。
int a=10; const int b=20; int arr[a]{0}; int arr[b]{0};在上面的代码中,当我们在c下编译的时候都编译不通过,原因是在c中将const变量也视为变量,不能作为数组的长度。而在c++中可以int arr[b]{0}是可以编译通过的,将他视为常量处理,编译的时候,直接将b替换成20来处理。
int n1 = 5; const int n2 = 10; int arr[n2]{ 0 }; //c++修改const变量时,必须进行转换 int* n2p = const_cast<int*>(&n2); *n2p = 100; cout << n2; cout << *n2p;const和指针:
int main() { int a=10,b=20; int *p1=&a; const int *p2=&b; int *const p3=&a; const int * const p4=&a; }int main() { // 1. 定义普通整型变量,a=10,b=20,都有可读写权限 int a=10,b=20; // 2. 普通指针 p1:指向变量a的地址 int *p1=&a; /* 特性: - 无const修饰,p1本身可修改(能指向其他地址),指向的内容也可修改 - 合法操作:p1=&b; *p1=30; - 核心:权限最大,可改指针指向、可改指向的内容 */ // 3. const修饰「指向的内容」的指针 p2:指向变量b的地址 const int *p2=&b; /* 特性: - const在*前 → 修饰「指针指向的内容」(即*p2),内容只读、指针可改 - 合法操作:p2=&a; (指针换指向) - 非法操作:*p2=30; (不能通过p2修改b的值,编译器报“只读内容”错) - 核心:仅限制“修改内容”,不限制“换指向” */ // 4. const修饰「指针本身」的指针 p3:指向变量a的地址 int *const p3=&a; /* 特性: - const在*后 → 修饰「指针p3本身」,指针只读、内容可改 - 必须初始化(这里=&a),否则编译器报错(只读变量无法后续赋值) - 合法操作:*p3=40; (修改a的值) - 非法操作:p3=&b; (不能换指向,编译器报“只读指针”错) - 核心:仅限制“换指向”,不限制“修改内容” */ // 5. const同时修饰「内容+指针」的指针 p4:指向变量a的地址 const int * const p4=&a; /* 特性: - const在*前 + *后 → 双重只读:内容只读、指针也只读 - 必须初始化(这里=&a),否则编译器报错 - 合法操作:仅能读取 → printf("%d", *p4); - 非法操作:*p4=50; (改内容)、p4=&b; (换指向),两者都报错 - 核心:权限最小,只能读,不能改任何东西 */ }总结:const 在 * 前 → 内容不可改;const 在 * 后 → 指针不可改。