当前位置: 首页 > news >正文

C语言问答进阶--5、基本表达式和基本语句

赋值表达式

表达式是什么?表达式是由运算符和操作数组成的式子。

如下的代码

#include "iostream.h"

int main()

{

   int a=1,b=2,sum; 

   cout<<(sum=a+b)<<endl; 

   return 0;  

}

那么如下的呢?

#include "iostream.h"

int main()

{

   int a=1,b=2; 

   cout<<(int sum=a+b)<<endl;  

   return 0; 

}

结果是: 编译就会出错!

在这里,这是声明和定义,不能放置于cout输出的位置。

sizeof运算符

C语言中提供了一个可以得到各种数据类型占用内存空间大小的运算符: sizeof运算符。

它的语法如下:sizeof(类型名或变量名)

如下:

#include<iostream>

using namespace std;

int main()

{

  cout<<sizeof(int)<<endl;

  return 0;

}

在32位机器上就会打印4.

在16位机器上就会打印2.

如果想要得到所有基本类型占用的字节数,如下代码:

#include <iostream>

#include<conio.h>

using namespace std;

int main()

{

cout<<"It will show the lengths of all kinds of numbers(It is system-related):"<<endl;

    cout<<"char ---------------"<<sizeof(char)<<endl;

    cout<<"bool ---------------"<<sizeof(bool)<<endl<<endl;

    

    cout<<"unsigned char ------"<<sizeof(unsigned char)<<endl;

    cout<<"wchar_t ------------"<<sizeof(wchar_t)<<endl;

    cout<<"short --------------"<<sizeof(short)<<endl;

    cout<<"unsigned short -----"<<sizeof(unsigned short)<<endl<<endl;

    cout<<"int ----------------"<<sizeof(int)<<endl;

    cout<<"unsigned int -------"<<sizeof(unsigned int)<<endl;

    cout<<"long ---------------"<<sizeof(long)<<endl;

    cout<<"unsigned long ------"<<sizeof(unsigned long)<<endl;

    cout<<"float --------------"<<sizeof(float)<<endl;

    cout<<"double -------------"<<sizeof(double)<<endl<<endl;

    

    getch();

    return 0;

}

此程序是得到了各种不同的数据类型(或者称作基本数据类型或内置数据类型)在内存中占据的空间。请不要相信这些类型占用空间一定是如上图示,因为它们是具有系统依赖性的,不同的系统可能不一样的。

类型不一致带来的问题

#include "iostream.h"

int main()

{

   int i;

   cin>>i;

   cout<<i<<endl; 

   return 0;

}

这个程序,声明的i是整型,而实际上如果输入了浮点类型的数,如1.2,那么将打印什么呢?

打印的是1.

原因很简单,声明的int是整型的,当遇到的输入符号不是整数0~9字符时就会认为输入结束。

我们用C风格代码来改写这个程序:

#include<stdio.h>

int main()

{

   int i;

   scanf("%d",&i);

   printf("%d\n",i); 

   return 0;

}

结果也是一样:

下面是个关于用不同类型的值来赋值的例子:

#include "iostream.h"

int main()

{

   int x=10.4;

   cout<<x<<endl; 

   return 0;

}

编译的时候出现警告:

意思就是10.4是个double类型常量,现在要把它赋值给整型变量中,可能有数据精度或数值的损失。

A:其实,x的值是在编译结束之前就已经计算出来了。

Q:这个不该是程序执行的时候才算的吗?

A:其实编译器也是一种程序嘛,这些事情编译器可以做了,就不用麻烦CPU在这个程序执行的时候做了。C语言是高效率的程序设计语言,一个很大的原因就在于有很多事情在编译的时候就可以计算好。

Q:那么为什么编译器把10.4看成了double类型呢?

A:那你觉得还可以看成什么类型呢?

Q:不可以看成是float类型吗?

A:应该说,float类型只是double类型的子集,编译器也是为了考虑数据精度和数据能表达的范围来考虑,一般对于之类数据都看成double类型统一处理。

A:就是把整数10赋值给变量x的。

溢出

现在我们深入研究下程序设计语言中存在的溢出现象:

下面这个程序是一个溢出的实例:

#include "iostream.h"

int main()

{

  short h=32768;

  cout<<h<<endl;   

  return 0;

}

short类型是16位的,最大能表达的数据是32767,而却给它赋值32768;
这将导致溢出,溢出的结果是可能造成不可预料的错误,一般都会出现错误。

当然溢出也有一定的规律可循:

32767的表示为:

0111 1111 1111 1111

而赋值32768,就是在32767的基础上再加1,得到:

1000 0000 0000 0000,

当然计算机会把这个当作short类型处理,是个负数,-32768.

我们要熟记计算机内部各种数据类型的取值范围,这样才能在应用的时候选择更好的数据类型,而且也能最大程度地避免发生错误;就算发生了错误,也能在最快的时间理解错误可能发生的原因。

【C#的checked关键字】

C#中的checked关键字可以实现对于整型算术运算启用溢出检查。

当然,这个关键字不能直接拿到C语言中来使用,如果可以的话,可以自己写个程序同样用来判断整型数据类型在计算中是否溢出。

【编译器是如何从源代码计算一个整数的值的】

正如上面的代码,short h=32768;

对于编译器来说,源代码就是字符串的集合。如何把数字形式的字符串转换成整数,这是编译器需要做的事情。

C中有atoi、atol等将字符串转换成整数或长整型的函数。

这两个函数在atox.c源文件中有具体实现的代码。

【如何得到一个溢出的数的准确值】

正如上面,当把32768赋给一个short类型的变量时。首先,先考虑short类型的最大和最小值。最大值是32767,最小值是-32768.

再分析,当前赋给此变量的值是多少。

是32768,它和short类型的最大值差不多,而且是比最大值要大。所以,先得到32767的形式。

0111 1111 1111 1111  (中间的空格仅仅是为了看起来更清楚)

32768比32767大1,即再加1.

得到

1000 0000 0000 0000

所以,结果也就知道了。

【用C/C++写的检测溢出程序】

/*++++++

完成时间:2008年8月10日14:51:57

作者:    陈曦

作用概述:此程序用于检测int、short、long类型数据的加法是否发生溢出;运用的是双符号位原理:

如果两个相加的数是符号相同,但是得到的和的符号与它们的不相同,那么这就发生了计算溢出。

程序的局限:如果在输入两个要相加的数的时候,输入的数就已经溢出了,那么程序得到的结果可能就错了。

扩充性:对于float等浮点类型没有进行过测试,不知道此程序是否可以得到正确结果。

++++++*/

//头文件

#include <iostream>

using namespace std;

//数据类型的宏定义,可以改为int、long类型;

#define TYPE short

//检测加法是否溢出的函数,参数为TYPE类型的两变量

void checkAdd(TYPE i,TYPE j)

{

//定义要处理的三个数的符号,初始化为0

int sign[3]={0};

//如果数大于等于0,那么符号为0,否则符号为1(用自增的方式体现)

i>=0? sign[0] : sign[0]++;

j>=0? sign[1] : sign[1]++;

//定义相加的和sum

TYPE sum=i+j;

//计算sum的符号

sum>=0? sign[2] : sign[2]++;

if(sign[0]==0&&sign[1]==0&&sign[2]==1  //如果两加数为正数且和为负数

 ||sign[0]==1&&sign[1]==1&&sign[2]==0 )//如果两加数为负数且和为正数

       //那么得出溢出的结论

      {

   cout<<i<<" + "<<j<<" = "<<sum<<" ,sum is overflow!"<<endl;  

      }

   //否则,没有溢出

   else  

       {                                                  

       cout<<i<<" + "<<j<<" = "<<sum<<endl;

       }

}

//主函数

int main()

{

    TYPE i,j;

    

    //输入两要相加的数的值

    cin>>i>>j;

    

    checkAdd(i,j);

    return 0;

}

下面是个关于无符号类型数据溢出的实例:

#include "iostream.h"

int main()

{

   unsigned short h=65535;

   h+=1;

   cout<<h<<endl;                      

   return 0;

}

这个可以理解成溢出。

毕竟short类型长度是16位,而最大也只能表达65535;

后面的一句加了1,从二进制的角度考虑就是

  1111  1111   1111  1111

 +0000  0000  0000  0001

 =0000  0000  0000  0000

最高位进位了,但是却不会显示出来了。

有一天老师布置了一个较大的数相乘的题目;当然懒得用笔算,用程序吧。

#include "iostream.h"

int main()

{

  cout<<32767*32767*32767<<endl;

  return 0;

}

显然答案是错的!至少从尾数就可以判断出来。

那么哪错了呢?

溢出。

Q:那么为什么编译器不把这个值看做是double类型的呢?如果是这样,不就不会溢出了吗?

A:当然,编译器没这么聪明。当代码中是整数字面值出现的时候,编译器就认为结果也是用整型表示的。所以,也就溢出了。

【操作符中间有空格】

实际上,考虑这个问题得从这个角度去想:标识符和操作符其实在这一点上等同看待,即标识符中间不能有空格,那么操作符也不可以。

还有很多类似情况:

#include "iostream.h"

int main()

{

  int a=10;

  if(a> =1)

    cout<<"It is bigger than 1."<<endl;  

    return 0;

}

等等。

判断值是否相等

#include "iostream.h"

int main()

{

   if(0==NULL) 

   cout<<"0 is equal to NULL"<<endl;

   else

   cout<<"0 isn't equal to NULL"<<endl;

   return 0;

}

实际上,许多编译器对 NULL 的处理方式就是:

#define NULL 0

NULL 被用在什么地方?

它可以表示空指针,可以表示字符串结尾的 '/0' 符号等等。

下面的类似上面的:

#include "iostream.h"

int main()

{

   if(1==1.0000) 

     cout<<"1 is equal to 1.0000"<<endl;

   else

     cout<<"1 isn't equal to 1.0000"<<endl;

   return 0;

}

从现实的角度出发:

当然1是等于1.0000 .

而实际上,编译器在处理 if(1==1.0000) 的时候是先把前面的1转化成浮点型的1,当然它是等于后面的1.0000了。也就是在程序中,出现了不同精度或长度的数据之间的操作的时候,要统一转化成一种格式,然后再计算,这样做也是为了使得计算的结果更精确。

很多人在一些场合看到如下的宏定义:

#define NULL 0

那么如下程序该是什么结果呢?

#include "iostream.h"

int main()

{

   if("\0"==NULL) 

    cout<<"Equal"<<endl;

    else

    cout<<"Isn't equal"<<endl;

    return 0;

}

"\0" 是个字符串,这里使用的字符串 "\0" 其实是使用它的地址。当然不等于0了。

【上面的字符串的地址能这么肯定不是0吗】

是的。因为,笔者的程序运行的Windows操作系统上,虚拟地址0其实是不可能因为程序使用而被分配的;这个区域是禁止访问的。

如下就的等同的:

#include "iostream.h"

int main()

{

   if('\0'==NULL) 

    cout<<"Equal"<<endl;

    else

    cout<<"Isn't equal"<<endl;

    return 0;

}

在这里,字符'\0'是被当做整型0来处理的,当然和宏定义的NULL的0是相等的。

我想您对字符串和字符会有更深的理解。

数据类型的最大值和最小值

下面的程序是为了得到整型值的最大和最小值:

#include "iostream.h"

#include "limits.h"

int main()

{

  cout<<INT_MAX<<endl; 

  cout<<INT_MIN<<endl; 

  return 0;

}

INT_MAX和INT_MIN都是宏定义,它们在头文件limits.h中:

limit就是界限、限制的意思,在这里正是这样的意思。

同样还有很多类型有相应的表达:如LONG_MAX等等。

这个很有用,在有的时候需要得到一种类型的值的极值或者把此当作一个基础值来做比较的分析都很简便了。

如下为此头文件里的定义:

【可以用与INT_MAX或INT_MIN的值的对比来测试一个数是否溢出吗】

可能有人会想,既然INT_MAX和INT_MIN的值都已经知道了,那么测试一个数是否溢出不就可以直接用与它们的大小关系得到吗?

如下测试两个int类型的数的和是否超出INT_MAX值的程序:

#include <iostream>

#include<limits.h>

using namespace std;

int main()

{

cout<<"The biggest number of int type is :"<<INT_MAX<<endl;

    int i,j;

    cin>>i>>j;

    

    if(i+j>INT_MAX)

    cout<<i<<" + "<<j<<" = "<<i+j<<" ,Overflow!"<<endl;

    else

    cout<<i<<" + "<<j<<" = "<<i+j<<",Not overflow!"<<endl;

    return 0;

}

而结果如下:

得到的结果显然是错误的,这也表明不能用这种单纯的方式去判断是否溢出。

Q:为什么这种判断不可以呢?

A:21亿和1亿的和超过了int类型的最大值,溢出了,但是它的最高位是1,即是负数。而用它和int最大值比较的时候,它显然比INT_MAX要小,也就造成了没有溢出的假象。

【a==2】这种表达式

#include <iostream>

using namespace std;

int main()

{

   int a=3;

   a= =2; //此处是为了看清楚才把两个等号之间加了空格,源代码中没有此空格

   cout<<a<<endl;

   return 0;

}

这里想提的是a= =2; 这种语句,也被称作表达式语句;其实在这里它并没有实质的作用,它并没有做赋值改变值的操作,也没把这个表达式的值当作条件判断的依据,它只是个关系表达式,只是加了个分号被看做了语句,别的什么也没有。曾经看过一个类似这样的例子,一个很大的软件系统就因为这样一个式子导致了巨大的错误,是把这类式子看成了具有赋值作用的语句导致的。

于是,笔者就在想,为什么C语言允许这样的表达式存在,确实是没什么实在的作用?

当然,在编译的时候出现了警告!但是,警告也丝毫没有改变这类式子存在的可能。

也许是C语言自由的另外一个表现吧。

字符串在一起

将介绍一个关于字符串的程序,它是把两个字符串放在一起:

#include <iostream>

using namespace std;

int main()

{

    cout<<"Hello" " CX"<<endl;

    return 0;

}

这里,把两个字符串放到一起,编译器会把它们合并成一个字符串,然后输出;

那么为什么可以有这种机制呢?

或许这种方式最简便了。

【自增运算符的弊端】

#include <iostream>

using namespace std;

int main()

{

    int x=2;

    int z=x/++x;

    cout<<z<<endl;        

    return 0;

}

这个得到的1是在很难理解?

这个程序也真实地表明了自增运算符有的时候确实会造成很大的误解,毕竟它这一个运算符的存在要做两件事,一件是被使用,一件是要自增。那么到低先做哪件事?这是出现混乱的根源。C++标准从来没有明确表明应该先做哪件事,所以在两件事可能造成混乱的地方慎用,不行的话,就分开为2句话来写。

【字符串的strlen和sizeof】

这个程序是关于字符串的strlen和sizeof值:

#include <iostream>

using namespace std;

int main()

{

    char *c="a";

    cout<<strlen(c)<<endl;   

    cout<<sizeof(c)<<endl;   

    return 0;

}

strlen这个长度是给程序员看的,正如我们想弄明白一个字符串的长度一样;

而sizeof带有一点存储的意味,更多地倾向与在内存中的存储关系。

【//是写给编译器看的】

C语言中,为了表示  /  这个字符,必须在它前面加个  /  字符。

原因就是编译器才能看明白。否则,那么以  /  开头的转义字符就没有意义了。

编译器忽略空格

编译器是很聪明的,正如你写int i=1;和 int i =  1;一样,对它来说,这是一样的。

但是,这也不是绝对的,也有意外。

Q:这个程序为什么不能通过编译呢?

#include "iostream.h"

int main()

{

    cout<                     

<1               

    <<

endl;

return 0;

}

编译错误信息为:

A:为什么你认为它是可以通过编译的呢?

Q:它不是和这个程序可以看做一样的吗?

#include "iostream.h"

int main()

{

cout<<1<<endl;

return 0;

}

A:为什么你认为这两个程序可以被看做是一样的呢?

Q:您不是说过,编译器会忽略一些空格、回车符或是TAB符号之类的符号,进行有用的代码分析吗?这里

cout<  

<1    

<<

endl;

不也可以忽略那些回车符和空格之类的,把它转化成 cout<<1<<endl; 吗?

A:其实,以前说的那些忽略空格之类的理论是有前提的:所有的变量、操作符等必须被放在一起的符号是不能用空格、回车符等来隔开的。

Q:你是说,在这里

cout<  

<1 

中 << 符号是不能隔开的?

A:是的。你看如下的程序,就可以通过编译:

#include "iostream.h"

int main()

{

cout  << 1  << endl;

return 0;

}

执行如下:

问题就在于对比下:

cout<<1<<endl; 和 cout  << 1  << endl;

我们得先提取标识符,包括运算符:

从左到右依次有 

cout  

<< 

<<

endl 

只要不用空格之类符号在它们里面隔开就不会编译错误了。

【运算符也要当作普通标识符来看】

运算符(比如这里的<<)也是要把它当作普通变量标识符一样来看待的!你把它们分开了,编译器当然也就不识别了!

Q:那么如下的程序也就是可以正确编译的吧?

#include "iostream.h"

int main()

{

    cout                     

    <<1                

    <<

endl;

return 0;

}

A:是的。且执行正常:

当然,如下的也就编译错误了:

#include "iostream.h"

int main()

{

    cout                     

    < <1                

    < <

endl;

return 0;

}

编译的出错信息为:

话说Press any key to continue

Q:函数里什么也不加就会什么也不打印吗?

A:

#include "iostream.h"

int main()

{

    return 0;

}

Q:为什么还会有句话显示呢?

A:这不是这个程序执行的时候打印的了,这是集成开发环境在调试程序的时候打印的语句了!

【集成开发环境】

就是开发环境的集成;

理论上说,编写程序可以不用集成开发环境的,用一个记事本写上一段源代码,在cmd.exe中用编译的命令(VC对C/C++代码的编译命令是cl)就可以得到我们要的目标代码或可执行程序(当然这个需要我们之前安装了这些文件并把它们信息加入环境变量中,包括这些命令文件和需要的头文件及链接库文件)。

(这里是在C盘根目录下执行的cl命令,如果已经把cl命令加入了系统环境变量中,那么在什么位置都可以执行这个命令)

下面举个具体的例子:

1、在记事本中编写源代码:

保存在C盘根目录下:

2、进入cmd.exe,转入C盘根目录:

输入cl  hello.cpp命令:

接着会发现许多编译的时候出现的信息,可能有警告,不过不要紧;

文件夹里已经生产了我们所要的.exe文件了;

我们用dir命令:

我们发现目标文件、可执行文件确实在这里。

3、执行程序:

输入hello.exe:

我们看到了我们要的结果。

现在我们回过头来,既然这样就可以,那么集成开发环境的作用是什么呢?

当然,方便是一方面,而便于调试应该说是最主要的。

Q:也就是说,我们如果直接双击这个程序来执行,最后不会打印

的吧?

A:是的。正如我们上面看到的hello.exe,它只是打印了该打印的;我们可以回到我们刚刚生成的那个hello.exe文件夹下,双击它:我们会发现只是一闪而过,这表明它已经执行完毕了!控制台应用程序就是这样,执行完了就会关闭它的窗口。

Q:那么如何才能让控制台应用程序执行完了不立即关闭它的窗口呢?

A:那么可以用getch函数。就用上面的hello.cpp举例,在代码中加入getch(); 

因为这个函数在conio.h头文件中,那么再用#include把这个文件包含上。

#include<iostream>

#include<conio.h>

using namespace std;

int main()

{

  cout<<"Hello World!"<<endl;

  getch();

  return 0;

}

执行结果如下,我们发现它并没有像以前的程序那样立即出现

Press any key to continue的提示;

接着按任意键,才出现了Press any key to continue的提示,这就是getch()函数起得作用。

我们到这个程序的目录下双击执行:

我们发现它并没有像前面那样一闪而过了。

Q:那么介绍下getch函数吧。

A:

下面介绍两个关于字符操作的重要函数:

#include<conio.h>

int main()

{

    char ch;

    ch=getch();

    putch(ch);

    return 0;

}

getch还是的作用就是系统等待用户输入一个字符,而且输入的字符不会显示在控制台上,然后接着执行。

putch还是就是把后面参数中的字符输出到控制台上。

#include <iostream>

#include <conio.h>

using namespace std;

int main()

{

    char c;

    c=getche();

    cout<<c<<endl;

    return 0;

}

这个程序在输入了一个字符后,将先显示您输入的字符,接着再去打印它。

getche()函数的作用就是接收标准输入设备(一般是键盘)输入一个字符,显示输入的字符。

它和getch()函数的不同之处在于,getch()函数不显示输入的字符。

如下:

#include <iostream>

#include <conio.h>

using namespace std;

int main()

{

    char c;

    c=getch();

    cout<<c<<endl;

    return 0;

}

还有个关于字符操作的函数getchar():

#include <iostream>

#include <conio.h>

using namespace std;

int main()

{

    char c;

    c=getchar();

    cout<<c<<endl;

    return 0;

}

getchar()函数它是接收输入的一个字符,但是必须等到接收了Enter键之后才开始它的后续执行。

正如上面:输入了一个字符a之后,没有接收到Enter键,所以还会继续等待;

又输入了字符d,同样还要等待;接着输入了Enter,即把缓冲区内第一个字符给变量c,接着执行下面,即是打印这个字符,也就是a了。

我想大家见过这个函数:

getch(),还有个是getchar(),如果您更深入还有个getche();当然它们都有个目的就是等待操作者去输入一个字符,然后才会继续;这在一个场合很有用的:比方说,您的程序要输出很多,然而你希望是一段一段地看,免得一下都输出了上面的都不好找了,甚至就是被CMD给忽略了,那么用这个暂停下,给你足够的时间去看;当然它们3个之间是有区别的,以后介绍。

【kbhit函数】

下面介绍一个不常用的一个函数:

kbhit是keyboard hit的意思。

int kbhit(void);    位于头文件conio.h下;

作用是:检测是否有键按下,如果有,则返回非0值(即真),否则返回0(即假)。

如下程序:

#include <iostream>

#include"conio.h"

using namespace std;

int main()

{

   int i;

   for(i=0;!kbhit();i++) 

     cout<<i<<endl; 

   return 0;

}

执行的时候会看到:

您不按键它就继续执行下去,除非你按键了!那么它就立刻退出了循环(当然是因为!kbhit()的取值为假了)!

那么有人会问了,那它和getch()函数什么区别呢?

kbhit() 在执行时,检测是否有按键按下,有按下返回键值 ,没有按下返回0;是非阻塞函数 ;
getch() 在执行时,检测按下什么键,如果不按键该函数不返回;是阻塞函数。

这个函数在调试某些程序时很有用。

{ } 到底是什么?

当然这是和我们想的一致的;那么您可能有这样的怀疑了:{ } 这种符号到底是做什么的?

虽然很多人都知道这是一个函数的开始和结束的标志;或许还可能是一个if语句、for语句等等能奏效的范围!

那么如下的您认为可以么?

{                            

#include "iostream.h"

int main()

{

    return 0;

}

}

真的不对!

错误原因是missing function header !

那下面的呢?

#include "iostream.h"

int main()

{

    return 0;

}

{        

}

哦!当然还是不对的了!原因一样!missing function header !

还有这个呢?

#include "iostream.h"

{             

int main()

{

    return 0;

}

}

{ } 可以是一个函数的执行体部分的开始和结束标记,也可以是if条件语句的执行体范围的标记,可以是switch语句后面包含各个case的包含符,甚至可以当作是在任意一段执行代码中一个内嵌的域,正如:

#include <iostream>

using namespace std;

int main()

{

    int i=1;

      {

       int i=2;

       cout<<i<<endl;

      }

    cout<<i<<endl;

    return 0;

}

你可以分清楚两个i的不同作用范围吗?

这个将在后面的变量的作用域中详细讲解。

深入scanf函数

下面是关于输入和输出的程序:

#include "iostream.h"

#include "stdio.h"

int main()

{

   int a;

   cout<<"Input a number:"<<endl;

   scanf("%d",&a);

   cout<<a<<endl; 

   return 0;

}

这个大家很熟悉了。

而下面的呢?

#include "iostream.h"

#include "stdio.h"

int main()

{

   int a;

   cout<<"Input a number:"<<endl;

   scanf("%d",a);        

   cout<<a<<endl; 

   return 0;

}

这个与上面的不同之处就在于scanf还是后面的变量a前面没有加取地址符&,结果呢?

得到了一个很诡异的值。

为什么会这样呢?

这还得从scanf说起。它后面的双引号后面必须用变量地址,把前面双引号内地变量由用户从标准输入设备(一般是键盘)输入到那个地址里。

第一个程序做到了;

第二个呢,是把自身当作地址了,当然这是不对的了。

#include "stdio.h"

#include "iostream.h"

int main()

{

   int a;

   cout<<"Input a number:"<<endl; 

   scanf("%d",&a);                  

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

}

本来是个很简单的程序,但是要求输入十进制整型,我们却输入了一个字母a,结果果然和我们想的不一样。

为什么会这样?

在输入了一个字母a而不是整型需要的数字0~9的话,那么程序自动认为输入结束。那么这里的变量a程序是使用了堆栈处的值给赋值的。当然,堆栈处的值目前是脏数据。

【用指针实现scanf中输入变量地址】

既然scanf函数是语法是这样的,我们不妨用指针来表达其中的输入变量的地址。

如下:

#include "iostream.h"

#include "stdio.h"

int main()

{

   int a;

   int *pa=&a;

   cout<<"Input a number:"<<endl;

   scanf("%d",pa);

   cout<<a<<endl; 

   return 0;

}

我们发现编译没错,运行也很正常:

Q:那么为什么scanf函数后面要用变量地址呢?理论上说,用变量名不也可以吗?

A:你说的有道理。如果把这个位置用变量名来表示,改下编译器,其实也可以表达输入的值放到这个变量里。但是,对于scanf这个基本输入函数,要对所有类型都适用才可以;如果是数组呢?

对于声明的数组,我们能用的只是它的数组名,可是它就是地址。那么如何表达输入一个字符数组(也就是字符串的一种形式)呢?

char c[6];

scanf("%s",c);

这里的c是什么?它是个地址。

我想上面这个程序在这个位置用地址就是为了和形如字符数组的输入相统一。

关于注释

注释可以防止因为时间的原因导致忘记了代码的意思了,而且对于别人对代码的维护也有极其重要的参考价值。

C语言有2种注释方法:一种是/*  */方式,就是把要注释的代码放到/*和*/之间。

但是这种无法嵌套。

第二种是:

C++还提供了一种很简洁的单行注释方式://  .

注释,是程序代码中重要的组成部分,尤其在大型软件制作中,注释是极其重要的。

介绍的将是C中更让人惊叹的代码:

#include <iostream>

using namespace std;

int main()

{

    int a= /*This is the value of a*/ 2;

    cout <<a<<endl;

    return 0;

}

注释可以放到代码中,一般来说都是代码后面后是自己独立成行;

像上面这样,代码和注释居然可以如此亲密,没有编译和链接的错误,还真是少见!

不过,由此加深大家对C语言自由的理解深度!

#include <iostream>

using namespace std;

static int div(int *divisor)

{

int result=5;

result=result/*divisor; /*Do divide */;

*divisor=1;

return result;

}

int main()

{

    int num=5;

    cout<<div(&num)<<endl; 

    return 0;

}

这里又一次展示了注释在代码中的奇特表现!

编译器支持中文吗

Q:程序中可以输出中文信息吧?

A:是的,可以。一般新点的编译器都会支持的。

TC3.0编译器,不支持中文,如下代码:

#include <iostream>

using namespace std;

int main()

{

    cout<<"您好!"<<endl;

    return 0;

}

执行结果是:

逗号表达式

下面是个类似逗号表达式的程序:

#include <iostream>

using namespace std;

int main()

{

    long one_million;

    one_million=1,0,0;

    cout<<one_million<<endl;  

    return 0;

}

为什么会得到这个结果呢?

如果说,1,0,0这个位置可以用逗号表达式来解释,那么值也是最后一个的0啊,为什么最后打印的是1呢?


微风不燥,阳光正好,你就像风一样经过这里,愿你停留的片刻温暖舒心。

我是程序员小迷(致力于C、C++、Java、Kotlin、Android、iOS、Shell、JavaScript、TypeScript、Python等编程技术的技巧经验分享),若作品对您有帮助,请关注、分享、点赞、收藏、在看、喜欢,您的支持是我们为您提供帮助的最大动力。

欢迎关注。助您在编程路上越走越好!

相关文章:

C语言问答进阶--5、基本表达式和基本语句

赋值表达式 表达式是什么&#xff1f;表达式是由运算符和操作数组成的式子。 如下的代码 #include "iostream.h" int main() { int a1,b2,sum; cout<<(sumab)<<endl; return 0; } 那么如下的呢&#xff1f; #include "iostream.h" int mai…...

uniapp3.0实现图片上传公用组件上传uni-file-picker,uni.uploadFile

用uniapp3.0的写法组合式api&#xff0c;setup形式封装一个图片上传公用组件&#xff0c;要求 1、使用uni-file-picker选择文件 2、uni.uploadFile上传图片 3、要能支持上传接口动态化 4、支持删除如片列表中已上传项 5、可以预览已上传列表图片 6、支持动态化限制图片格…...

Unity游戏开发002

Unity游戏开发002 目录 第一章&#xff1a;Hello&#xff0c;Unity&#xff01;第二章&#xff1a;创建一个游戏体 本文目录 Unity游戏开发 Unity游戏开发002目录本文目录前言一、创建一个游戏体1. 编辑器语言设置2. 创建游戏对象的两种方法3. 快速复制和粘贴物体4. 注意事项…...

MySQL基础练习题38-每位教师所教授的科目种类的数量

目录 题目 准备数据 分析数据 总结 题目 查询每位老师在大学里教授的科目种类的数量。 准备数据 ## 创建库 create database db; use db;## 创建表 Create table If Not Exists Teacher (teacher_id int, subject_id int, dept_id int)## 向表中插入数据 Truncate table…...

haproxy 原理+实战

haproxy 1 haproxy简介1.1 定义1.2 原理讲解1.3 HAProxy的优点&#xff1a; 2. haproxy的基本部署2.1 实验环境2.1.2 haproxy主机配置2.1.3 webserver1配置2.1.4 webserver2配置 3. haproxy的全局配置4. haproxy代理参数5. haporxy的热处理6.haproxy的算法6.1 静态算法6.1.1sta…...

OSPF进阶

一、LSA详解 Type&#xff1a;LSA的类型&#xff08;1、2、3、4、5、7类&#xff09; link-state-ID&#xff1a;链路状态表示符 ADV router&#xff1a;产生该LSA的路由器 age&#xff1a;老化时间 Metric&#xff1a;开销值&#xff0c;一般都为ADV router到达该路由的开…...

SuccBI+低代码文档中心 — 可视化分析(仪表板)(下)

制作仪表板 引入数据模型 仪表板所需模型已经在数据模块中准备好&#xff0c;可以将对应模型表添加到数据模型中。提供了两种添加方式&#xff1a; 在数据栏中点击添加按钮&#xff0c;在弹出框中通过搜索或直接在其所在目录下选中该模型&#xff0c;点击确定。 点击数据按钮…...

前端创作纪念日

机缘 作者也是一名新人大学生&#xff0c;在学习过程中总是get不到专业的知识体系&#xff0c;机缘巧合下了解通过md文档记笔记然后分享在各大博客平台上面&#xff0c;可以吸引社区博客朋友们的关注的鼓励&#xff0c;使得直接创作努力学习的心更加澎湃。 实战项目中的经验分…...

丰收季遇科技之光:北斗卫星导航引领现代农业新篇章

在这个金风送爽、硕果累累的丰收时节&#xff0c;广袤的田野上洋溢着农民们欢声笑语&#xff0c;每一粒饱满的果实都是大自然与辛勤耕耘者的共同馈赠。而在这片希望的田野上&#xff0c;一项科技革命的浪潮正悄然改变着传统农业的面貌——北斗卫星导航系统&#xff0c;正以它精…...

解决windows7虚拟机安装不了vmtools问题

安装不了vmtools问题所在&#xff1a; 没打补丁 ​ 打补丁问题 补丁在本地下载之后无法传到win7虚拟机中 补丁获取 补丁链接如下&#xff1a; https://catalog.s.download.windowsupdate.com/c/msdownload/update/software/secu/2019/09/windows6.1-kb4474419-v3-x64_b5614c6…...

Microsoft VBA Excel VBA函数学习笔记——数据切分熟练度+1

问题场景 123456Stock006006006002002002MarketUSUSUSUSUSUSWeight0.010.1090.2280.2220.2390.72CurrencyEURUSDCNYEURUSDCNYTerm10.0740.0820.0120.0470.0580.067Term20.040.020.010.070.0580.067Term30.0540.0520.0140.0870.0480.017Term40.0710.0840.0020.0170.0180.097………...

uniapp获取swiper中子组件的内容高度

swiper有默认高度,如果不单独设置一个具体高度&#xff0c;swiper后面的内容将不会展示 这里展示的例子是: swiper中放有一个子组件,想要完整展示子组件的内容&#xff0c;swiper就需要获取到子组件的内容高度并设置 <!-- 注意: 这里的单位是 px,不是rpx --><swiper…...

基于计算机爱心小屋公益机构智慧管理(源码+论文+部署讲解等)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台的优…...

详细学习PyQt5的样式表与界面美化

Pyqt5相关文章: 快速掌握Pyqt5的三种主窗口 快速掌握Pyqt5的2种弹簧 快速掌握Pyqt5的5种布局 快速弄懂Pyqt5的5种项目视图&#xff08;Item View&#xff09; 快速弄懂Pyqt5的4种项目部件&#xff08;Item Widget&#xff09; 快速掌握Pyqt5的6种按钮 快速掌握Pyqt5的10种容器&…...

遥控器android设备键值原理

输入设备触发事件发送数据-》将键值映射到内核中预定义的键值-》上报键值&#xff0c;通过kl文件将按键码转化为标签字符串 内核获取键码&#xff0c;扫描码 按键标签其实对应的也是一个按键码。与kernel上报的按键码不同&#xff0c;按键标签所对应的按键…...

零基础也想学编程?Java零基础入门学习路线 + Java教程已准备好!

本文作者&#xff1a;程序员鱼皮 免费编程学习 - 编程导航网&#xff1a;https://www.code-nav.cn 符号表 可以通过路线知识点前的表情字符&#xff0c;根据自己的实际情况选择学习&#xff1a; &#x1f315; 所有同学必须学习&#xff01;&#xff01;&#xff01;&#x1…...

Avnet ZUBoard 1CG开发板上手—深度学习新选择

Avnet ZUBoard 1CG 开发板上手—深度学习新选择 摘要 本文主要介绍了 Avnet ZUBoard 1CG 开发板的特性、架构、硬件单元等概念&#xff0c;并对如何使用以太网接口和串口连接开发板进行基本介绍&#xff0c;同时辅以两个应用例程演示其功能。 原文链接&#xff1a; FreakSt…...

C/C++复习 day1

C/C复习 day1 文章目录 C/C复习 day1前言一、C语言1.memcpy函数2.memmove函数3.strstr函数4.宏定义的函数5.大小端的介绍以及判断 二、C入门基础1.C是如何支持函数重载的&#xff1f;2.建议用const enum inline去替代宏 三、C类和对象1.类大小的计算2.移动构造和移动赋值1.右值…...

再见Figma!!新的设计,代码协作神器!【送源码】

软件介绍 Penpot 是一款专门用来帮助设计师和开发者更好地合作的软件。它可以让设计师轻松地做出漂亮的设计稿&#xff0c;还能让这些设计稿变成真正的网站或者应用的一部分。这样&#xff0c;设计师和开发者之间就不会因为沟通不畅而产生麻烦了。 Penpot 专为设计师与开发者之…...

快速拷贝复制工具软件@拷贝工具@多线程拷贝@robocopy

文章目录 refs常见复制工具高速拷贝工具特性对比 Robocopy&#x1f47a;Robocopy工具基本用法语法示例 常用选项常见选项列表示例 高级用法多线程复制日志记录 用例案例直接递归复制大量文件的文件夹多线程复制监视被打开文件文件数 复制时排除某个目录排除交接点跳过无法复制的…...

JavaScript 逆向爬取实战

准备介绍&#xff1a; 当我们学习完整个 JS 逆向技巧后&#xff0c;这里是一次完整的分析爬取实战 案例介绍 本节案例网站不仅在 API 参数有加密&#xff0c; 而且前端 JS 也带有压缩混淆&#xff0c;其前端压缩打包工具使用 webpack , 混淆工具使用 javascript-obfuscator 。…...

Vue 项目中导入文件时如何默认找寻该文件夹下的 index.vue 文件

文章目录 需求分析 需求 如下图&#xff0c;在Vue 项目中导入 frequencyChange 文件夹时如何默认找寻该文件夹下的 index.vue 文件 分析 确保项目结构和命名约定 首先&#xff0c;确保你的 Vue 单文件组件按照约定命名&#xff0c;例如&#xff1a; components/Example/inde…...

Idea2023.3.3 —— SourceTree与gitee关联

SourceTree SourceTree链接: https://pan.baidu.com/s/1oqPxhpHeNOOiuRRQydes6g?pwdngru 提取码: ngru 点击Generate 分别保存私钥和公钥 gitee官网注册 这是gitee的公钥&#xff0c;与上面SourceTree的公钥私钥不一样 gitee生成公钥&#xff0c;确保本地安装好git git链接: h…...

一文HDMI (High-Definition Multimedia Interface)

HDMI&#xff08;High-Definition Multimedia Interface&#xff0c;高清多媒体接口&#xff09;是一种紧凑的音视频接口&#xff0c;它能够将未压缩的视频数据以及压缩或未压缩的数字音频数据&#xff0c;从符合HDMI标准的源设备无缝传输到兼容的计算机显示器、视频投影仪、数…...

【HBZ分享】高并发下如何设计缓存来提升系统性能?

普通模式 普通模式即前段调用后端接口&#xff0c;然后后端先查缓存&#xff0c; 查不到的情况下再查数据库&#xff0c;然后把数据库中的内容放到缓存中。瓶颈&#xff1a;瓶颈在于tomcat的性能&#xff0c;一般并发可以&#xff0c;面临海量并发冲击&#xff0c;tomcat就显得…...

【AI 绘画】 文生图图生图(基于diffusers)

AI 绘画- 文生图&图生图&#xff08;基于diffusers&#xff09; 1. 效果展示 本次测试主要结果展示如下&#xff1a; SDXL文生图 可爱Lora 2. 基本原理 模型基本原理介绍如下 stable diffusion首先训练一个自编码器&#xff0c;学习将图像数据压缩为低维表示。通过使…...

已解决HarmonyOS模拟器卡顿问题

以下是一些可以尝试用来解决 HarmonyOS 模拟器卡顿问题的方法&#xff1a; 一、检查系统资源占用 关闭不必要的后台程序 在电脑上&#xff0c;通过任务管理器&#xff08;Windows 系统中按 Ctrl Shift Esc&#xff0c;Mac 系统通过活动监视器&#xff09;查看并关闭占用大量 …...

C++ | 深入理解C++中的特殊类设计和单例模式(懒汉模式、饿汉模式)

目录 特殊类设计和单例模式 1、不可拷贝类 2、只能在堆上创建对象的类 3、只能在栈上创建对象的类 4、不可继承的类 5、单例模式(懒汉模式、饿汉模式) 特殊类设计和单例模式 在C编程中&#xff0c;类的设计往往需要满足特定的需求和约束。特殊类设计模式提供了一种方法来…...

Java设计模式之中介者模式

Java设计模式之中介者模式 在软件开发中&#xff0c;设计模式是解决常见问题的最佳实践。通过运用设计模式&#xff0c;我们可以提高代码的可维护性、可扩展性以及可读性。今天&#xff0c;我们将探讨一种非常重要的行为型设计模式——中介者模式&#xff08;Mediator Pattern…...

实现父组件调用子组件方法时报错:[Vue warn]: Invalid vnode type when creating vnode: null.

使用uniapp实现父组件调用子组件方法时报错&#xff1a;[Vue warn]: Invalid vnode type when creating vnode: null. 实现代码如下&#xff1a; 子组件&#xff1a; <template><view><view class"toolsHeader"><view class"toolsTitl…...

龙华网站建设招聘/网站推广策划思路的内容

在2019腾讯全球数字生态大会新闻发布会上&#xff0c;腾讯云联合腾讯研究院&#xff0c;共同发布了行业重磅报告&#xff1a;《产业互联网——构建智能时代数字生态新图景》。报告首次阐述了产业互联网的战略框架和实践方法论。报告指出&#xff0c;产业互联网的实现&#xff0…...

企业宣传如何做网站/网站开发是做什么的

ylbtech-OpenSource:WebForm-博客园-1.0-账户模块(Passport)-登录与注册大伙闲暇无聊&#xff0c;于是有人建议咱们自己做一个博客园吧&#xff01;于是对我们天天使用&#xff08;甚至朝夕相伴&#xff0c;为啥&#xff1f;大多人想多写几篇好文章&#xff0c;一是分享一下心得…...

品牌网站建设定位/腾讯中国联通

Java反射机制Java反射(Reflection)是Java非常重要的动态特性&#xff0c;通过使用反射我们不仅可以获取到任何类的成员方法(Methods)、成员变量(Fields)、构造方法(Constructors)等信息&#xff0c;还可以动态创建Java类实例、调用任意的类方法、修改任意的类成员变量值等。Jav…...

商丘网站公司电话号码/seo公司的选上海百首网络

指针指针定义指针变量进行函数传参——交换两个变量的值指针变量进行函数传参——数组作函数参数数组形参的定义指针定义 p是一个指针&#xff0c;代表的是存储的值的地址&#xff1b; *p是存储在地址中的值。 //创建变量a&#xff0c;将值1存储在该变量中 int a 1; //创建变…...

张家港网站建设做网站/成人职业技能培训班

文件上传是我们会经常用到的一个业务&#xff0c;其实在h5中新增了FormData的对象&#xff0c;用它来提交表单&#xff0c;并且可以提交二进制文件&#xff0c;所以今天就写写文件上传&#xff0c;希望可以对大家有帮助 FormData 上传文件实例 首先看一下formData的基本用法&am…...

物联网应用技术是干什么的/网站seo推广排名

paper weekly这篇文章最初出现在Thorben Janssen的Java EE博客上&#xff0c;每周都会发布Java新闻&#xff1a; Thoughts-on-java.org 。 使用Java 8的Optional的理想方法仍然是广泛讨论的话题。 三周前 &#xff0c;我们在Stephen Colebourne上发表了一篇有趣的文章&#xf…...