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++中输入输出语句怎么写_const指针赋值给非const分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/80878.html