【学习笔记】关于输入缓冲区。一个不规范的字符赋值语句引发的讨论

1.这种赋值语句是我练习时写的错误代码,它不符合规范,是实际写代码的时候,不应该出现的。只是结果碰巧“正确”,因此拿出来讨论。

2.因此这篇笔记对实际代码能力的提升并没有太大帮助。

下面这段代码,4个printf语句的输出结果分别为多少?

#include<stdio.h>

int main()

{

char *a = ‘abc‘ ;

char b = ‘abc‘ ;

printf( "%s\n", &a);      //c

printf( "%c\n", a);         //cba

printf( "%s\n", &b);     //c [email protected]$#$(乱码) cba

printf( "%c\n", b);        //c

return 0;

}

----------------------------------------------------------------------------------------------------------------------------------------------------

从一开始学习C语言中的char变量类型,几乎所有教材都在警告我们,单引号里面只允许存放一个字符

如:

char a = ‘1‘ ;

char a = ‘1‘ ;

然而,当单引号中的内容为若干个字符时,比如

char a = ‘123‘ ;

char a = ‘abc‘ ;

这时编译器并不会报错,而是会在编译的时候弹出警告。(测试环境:VS2015、VC++6.0、vim)

但注意,这种赋值语句是不符合规范的,是实际写代码的时候,不应该出现的。

下面这段代码:

int main()

{

char b = ‘abcd‘ ;

printf( "%c\n", b);

return 0;

}

printf语句输出的结果为字符d

由此可以看出,当我们用这种不规范的方式给一个char类型变量赋值,该变量实际得到的值是我们所赋的那串字符 abcd 的最后一个字符 d

----------------------------------------------------------------------------------------------------------------------------------------------------

现在来看一开始那段代码:

#include<stdio.h>

int main()

{

char *a = ‘abc‘ ;

char b = ‘abc‘ ;

printf( "%s\n", &a);      //c

printf( "%c\n", a);         //cba

printf( "%s\n", &b);     //c [email protected]$#$(乱码) cba

printf( "%c\n", b);        //c

return 0;

}

在调试的过程中打开内存,监视到的内存如下:

0x0018FC93  63     cc     cc     cc     cc     cc     cc     cc     cc    (&b)

0x0018FC9C  63    62     61    00    cc     cc     cc     cc     b8    (&a)

通过监视到的内存可以理解printf函数为什么这样输出了。

但是这里又出现了一个问题:

char *a = ‘abc‘ ;

应该可以等同于:

char *a = NULL;

a = ‘abc‘ ;

同样是给一个变量赋值,为什么在内存中存储的方式不一样呢?

既然赋值相同,那么问题就出在变量类型上:

b 是 char类型的变量。

而 a 是 char类型的指针, 本代码中,无法获取 *a 的值,但按照定义, *a才是char类型的变量。而 a 作为指针,默认是一个int类型的变量。

这也解释了 &a 在内存中那个 00 的来源:int类型变量占4个字节,刚才赋值用了3个字节,剩下的一个字节,编译器用 00 补上了

而也正是因为那个‘00’ ,代码中的printf( "%c\n", a);  才能“正常”输出。

明确了这点,将代码修改:

int main()

{

char *a = ‘abcd‘ ;

char b = ‘abcd‘ ;

printf( "%c\n", a);

printf( "%s\n", &a);

printf( "%c\n", b);

printf( "%s\n", &b);

return 0;

}

执行这条代码, 果然,由于4个字节被“填满”  printf( "%s\n", &a); 这条语句也无法正常输出了

--------------------------------------------------------------------------------------------------------------------------------------------------------------------

我们都知道,

正常情况下,char类型的变量,在接受赋值的时候,是只接收字符最后一个字节的。

但实际代码执行的时候,它并不是直接去找最后一个字符,

而是从第一个字符开始,逐个读取,每当读取一个字符,就将上一个字符“冲掉”,所以我们看到的永远是最后一个输入的内容。

现在只剩下最后一个问题了:除了最后一个字符,其他字符在哪里存放过,又是如何存放的呢?

再看一遍刚刚调试时的那个代码的内存:

0x0018FC93  63     cc     cc     cc     cc     cc     cc     cc     cc    (&b)

0x0018FC9C  63    62     61    00    cc     cc     cc     cc     b8    (&a)

可以看出,int 类型的 a变量,由于能容纳4个字节,所以并没有把多余的字符“冲掉”。

char 类型的变量 b ,只能容纳一个字节,但从始至终,b变量只存放过 63(c)。

而61 62 则是在 输入缓冲区 内,被先后“冲掉”的。

也就是说,给变量赋值,属于“写入”操作,这种操作并不是直接操作内存,

而是从一块被称为 输入缓冲区  的内存区域来回复制数据,这样的做法有利于提高机器工作效率。

当给int类型变量赋值时,相应的 输入缓冲区 长度为 4,当写满4个字节,再一次性地把这4个字节同时送入要赋值的变量所在的内存。

给 char 类型变量赋值同理,只不过这时  输入缓冲区 的长度为1,每写一个字节,就把前面的字节替代掉,所以最后得到的就是最后一个字节。

最后

这种赋值语句是我练习时写的错误代码,它不符合规范,是实际写代码的时候,不应该出现的。

这种赋值语句是我练习时写的错误代码,它不符合规范,是实际写代码的时候,不应该出现的。

这种赋值语句是我练习时写的错误代码,它不符合规范,是实际写代码的时候,不应该出现的。

				
时间: 2024-10-19 15:42:20

【学习笔记】关于输入缓冲区。一个不规范的字符赋值语句引发的讨论的相关文章

C++学习笔记之输入、输出和文件

一.流的概念 数据从内存的一个地址移动到另一个地址称为数据流动——流操作 流操作是通过缓冲区(buffer)机制实现的. 缓冲区:内存的一块区域——用作文件与内存交换数据. 数据从文件中读出:文件 → 缓冲区 → 内存 将数据写入文件:内存 → 缓冲区 → 文件 为什么要使用缓冲区而不直接从文件中读取数据到内存或者直接有内存写入文件呢?我们的文件通常都存在磁盘中,程序从磁盘读取一个字符需要大量的硬件活动,速度非常慢.缓冲方法则从磁盘上读取大量信息,将这些信息存储在缓冲区,然后每次从缓冲区里读取一

[Spring Data MongoDB]学习笔记--注册一个Mongo实例

1. 通过Java based bean metadata @Configuration public class AppConfig { public @Bean Mongo mongo() throws UnknownHostExceptioin { return new Mongo("localhost"); } } 上面的方式包含异常处理,这并不是我们想要的. 所以,应该尽量用下面这种方式MongoFactoryBean,或者后面的xml方式. @Configuration p

maven权威指南学习笔记(三)&mdash;&mdash;一个简单的maven项目

目标: 对构建生命周期 (build  lifecycle),Maven仓库 (repositories),依赖管理 (dependency management)和项目对象模型 (Project Object Model)有一个基本的理解 目前不准备深入学习maven故此使用工具ideaj 来帮助学习, 用idea创建一个默认的maven工程,结构如下: 相关命令: 打包:mvn package 编译:mvn compile 编译测试程序:mvn test-compile 清空:mvn cle

【原】Java学习笔记023 - 字符串缓冲区_正则表达式

1 package cn.temptation; 2 3 import java.util.Arrays; 4 5 public class Sample01 { 6 public static void main(String[] args) { 7 // 因为字符串创建后就不能修改,导致在进行字符串拼接时,会产生大量的中间字符串,创建对象都是需要消耗资源 8 // 所以,能不用字符串的直接拼接尽量不使用 9 10 // 字符串缓冲区:StringBuffer类/StringBuilder类

stm32寄存器版学习笔记06 输入捕获(ETR脉冲计数)

STM32外部脉冲ETR引脚:TIM1-->PA12;TIMER2-->PA0:TIMER3-->PD2;TIMER4-->PE0… 1.TIM2 PA0计数 配置步骤 ①开启TIM2时钟,配置PA0输入 APB1外设复位寄存器 (RCC_APB1RSTR) APB2外设时钟使能寄存器(RCC_APB2ENR) 置1开启.清0关闭. Eg:RCC->APB1ENR|=1<<0; //使能TIM2时钟  RCC->APB2ENR|=1<<2;  

【安全牛学习笔记】Linux缓冲区溢出

Linux缓冲区溢出 FUZZING Crossfire 1.9.0版本接受入展socket连接时攒在缓冲区溢出漏洞 调试工具 edb 运行平台 Kali i486虚拟机 [email protected]:~# cd /usr/games/ [email protected]:/usr/games# ls crossfire [email protected]:/usr/games#  rm -rf crossfire [email protected]:~# mv crossfie.tar.

Java 输入/输出(I/O)学习笔记 -- 文件输入与输出

在保存数据时,可以选择二进制格式或文本格式.例如,整数 1234 存储成二进制数时,它被写为由字节 00 00 04 D2 构成的序列(十六进制表示法) ,而存储成文本格式时,它被存成了字符串" 1234" .尽管二进制格式的 I/O 高速且高效,但是不宜人来阅读. 读写文本数据 在存储文本字符串时,需要考虑字符编码(character encoding)方式.在 UTF-16 编码方式中,字符串" 1234"编码为 00 31 00 32 00 33 00 34

caffe学习笔记2:Fine-tuning一个类别识别的预处理的网

Fine-tuning一个预处理的网用于类型识别(Fine-tuning a Pretrained Network for Style Recognition) 本文原文地址here 在这个实验中,我们我们探索一个通用的方法,这个方法在现实世界的应用中非常的有用:使用一个提前训练的caffe网络,并且使用自定义的数据来fine-tune参数. 这个方法的优点就是,提前训练好的我那个落是从一个非常大的图像数据集中学习到的,中层的网可以捕获一般视觉表象的语义信息.考虑把它作为十分强大的通用的可视化特

hibernate 框架学习笔记---网上摘抄的一个非常好的例子

编写Spring+Hibernate框架下的应用,总是离不了编写一个通用的泛型GenericHibernateDao.查阅了网上不少的GenericHibernateDao实现,归纳整理为如下实现,供后续编码参考. 首先定义接口泛型DAO接口 GenericDao package com.th.huz; import java.io.Serializable;import java.util.Collection;import java.util.Iterator;import java.uti