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

news2024/11/10 17:02:05

赋值表达式

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

如下的代码

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2035068.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智能建筑系统,实现智慧城市的可持续发展

智能建筑系统是指通过现代技术和通信技术&#xff0c;对建筑系统进行全方位、智能化的管理和控制。该系统可以通过各种传感器、安全监控系统和计算机设备对工程建筑的内外环境进行认知和控制&#xff0c;进而监控和管理建筑工程设备和信息。 智能建筑系统可以调节室温、湿度等环…

基于Hadoop的汽车大数据分析系统设计与实现【爬虫、数据预处理、MapReduce、echarts、Flask】

文章目录 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主项目介绍爬虫数据概览HIve表设计Cars Database Tables1. cars_data2. annual_sales_volume3. brand_sales_volume4. city_sales_volume5. sales_volume_by_year_and_brand6. sales_distribu…

Mysql的完整性约束

主键约束&#xff1a;一个表中只有一个主键&#xff0c;通过主键找到唯一的记录。主键不能为空不能重复。 CREATE TABLE s1&#xff08;id TINYINT PRIMARY KEY UNSIGNEDINT AUTO_INCREAMENT,name VARCHAR(20) NOT NULL UNIQUE ,age TINYINT DEFAULT 18&#xff09;;…

镜像仓库认证信息加密初始化脚本

文章目录 一、场景说明二、脚本职责三、参数说明四、操作示例五、注意事项 一、场景说明 本自动化脚本旨在为提高研发、测试、运维快速部署应用环境而编写。 脚本遵循拿来即用的原则快速完成 CentOS 系统各应用环境部署工作。 统一研发、测试、生产环境的部署模式、部署结构、…

Python爬虫——爬取bilibili中的视频

爬取bilibili中的视频 本次爬取&#xff0c;还是运用的是requests方法 首先进入bilibili官网中&#xff0c;选取你想要爬取的视频&#xff0c;进入视频播放页面&#xff0c;按F12&#xff0c;将网络中的名称栏向上拉找到第一个并点击&#xff0c;可以在标头中&#xff0c;找到…

【研发日记】嵌入式处理器技能解锁(三)——TI C2000 DSP的C28x内核

文章目录 前言 背景介绍 C28x内核 浮点单元(FPU) 快速整数除法单元(FINTDIV) 三角数学单元(TMU) VCRC单元 CPU总线 指令流水线 总结 参考资料 前言 见《【研发日记】嵌入式处理器技能解锁(一)——多任务异步执行调度的三种方法》 见《【研发日记】嵌入式处理器技能解…

Linux--应用层自定义协议与序列化(例子:网络计算器)

目录 0.上篇文章 1.应用层 再谈一谈协议 网络版计算器 序列化 和 反序列化 2.重新理解 read、 write、 recv、 send 和 tcp 为什么支持全双工 3.网络计算器&#xff08;代码实现) 3.1序列化&反序列化的接口 3.2 项目逻辑 3.3 代码 3.3.1辅助库 3.3.2 基于TCP的…

非线性RCD负载:电力系统的智能管理

随着科技的不断发展&#xff0c;电力系统的规模日益扩大&#xff0c;复杂性也越来越高。在这种背景下&#xff0c;非线性RCD负载&#xff08;Resistive-Capacitive-Inductive load&#xff09;的出现&#xff0c;对电力系统的智能管理提出了新的挑战。非线性RCD负载是指由电阻、…

【学习笔记】Day 10

一、进度概述 1、《地震勘探原理》第三章 二、详情 3.1 野外工作概述 主要介绍地上与海上两种情况下的测量方法&#xff0c;这里不做详解&#xff0c;需要就看书。 其中简况分为&#xff1a;试验工作&#xff0c;生产工作过程&#xff0c;干扰波调查&#xff0c;干扰…

thinkphp8反序列化分析

thinkphp8反序列化 前言 摆了一个暑假&#xff0c;正好看见周会有人分析了tp反序列化&#xff0c;想起这条链子的发现者就是我尊敬的nivia&#xff0c;这不得好好分析一下&#xff0c;而且师傅也是分析了这个&#xff0c;所以有了这个文章 链子一 __call触发 分析 相比于我…

SpringSecurity+前端项目+redis完成认证授权的代码

1. 前端准备工作--都在全局main.js页面中设置的 1.1. 创建Vue工程后&#xff0c;并导入element ui和axios&#xff0c;添加连接后端项目的路径&#xff0c;把axios挂载到Vue 1.2. 前置路由守卫&#xff08;所有路由跳转前核实一下身份&#xff09; //前置路由守卫--所有的路由…

C++密码管理器

先问一句 最近有几个关注我的原力等级为0或-1&#xff0c;文章全是转载&#xff0c;转载时间基本都在2021年&#xff0c;而且关注了很多人&#xff0c;这些是僵尸粉吗&#xff1f; 文末有投票&#xff0c;麻烦参与一下谢谢 实现功能列表 暂时还没做加密功能 打算用openssl/a…

C++ STL初阶(9):list 中关于reverse_iterator的实现

在完成vector和list的iterator相关部分的实践后来完成反向迭代器的实现 1. list的反向迭代器 书接上回&#xff0c;反向迭代器应当重新封装一个类。 反向迭代器和正向迭代器最大的区别就是&#xff0c;反向迭代器是倒着走的&#xff0c;所以最核心的逻辑就是将封装成-- 注意&am…

数字化转型-成就智慧智慧企业

数字化转型是企业迈向智慧化发展的关键路径&#xff0c;通过将先进的数字技术融入企业核心业务&#xff0c;构建智能化、数据驱动的运营模式&#xff0c;实现业务的全面升级与优化。智慧企业的实现依托于几个核心要素&#xff1a;首先是数字基础设施的建设&#xff0c;包括云计…

浅述TSINGSEE青犀EasyCVR视频汇聚平台与海康安防平台的区别对比

在我们的很多项目中都遇到过用户的咨询&#xff1a;TSINGSEE青犀EasyCVR视频汇聚平台与海康平台的区别在哪里&#xff1f;确实&#xff0c;在安防视频监控领域&#xff0c;EasyCVR视频汇聚平台与海康威视平台是两个备受关注的选择。它们各自具有独特的功能和优势&#xff0c;适…

RSS 源:在信息洪流中找回你的时间掌控权

简单介绍了 RSS 后&#xff0c;那么关键的一步就是建立好自己的 RSS 源了。 并不是所有平台都会提供 RSS 源&#xff0c;因此我们也没办法直接去订阅。 目前使用 RSS 的难题之一就是 RSS 源的匮乏&#xff0c;是无数人重新拥抱 RSS 的第一大障碍。 那么&#xff0c;如何去找…

全球化浪潮下的数据库革新:嘉里物流 TiDB 实践价值的设想

导读 本文来自 TiDB 社区武汉站——嘉里物流架构团队负责人肖飞老师的演讲《嘉里物流 & TiDB 在全球化业务场景中应用设想》。本次分享探讨了嘉里物流在全球化扩展中&#xff0c;将如何通过 TiDB 的强大功能应对海量数据挑战&#xff0c;优化技术架构&#xff0c;并提升决…

Adaptive Subgraph Neural Networkwith Reinforced Critical Structure Mining

1 Introduction graph mining area: 图挖掘领域 图具有广泛的局部结构&#xff1a;从节点、模体&#xff08;motifs&#xff09;到子图&#xff08;subgraph&#xff09; 主流研究表明&#xff1a;图的重要特征和突出模式是通过主要由一些关键局部结构&#xff08;如模体和子图…

html+css 实现hover 翻转按钮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽效果&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目…

NCL的安装和运行;气象数据可视化;散点图、直方图、等值线图、箭头图、任意折线和任意图形、非规则网格、图形叠加、组图的绘制

NCAR Command Language&#xff08;NCL&#xff09;是由美国大气研究中心&#xff08;NCAR&#xff09;推出的一款用于科学数据计算和可视化的免费软件。它有着非常强大的文件输入和输出功能&#xff0c;可读写netCDF-3、netCDF-4 classic、HDF4、binary、ASCII数据&#xff0c…