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等编程技术的技巧经验分享),若作品对您有帮助,请关注、分享、点赞、收藏、在看、喜欢,您的支持是我们为您提供帮助的最大动力。

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值