#include
#include
#include
using namespace std;
int main() {
string t, m;
cin >> t;
cin >> m;
cout << t << "!!" << m;
}
input 1
abc
cdf
output 1
abc!!def
input 2
abc cdf
output 2
abc!!def
#include
#include
#include
using namespace std;
int main() {
string t, m;
cin.get(t);// >> t;
cin.get(m);// >> m;
cout << t << "!!" << m;
}
Debug:
“std::basic_istream> &std::basic_istream>::get(std::basic_streambuf> &,_Elem)”: 无法将参数 1 从“std::string”转换为“_Elem &”
说明了cin可以读入string,但是cin.get()不能
#include
#include
#include
using namespace std;
int main() {
char t[10], m[20];
cin >> t;
cin >> m;
cout << t << "!!" << m;
}
input 1:
abc
abc
output 1:
abc!!abc
说明回车在cin会被认为结束
input 2:
abc abc
output 2:
abc!!abc
说明cin会认为空格是分割符不会直接读入一个空格到char数组中,如果不想留0数组一定是scanf输入
input 3:
abcabcabca abc
output 3:
---------------------------
Microsoft Visual C++ Runtime Library
---------------------------
Debug Error!
Program: E:\Test\ccpp\ts\Project1\Debug\Project1.exe
Module: E:\Test\ccpp\ts\Project1\Debug\Project1.exe
File:
Run-Time Check Failure #2 - Stack around the variable 't' was corrupted.
(Press Retry to debug the application)
---------------------------
中止(A) 重试(R) 忽略(I)
---------------------------
注意,这里的cin读入不是像scanf那样大小是10就可以访问十个元素,这是一种“字符数组”,和char数组是有区别的,他要预留一个位置给\0
QQQ1问问题:不是说例如int a[10]的可访问a[0]-a[9],a[10]存在,就是\0
但是用VS调试不是这样的,但是访问*(&a+10)返回的是0,但是实际上为0的概率太小了,所以不知道为什么要预留\0,还有就是如何在控制台输入ASCII0
cin.get(&a[i],size)
#include
#include
#include
using namespace std;
int main() {
char t[10], m[20];
cin.get(t,10);
cin.get(m,15);
cout << t << "!!" << m;
}
input 1:
123 456
output 1:
123 456!!
说明输入时忽略空格空格符的,认为是字符串的一个组成,他是记录回车了刘输入,所以只有回车为分隔符(\n&\r),但是,他是解析流输入以后,回车被弹出,到时认为m的输入为一个但回车,size可以比实际大,甚至可以比数组大,由于是流输入,他相当于有一个缓冲区,所以不会报错,但是如果你的输入超过size,他会截取前size的字符赋值,如果输入超过数组内存,Visual Studio 2019会报错,但是Dev-C++不报错,输入了多少就输出多少,可能是用了流输入缓冲区
QQQ2问问题:why?
顺便说一下work()叫做函数头,里面的是形参列表
main():
int main()
注意: main()必须返回0
但是在main()中不写会默认返回0(只有在main()中)
注意大小写不能任意修改
可以不叫main(),在c-开发的专用程序中可以不需要main()但是编译器会调用一个叫做_tmain()的函数(就像默认构造函数和构造函数的关系)或者在Windows下写dll模块不需要写main()
例如#include
这句话意思是在编译阶段将iostream文件的内容替换这里的#include
,一个简单地复制粘贴的过程,所以说,如果你不小心修改了iostream文件的内容,编译结果会改变,但是如果把.cpp或者.o文件(.o文件可以理解为编译器的"直译文件",没有进行连接等操作,.o文件经过连接就是exe文件了)复制到别的电脑上仍然可以正确编译
p.s.iostream
文件:由istream和ostream组成,顾名思义就是输入流和输出流,他的兄弟(准确的说是应该是孩子)还有fstream(file-stream)是iostream的一个子类,他的对象继承使用istream/ostream的方法进行基本的操作,但实际上大部分继承的方法都有自己的独有方法,例如在穿参时对于ifstream对象可以选择istream/ifstream,但是在两个都存在的时候会选择ifstream
void test(istream &ost){
...
}
void test(ifstream &ost){
//<-----choose it
...
}
main(){
...
ifstream ff;
ff.open()
test(ff)
...
}
头文件类型 | 约定 | 实例 | 说明 |
---|---|---|---|
C++旧式风格 | 以.h结尾 | iostream.h | C++可以使用 |
C旧式风格 | 以.h结尾 | math.h | c/c++都可以使用 |
C++新式风格 | 没有扩展名 | iostream | C++可以使用,但是要加上namespace std |
转换后的C | 去.h前缀家c | cmath | C++可以使用,忽略c的特性,要加上namespace std |
唯一要注意Visual Studio 2019在gcc/g++8.2.0-5下cstring
string.h
string
三个头文件各不相同
实验
#include
using namespace std;
int main() {
int t = 1;
cout << t;
}
编译成功
#include
//using namespace std;
int main() {
int t = 1;
cout << t;
}
编译失败,找不到标识符cout
#include
//using namespace std;
int main() {
int t = 1;
cout << t;
}
编译成功(有可能编译失败了,那么是缺少头文件iostream.h手动补进来就可以了,代码在下面)
/***
*iostream.h - definitions/declarations for iostream classes
*
* Copyright (c) 1990-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
* This file defines the classes, values, macros, and functions
* used by the iostream classes.
* [AT&T C++]
*
* [Public]
*
****/
#if _MSC_VER > 1000
#pragma once
#endif
#ifdef __cplusplus
#ifndef _INC_IOSTREAM
#define _INC_IOSTREAM
#if !defined(_WIN32) && !defined(_MAC)
#error ERROR: Only Mac or Win32 targets supported!
#endif
#ifdef _MSC_VER
// Currently, all MS C compilers for Win32 platforms default to 8 byte
// alignment.
#pragma pack(push,8)
#include
#endif // _MSC_VER
/* Define _CRTIMP */
#ifndef _CRTIMP
#ifdef _DLL
#define _CRTIMP __declspec(dllimport)
#else /* ndef _DLL */
#define _CRTIMP
#endif /* _DLL */
#endif /* _CRTIMP */
typedef long streamoff, streampos;
#include // Define ios.
#include // Define streambuf.
#include // Define istream.
#include // Define ostream.
#ifdef _MSC_VER
// C4514: "unreferenced inline function has been removed"
#pragma warning(disable:4514) // disable C4514 warning
// #pragma warning(default:4514) // use this to reenable, if desired
#endif // _MSC_VER
class _CRTIMP iostream : public istream, public ostream {
public:
iostream(streambuf*);
virtual ~iostream();
protected:
iostream();
iostream(const iostream&);
inline iostream& operator=(streambuf*);
inline iostream& operator=(iostream&);
private:
iostream(ios&);
iostream(istream&);
iostream(ostream&);
};
inline iostream& iostream::operator=(streambuf* _sb) {
istream::operator=(_sb); ostream::operator=(_sb); return *this; }
inline iostream& iostream::operator=(iostream& _strm) {
return operator=(_strm.rdbuf()); }
class _CRTIMP Iostream_init {
public:
Iostream_init();
Iostream_init(ios &, int =0); // treat as private
~Iostream_init();
};
// used internally
// static Iostream_init __iostreaminit; // initializes cin/cout/cerr/clog
#ifdef _MSC_VER
// Restore previous packing
#pragma pack(pop)
#endif // _MSC_VER
#endif // _INC_IOSTREAM
#endif /* __cplusplus */
说明没有扩展名的必须namespace但是有.h的不需要(实际上iostream.h是叫非标准输入输出)
假设这样一种情况,当一个班上有两个名叫 Zara 的学生时,为了明确区分它们,我们在使用名字之外,不得不使用一些额外的信息,比如他们的家庭住址,或者他们父母的名字等等。
同样的情况也出现在 C++ 应用程序中。例如,您可能会写一个名为 xyz() 的函数,在另一个可用的库中也存在一个相同的函数 xyz()。这样,编译器就无法判断您所使用的是哪一个 xyz() 函数。
因此,引入了命名空间这个概念,专门用于解决上面的问题,它可作为附加信息来区分不同库中相同名称的函数、类、变量等。使用了命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。
我们举一个计算机系统中的例子,一个文件夹(目录)中可以包含多个文件夹,每个文件夹中不能有相同的文件名,但不同文件夹中的文件可以重名。
using namepace std;
这里的using是编译指令,要using的不是单单一个关键字,而是一个名字空间,所以写上namespace,之后知道他是一个namespace了,我们指明我们要使用的名字空间是std名字空间
但是有一个问题,比如我要用std,还要用一个我自己写的namespace np,结果np和std都有endl这个名字,就比较尴尬了,所以更妥当的方式是这样写
std::cout<<"hellow word";
我们之后说为什么他会有两个冒号
但是这种方法未免太麻烦了
于是人们又想了一个办法
告诉编译器我要使用using预编译但是不是整个名字空间而是名字空间里的一个元素
using std::cout;
这样以后就不用写了,这样的using我们叫他using声明,而using namespace std
我们叫他using编译,不仅仅是因为他们声明的范围不同,更重要的是using声明可以限定在局部使用
下面说::
符号
::
是一种作用于解析运算符,就我而言我喜欢把他翻译成"下的",就像->
和.
我喜欢把他们翻译成“的”一样
std::cout,std名字空间下的cout
m::work(),写类的外联函数用到的,m类下的work()函数
特殊用法:全局作用域,有的时候写了全局函数但是找不到,这个时候就需要这样调用
::work();
一个简单地输出
cout<<"pich"<<endl;
1.<<
:这不是左移运算符吗,但是他在这里不是这个意思,这是通过重载的技术让<<
同时具有了两种含义,就像汉语里的多音字一样,我们可以判断他是那个读音,计算机通过"上下文"判断这个<<
不是左移,他的意思是把这个要输出的对象发送给cout,我们叫他插入运算符
2.endl:这是一种控制符,如果没有他,cout在输入一行后是不会自动换行的
3.\n
:这是一种换行符,但是和endl还是有区别的,在早期的编译器中cout<<"\n"
是不允许的,还有就是,我习惯只在最后一个地方写endl,虽然在中间写endl也是合法的,最后一个比较formal:endl确定可以在程序继续运行之前刷新,但是\n
不做保证
对于这样的转义字符,如果要想在控制台上输出’\n’需要写’\n’才可以,在C++ 11里面还有一种方法是raw字符串
4.Raw字符串
string s = R"1234(现在,我们连"(和)"都可以显示了)1234";
cout << s << endl;
这样写我们在控制台上就可以得到1234(现在,我们连"(和)"都可以显示了)1234
int a=15;
首先告诉电脑我要一个int类型的变量,快点开空间
然后告诉这个内存单元叫做a
然后存储
这里C++11还提供了一种赋值方法
int a=b=c=d=e=1223;
赋值操作时候从右往左进行的
QQQ问题:好像还有一个什么东西也是自右向左的,但是忘了qwq
新的赋值方法:
#include
using namespace std;
struct tt {
int a, b;
double c, d;
int e;
};
int main() {
int a {
123 };
tt m {
1,2,1.2,1.3,4 };
m{
1,2,1.2 };
}
我们使用{}对他进行赋值,没有=
他的特别之处就是可以对结构体进行赋值,按照写结构体的时候变量的顺序赋值,对于{}
内数目少于总变量数的时候,从前往后填充,不够的填0
更多细节留到后边
**新的变量定义:**如果不想判断是什么类型,可以使用C++11的auto
auto a=123;
auto ss="qwqyinyinyin";
好像看不出什么优势
那看看下面这个
set<string> s;
set<string>::iterator it=nams.begin()
可以写成
set<string> s;
auto it=nams.begin()
可以偷懒了qwq
但是如果你用一个很复杂的结构体去auto,编译器会提示
在直接列表初始化上下文中,类型“auto”只能从单个初始值设定项表达式推导出
无法推导“auto”的类型(依据“initializer list”)
不能判断,尤其是有时候丧心病狂的不把{}
里的内容写全
从报错提示我们可以知道,auto使用条件
一个小的注意事项:
对于char:char t="a";
是不合法的,必须是char t="a"
;而且print("%d",t);
输出t的ASCII
对于string:string t="a";
是不合法的,必须是string t="a"
;而且print("%d",t);
输出不是t的ASCII
print和cout的对比:
1.cout用起来更方便,不需要考虑变量类型,比如多个版本的C++标准对于long long int的占位符不同,用cout就不需要考虑这个问题
2.cout体现了C++的OOP特性通过重载<<
实现了输出,所以可以继续通过重载<<
的方式实现对结构体的整体输出
3.print设计不够精密,所以有些问题不会报错
4.print在实现特别复杂细致的格式的时候比较方便,但是print可以实现的cout都可以实现,甚至更多
5.print输出由于不保证在下一步运行时完成输出,所以速度快,cout用ios::sync_with_stdio(false);
关闭同步单数速度还是差一点,但是注意关闭同步时候cout和printf
上次忘了说的:cin.get()
,和getchar()一样,但是回车敏感,所以要暂停就要写两个,一个吃上一次输入的回车,一个等待输入
work()
叫做函数调用
work
函数为被调用函数
函数原型:在编译的时候,编译器需要知道函数的返回值,所以需要函数原型,但是如果函数本身写在函数调用之前就不需要了
正常情况下写法就是吧实际的函数头复制下来,去掉{
,去掉变量名,结尾加;
但是也有不一样的时候
比如我的work函数默认三个变量,但是有一个变量是可选的,不写则按默认值算,那么函数头应该写成
int work(int a,int b,int c=122)
函数原型是
int work(int a,int b,int c)
int work(int a,int b,int c)
函数原型是
int work(int a,int b,int c=122)
两种写法都可以
但是注意C++无法识别你想默认的是哪一个变量,只是从前往后填,哪里不够,哪里就按默认值走,所以,默认值必须写在最后
函数原型食欲函数就像变量声明之于变量,函数原型只是描述了函数的接口,也就是说描述了发送给函数和函数返回的信息
关于main函数的返回值:
Q:返回给谁?
A:可以吧操作系统看成调用程序,因此,main()的返回值是返回给系统了
Q:为什么return 0;
A:通常约定返回0代表程序正常结束,非零代表有问题
exQ:main是关键字吗
exA:不是,所以你完全可以写一个变量叫main(),但是不要写一个结构体,然后重载(),然后起名字叫main,然后main(),会有很多奇奇怪怪的错误