1. stdin,stdout和stderr的理解
Linux系统下,当一个用户进程被创建时,与之对应的三个数据流(stdin,stdout和stderr,即三个文件)也会被创建。
- stdin,标准输入文件,通常对应着终端的键盘。
- stdout,标准输出文件,通常对应着终端的屏幕。
- stderr,标准错误输出文件,通常对应着终端的屏幕。
默认情况下,三个数据流对应的文件描述符分别是stdin—0,stdout—1,stderr—2,可通过如下代码查看。
#include <iostream>
#include <cstdio>
int main()
{
int stdinFd = fileno(stdin);
int stdoutFd = fileno(stdout);
int stderrFd = fileno(stderr);
std::cout << "stdinFd: " << stdinFd << std::endl;
std::cout << "stdoutFd: " << stdoutFd << std::endl;
std::cout << "stderrFd: " << stderrFd << std::endl;
return 0;
}
值得注意的是,stdout和stderr存在着一个重要的区别。
程序如下(Linux环境下运行)
#include <stdio.h>
int main(){
fprintf(stdout, "hello");
fprintf(stderr, "world");
return 0;
}
程序输出结果如下
worldhello
原因在于,stdout存在一个缓冲区,它的输出会先放在缓冲区里面,遇到换行或者缓冲区刷新时才会输出到屏幕上。而stderr不存在缓冲区,也就是说stderr的输出内容会直接打印在屏幕上。所以会出现上面的输出结果。
2. C++的统一初始化和输入输出
C++统一初始化——列表初始化 { }
#include <iostream>
using namespace std;
struct example{
char str[10];
int n;
};
int main(){
//普通变量初始化,下面六条语句作用相同
int a1 = 10;
int a2(10);
int a3 = int(10);
int a4 = {
10};
int a5 = int{
10};
int a5{
10};
//数组初始化,下面两条语句作用相同
int arr[5] = {
1,2,3,4,5};
int arr1[5]{
1,2,3,4,5};
//结构体初始化
struct example exa {
"hello", 3};
return 0;
}
从上面程序可以看出,变量都可以使用花括号{ }进行初始化,所以{ }成为C++的一种统一初始化方式。需要注意的是,列表初始化具有更强的类型检查,如下。
#include <iostream>
using namespace std;
int main(){
double a = 1.2;
int b = a; //代码正常运行,b == 1
int c = {
a}; //代码报错(从"double"转换到"int"需要收缩转换)
int d = {
(int)a} //代码正常运行
return 0;
}
C语言的输入输出最常见的是scanf 和 printf,这里需要注意的是,scanf是不安全的,自从Visual Studio 2005开始,scanf被scanf_s取代,在调用scanf_s时必须额外提供一个参数以表明最多读取多少位字符,代码如下。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(){
char str[5];
scanf("%s", str);
printf("%s\n", str);
return 0;
}
程序的输入输出为
helloworld
helloworld
调试代码,查看变量str
从调试结果可以看出,从str[4]之后(str[4]本身应该是’\0’),内存空间被非法写入,这可能引起别的变量被修改或者导致程序异常。为了避免这个问题,VS提供了scanf_s,上面程序的输入语句修改如下
scanf_s("%s", str, 5);
其中,5表示str的容量,由于字符串最后一位要设置为’\0’,所以str最大可读入4个字符。程序的输入输出如下。
//输入
hell
//输出
hell
这里需要注意的是,对函数scanf_s来说,当输入的数据超出变量的大小时并不是采取截断的方式,而是直接置为”\0″。意思就是,当输入数据为”helloworld”时,str并不是存储了”hell”,将”oworld”丢弃,而是直接将str置为空,str[0]置为了”\0″。如下
C++语言的输入输出使用的是cin和cout,必须包含头文件<iostream>和std标准命名空间,如下。
#include <iostream>
using namespace std;
int main() {
const int n = 5;
char str[n];
cin >> str;
cout << str << endl;
return 0;
}
cin的输入是以空格、换行或者制表符作为分隔符,如下。
#include <iostream>
using namespace std;
int main() {
const int n = 12;
char str[n];
cin >> str;
cout << str << endl;
return 0;
}
当输入为”hello world”时,str只会接收”hello”,而将” world”留给下一个变量接收。
//输入
hello world
//输出
hello
如果想把空格输入到字符数组str中去,可以使用getline函数。
#include <iostream>
using namespace std;
int main() {
const int n = 12;
char str[n];
cin.getline(str, n);
cout << str << endl;
return 0;
}
程序输入输出如下
//输入
hello world
//输出
hello world
如果想把空格和换行都输入进去,需要额外再给getline提供一个终止符。
cin.getline(str, n, '#');
程序输入输出如下
//输入
hell o
wor
#
//输出
hell o
wor
从下面的调试结果可以看出,空格’ ‘和换行’\n’都已经被输入进去。
3. const在C和C++中的不同之处
const在C和C++中,对常变量的处理方式不同(编译阶段,C中以变量为主,C++中以常量为主),以一段最简单的代码来看。
#include <stdio.h>
int main(){
const int n = 10;
int arr[n] = {
1,2,3,4,5};
return 0;
}
上面这个代码,如果运行在.c文件中会报错,而在.cpp文件中确能正常运行,这只是简单的展示了C和C++确实对const有着不同的处理方式,下面从汇编角度分析为何会出现这种情况。代码如下。
#include <stdio.h>
int main(){
const int a = 10;
int b = 0;
int *p = (int*)&a;
*p = 100;
b = a;
printf("a=%d *p=%d b=%d\n", a, *p, b);
return 0;
}
如果将上面的代码运行在.c文件中,程序输出结果如下。
a=100 *p=100 b=100
这似乎不值得惊讶,通过分析代码很容易得出这个结论。但如果将这段代码运行在.cpp文件中,输出结果可能令人不那么容易理解,如下。
a=10 *p=100 b=10
在VS2019中,通过对代码进行调试并进行反汇编来查看编译阶段C和C++对const的不同处理,进一步理解这个过程。首先来看.c文件下的调试和汇编。
再来看.cpp文件下的调试和汇编。
对比之下可以发现,在cpp文件中,从汇编的角度上看,常变量a出现的地方被10替换了,换句话说就是每当常变量a(单独)出现的地方直接被替换成了10,从代码上理解就是
b = a;
//被替换成了
b = 10;
printf("a=%d *p=%d b=%d\n", a, *p, b);
//被替换成了
printf("a=%d *p=%d b=%d\n", 10, *p, b);
//当然这条语句中的a是无法进行替换的,也就是上面为什么强调单独出现
int *p = (int*)&a;
这就是const在C和C++中的不同之处,C中以变量为主,C++中以常量为主。
4. const与指针
能力强的指针赋值给能力收缩的指针,该操作是正常的。
能力收缩的指针赋值给能力强的指针,该操作是错误的。
#include <iostream>
usi
int main()
{
int a = 10;
const int *p = &a; //right
int *s1 = p; //error
const int *s2 = p; //right
int * const s3 = p; //error
const int * const s4 = p; //right
return 0;
}
#include <stdio.h>
int main()
{
int a = 10;
int * const p = &a; //right
int *s1 = p; //right,通过s1并不能修改p
const int *s2 = p; //right
int * const s3 = p; //right
const int * const s4 = p; //right
return 0;
}
5. const与引用&
引用的定义:类型& 引用变量 = 变量;
#include <stdio.h>
int main()
{
int a = 10;
int &b = a; //引用
return 0;
}
const引用
#include <iostream>
using namespace std;
int main()
{
int a = 10;
const int b = 20;
int& x = a; //right
int& y = b; //error
const int& x1 = a; //right
const int& y1 = b; //right
int& x2 = 10; //error
int& y2 = 20; //error
const int& x3 = 10; //right
const int& y3 = 20; //right
return 0;
}
引用作为参数取代指针(传址)
#include <iostream>
#include <cassert>
using namespace std;
void swap_point(int* p1, int* p2)
{
assert(p1 != NULL & p2 != NULL);
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void swap_refer(int& num1, int& num2)
{
int tmp = num1;
num1 = num2;
num2 = tmp;
}
int main()
{
int a = 10, b = 20;
cout << "a = " << a << " b = " << b << endl;
swap_point(&a, &b);
cout << "a = " << a << " b = " << b << endl;
swap_refer(a, b);
cout << "a = " << a << " b = " << b << endl;
return 0;
}
从上面的代码来看,引用提供了一种更加简单的方式实现传址。
引用数组和指针
#include <iostream>
using namespace std;
int main()
{
int a = 10;
int arr[3] = {
1,2,3 };
int* p = &a;
int*& r = p; //引用指针
int& b = arr[0]; //引用数组元素
int(&parr)[3] = arr;//引用数组
return 0;
}
引用和指针的区别总结
- 语法规则上看,指针存储某个变量的地址,而引用是某个变量的别名;
- 指针变量需要分配内存区域,而引用不需要;
- 解引用时指针前面要加上*,而引用可以直接使用;
- 指针变量的值可以改变,存储不同变量的地址,而引用在定义时就被初始化,之后无法改变;
- 指针变量的值可以为空(NULL或nullptr),却没有空引用;
- 指针变量作为形参时需要检查它的合法性(判空),而引用不需要判空;
- 对指针变量使用”sizeof”得到的是指针变量的大小,而对引用使用”sizeof”得到的是变量的大小;
- 理论上指针的级数没有限制,但引用只有一级,不存在引用的引用,但可以有指针的指针;
- ++指针和++引用效果不一样;
从汇编角度上理解引用和指针的区别,代码如下。
#include <iostream>
using namespace std;
int main()
{
int a = 10;
int* p = &a;
int& b = a;
*p = 20;
b = 30;
return 0;
}
从上图可以看出,在汇编层面对引用的操作和对指针的操作是一样的。其实引用本身就是利用指针来实现的,&和*const的作用相同。
今天的文章c++中输入输出语句怎么写_stdout stderr分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/80053.html