C遇到的问题
C++遇到的问题
关于while(cin»n»m)的读取问题
原题是利用顺序表和链表实现两个多项式的相加
在读取时:
我写的代码是
int n,m;
while (cin >> n>>m) //n是系数,m是指数
{
a[m] = n;
}
while (cin >> n>>m)
{
b[m] = n;
}
这样是错误的,会一直让你输入字符,即使点了空格键也没用
原因是:
cin在输入时会自动跳过空格和换行符。 第一个多项式的输入是3 4 2 3 1 2,第二个多项式是-2 3 4 2 5 1 6 0。当用户使用第一个循环读取时,第一个循环会读取3 4,然后2 3,然后1 2,然后继续读下去,也就是说,原代码中的第一个循环会一直读取所有输入的两个整数,直到输入结束。后面的输入可能还在输入流中。
解决方法:用string输入+用sstream库
int main() {
string line1, line2;
getline(cin, line1); // 读取第一个多项式
getline(cin, line2); // 读取第二个多项式
// 处理第一个多项式
stringstream ss1(line1);
int coeff, exp;
while (ss1 >> coeff >> exp) {
a[exp] = coeff; // 直接赋值,输入中每个指数唯一
}
// 处理第二个多项式
stringstream ss2(line2);
while (ss2 >> coeff >> exp) {
b[exp] = coeff;
}
stringstream
是 C++ 标准库
<sstream>
头文件中提供的一个类模板,
它主要用于字符串和其他数据类型之间的相互转换,以及对字符串进行格式化操作,类似于标准输入输出流
cin
和
cout
,只不过它操作的对象是字符串而不是控制台或文件。
stringstream
有三种类型:
istringstream
(字符串读取数据)ostringstream
(向字符串写入数据)stringstream
(读+写)
当你有一个包含多个数据项的字符串,并且这些数据项之间有特定的分隔符(如空格、逗号等),可以使用
stringstream
来将字符串分割成多个独立的数据项,并将其转换为合适的数据类型。
stringstream使用示例:
字符串读写(用到istringstream):
类比cin吧,但还是有点抽象了
int main() {
std::stringstream ss;
// 写入数据
//这个写入数据的操作和std::stringstream ss input("10 20")没啥区别
ss << 10 << " " << 20;
// 读取数据
int num1, num2;
//cin>>num1是从键盘中读取数据并存储在num1中
//ss是从内部存储的字符串中读取数据并存储在num1中,很相似吧
//提取到的字符串类型会被转换成num1的类型
ss >> num1 >> num2;
std::cout << "num1: " << num1 << ", num2: " << num2 << std::endl;
return 0;
}
具体原理是指针:
- 执行
iss >> num1
时,从字符串的起始位置开始提取连续的数字字符"123"
,将其转换为整数123
并存储到num1
中,此时内部指针移动到空格字符处。 - 执行
iss >> num2
时,从空格字符后的位置开始提取连续的数字字符"456"
,将其转换为整数456
并存储到num2
中。
字符串分割与解析:
这里使用stringstream,因为要分割字符串,既有字符串的输入,也有字符串的输出
#include <iostream>
#include <sstream>
#include <string>
int main() {
std::string input = "10 20 30";
//创建一个 stringstream 对象 ss,并将包含三个整数的字符串 input 作为其初始化内容。
std::stringstream ss(input);
int num1, num2, num3;
//使用 >> 操作符从 ss 中依次提取数据,并将其存储到 num1、num2 和 num3 中
//就像使用 cin 从标准输入读取数据一样。
ss >> num1 >> num2 >> num3;
std::cout << "num1: " << num1 << ", num2: " << num2 << ", num3: " << num3 << std::endl;
return 0;
}
将其他数据类型(如整数、浮点数等)转换为字符串(以整数为例):
这里使用ostringstream,因为要将整数转换成字符串,输出字符串,所以用ostringstream
#include <iostream>
#include <sstream>
#include <string>
int main() {
//创建一个 ostringstream 对象 oss,它专门用于向字符串写入数据
int number = 123;
std::ostringstream oss;
//使用 << 操作符将整数 number 写入 oss 中。
oss << number;
//调用 oss.str() 方法获取 oss 中存储的字符串内容
std::string str = oss.str();
std::cout << "Converted string: " << str << std::endl;
return 0;
}
将字符串转换为整数
这里用istringstream,因为要从字符串中读取数据,将其转换成整数
#include <iostream>
#include <sstream>
#include <string>
int main() {
std::string str = "456";
std::istringstream iss(str);
int number;
iss >> number;
std::cout << "Converted number: " << number << std::endl;
return 0;
}
一直头疼字符串和数字相互转化的问题,还有怎么解决未知长度输入的问题,今日一见茅厕顿开
老师上课讲的输入输出切换的方法
从文件1.txt中读取一行整数,将这些整数累加求和,最后输出结果
这里也是遇到读取未知字符长度的问题
//在使用一些较新版本的 Visual Studio 编译器时,对于部分传统的 C 标准库函数(如 scanf、strcpy、fopen 等),
//编译器会发出安全警告。这是因为这些函数在使用时可能存在缓冲区溢出等安全风险
//编译器会给出相应的警告信息
//这里define作用是告诉编译器忽略这些安全警告
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
int main()
{
//使用freopen在文件和控制台之间切换输入
FILE* instream;
//从文件D:\\1.txt中输入数据,读取数据用r
//如果是要写入数据就用w
instream = freopen("1.txt","r", stdin);
int a, sum = 0;
while (1)
{
if (scanf("%d", &a) != 1) return 0; //如果读取失败,则结束
sum += a;
//原先的代码是if (char c = getchar() == '\n') break;
//==的优先级高于=,实际执行顺序是先判断输入字符是否是\n,然后返回0或者1
//所以正确的应该是下面这个
if ((char c = getchar()) == '\n') break;
}
printf("%d", sum);//也可以输出到文件2.txt,如果没有2.txt则会现场建一个
fclose(instream);
return 0;
}
对于FILE instream*
在 C 语言中,
FILE
是一个预定义的结构体类型,它用于表示文件流。
文件流是程序与文件之间进行数据传输的通道,通过文件流,程序可以对文件进行读取、写入等操作
这里声明了一个名为
instream
的指针变量,其类型为
FILE*
。这个指针变量用于指向一个
FILE
类型的对象,也就是指向一个文件流。通过这个指针对相应的文件进行各种操作。
文件流小示例:
基本流程就是打开文件然后输出文件中的字符
#include <stdio.h>
int main() {
FILE* instream; // 声明一个 FILE 类型的指针
instream = fopen("example.txt", "r"); // 打开一个文件,将文件流指针赋值给 instream
if (instream != NULL) {
char ch;
while ((ch = fgetc(instream)) != EOF) {
putchar(ch); // 从文件中读取字符并输出
}
fclose(instream); // 关闭文件流
} else {
printf("无法打开文件\n");
}
return 0;
}
fgetc(instream):从文件流中读取字符,直到文件结束(EOF)
获取数组实际长度
问题:cin.getline(a, 10000); cin.getline(b, 10000);以后怎么得知a和b的实际长度
此时a[]和b[]为字符串
- strlen()
- a.size()
- a.length()
若int a[]
- length=sizeof(a)/4
- 使用vector代替数组,可以直接调用size函数获取其实际长度
vector<int> c = {1, 2, 3, 4, 5}; int length = c.size();
关于指针赋值和&的问题
写链表的时候遇到两行这样的代码:
node是struct类型
同样都是指针=struct,为什么第一个需要用&取地址,而第二个不用呢
node index(0, 0);
node* tail = &index;
node* newnode = new node(a, b);
第一个
node index(0, 0)
:这行代码在
栈上
创建了一个
node
类型的对象
index,
栈上的对象由系统自动管理内存,当对象所在的作用域结束时,系统会自动释放该对象的内存。
第二个
new node(a, b)
:
new
是 C++ 中用于动态内存分配的运算符,它在
堆上
为
node
类型的对象分配内存,并调用构造函数
node(a, b)
对该对象进行初始化。堆上的内存需要程序员手动管理,使用
delete
运算符来释放。
node* newnode = ...
:
new
运算符返回的就是新分配对象的内存地址
,所以可以直接将
new
的返回值赋给指针
newnode
,不需要再使用取地址运算符
&
。
报错大合集
‘nullptr’在此作用域中尚未声明
/tmp/compiler_65pf_i0l/src:11:24: 警告:identifier ‘nullptr’ is a keyword in C++11 [-Wc++11-compat] 11 | next = nullptr; | ^~~~~~~ /tmp/compiler_65pf_i0l/src: In constructor ‘node::node(int)’: /tmp/compiler_65pf_i0l/src:11:24: 错误:‘nullptr’在此作用域中尚未声明 /tmp/compiler_65pf_i0l/src: In function ‘int main()’: /tmp/compiler_65pf_i0l/src:58:21: 错误:‘nullptr’在此作用域中尚未声明 58 | node* cur = nullptr; | ^~~~~~~
解决办法:编译器太老了不认识nullptr,把nullptr改为NULL
一运行就弹出这个窗口
原因:是错误设置了断点的原因,重新新建一个项目把代码复制过去就好了
STL list 的迭代器it报错
在 中蓝桥杯一题中遇到的
decrement是减小的意思
这里就是it–造成的错误,当it是第一个iterator的时候不能–
上课例子:关于浅拷贝和深拷贝导致的指针链表错误
#include<iostream>
using namespace std;
struct node{...};
class linklist
{
private:
node* head;
public:
//注意这种新新的写法,xxx():变量值(要赋的值) {函数体,没有就是光赋值不干别的}
linklist() :head(nullptr) {};
~linklist() {
node* current = head, * nextnode;
while (current != nullptr)
{
nextnode = current->next;
delete current;
current = nextnode;
}
}
void insertAtHead(int val) { }//在链表头部插入结点
};
int main()
{
linklist list1;
list1.insertAtHead(2);
list1.insertAtHead(1);
linklist list2 = list1;
return 0;
}
这段代码会报错,原因是指针指向的同一片区域被释放了两次
首先 linklist没有定义拷贝构造函数和赋值运算符,所以当执行
linklist list2 = list1;
的时候,编译器生成的默认拷贝构造函数会直接复制head指针的值,这就导致list1和list2的head指向的其实是同一片内存地址,也就是他俩指向的同一个链表,是浅拷贝导致的错误
所以当结束的时候会自动调用析构函数,同一片内存被delete两次,导致错误
但是,什么是深拷贝?什么是浅拷贝?
浅拷贝就是浅层拷贝
,比如说B要从A那里浅拷贝,其实A和B就是同一个东西,当A改变的时候B也会改变,当B改变的时候A也会改变
深拷贝就是A和B是完全独立的两个东西,A的值改变不会影响B同样B的值改变不会影响A
字符串和数字之间相互转换的问题
每次遇到每次都不会
char字符 –> int数字
(适合输入的数字为0~9之间,比如高精度算法)
方法一:减去 ‘0’
char c = '5';
int num = c - '0'; // 结果是整数5
于是num就水灵灵地转换成了5
对于s[i]-=‘0’:
假设s[i]的值为字符5,‘5’的ascii码减去'0’的ascii码也就是ascii码对应为5的那个字符,不过在内存中存储s[i]的时候还是存储的二进制形式的5,只不过单拿出来s[i]是ascii码对应的3,将s[i]运算的时候还是ascii码3。
方法二:atoi()(引入
这是把整个char数组看成一个数,转换成int
char str[]="123";
int num=atoi(str);
//num就变成了123
string字符串–>int数字
(适合输入11 2 3 + * #这一行数据,里面有数字11不能拆开成单个char字符,也有除了数字字符别的其他运算符,比如后缀表达式运算)
利用stoi函数(引入
string b="123"
int num=stoi(b);//此时num的值是123
stoi是把string类型的字符串转换成int类型并保存在num数组中
它还有它的好兄弟 stol(表示转换成long),stoll(表示转换成long long)
int数字–>string字符串
方法一:to_string()
int num=123;
string str=to_string(num);
//此时str就是"123"
方法二:stringstream()
int num=123;
stringstream ss;
ss<<num;
string str=ss.str();
//此时字符串str的值就是"123"
int数字–>char字符
利用的就是asscii码的问题
int num=1;
char s=1+'0';
//此时s就变成了字符'1'
int多位数字–>char数组
循环/10吧
掌握这些应该就差不多了