一、变量篇

1 全局变量和静态变量有什么异同?

相同:都在静态存储区分配空间,生命周期与程序生命周期相同。

区别:全局变量的作用域是整个程序,它只需要在一个源文件中定义,就可以作用于所有的源文件。而静态变量只在定义其的源文件内有效。

2 变量定义与变量声明有什么区别?

定义(definition)为变量分配存储空间,还可以为变量指定初始值。而声明(declaration)是指向程序表明变量的类型和名字。 定义也是声明,定义变量的同时也声明了它的类型和名字。一般为了叙述方便,把建立存储空间的声明称定义,而不把建立存储空间的声明称为声明。

3 C 语言中各种变量的默认初始值是什么?

全局变量放在内存的全局数据区,如果在定义的时候不初始化,则系统将自动为其初始化,数值型为0,字符型为空,指针变量也被赋值为NULL。静态变量的情况与全局变量类似。而非静态局部变量如果不显示初始化,那么其内容是不可预料的,将是随机数,会很危险,对系统的安全造成非常大的隐患。

4 如何判断一段程序是 C 编译程序还是 C++ 编译程序编译的?

如果编译器在编译 cpp 文件,那么 _cplusplus 就会被定义,如果是一个 C 文件在被编译, 那么 _STDC_ 就会被定义。_STDC_ 是预定义宏,当它被定义后,编译器将按照 ANSIC 标准来编译 C 语言程序。所以可以采用如下程序进行判断:

#ifdef _cplusplus
#define USING_CPP 1
#else
#define USING_CPP 0
#endif #include <stdio.h> int main()
{
if(USING_CPP)
printf("C++\n");
else
printf("C\n"); return 0;
}

5 C 语言中 int 和 float 有什么区别?

主要有如下三个区别:

(1)表示的数据范围不同。C 语言中的 int 变量通常的表示范围为-2147483648~2147483647,也就是-2^31到2^31之间。而-3.4E+38 ~ 3.4E+38则是 float 类型表示的数据范围。float 表示的数据范围要大于 int 表示的数据范围。

(2)变量赋值方法不同。C 语言中,将 i 设定为一个 int 变量并赋值的方法为:int i=xx;,其中 xx 为一个整数,例如 3、4、5,不可以是小数。将 i 设定为一个 float 变量的方法为:float i=yy;,其中 yy 为一个浮点型数,可以带上小数点,例如 3.0、4.5、5.7 等等。

(3) 字节构成不同。int 和 float 类型在计算机中都占 4 个字节,但是 float 类型的 4 个字节构成为包括一个符号位、一个 8 位二进制指数和一个 23 位尾数, 以指数形式存储,而 int 类型的 4 个字节构成全部为整数。

扩展: 为什么有些int是float表示不了的呢?

因为 int 与 float 同样占4个字节,float 表示的范围又比 int 大并且还包含很多小数,那 int 的每个值都能被 float 表示就是不可能的事情了。

参考:int、unsigned int、float、double和char在内存中存储方式

6 int为什么在32位系统中是4个字节?

32 位系统对应的 CPU 是32位的,为了和 CPU 的字宽一致,提高处理速度。

7 为什么一个指针在32位系统中占4个字节,在64位系统中占8个字节?

可以参考:

为什么一个指针在32位系统中占4个字节,在64位系统中占8个字节?

二、C/C++ 关键字

1 const 有哪些作用?

(1)定义常量,使其值不可被修改,另外使编译器可以对其进行类型检查;

(2)修饰函数形参,防止值被意外的修改,提高程序的健壮性;

(3)修饰函数返回值,使返回值不能被修改;

(4)修饰常量指针(const char *p)和指针常量(char * const p);

(5)在 C++ 中,修饰类成员函数,任何不会修改数据成员的函数都应该用 const 修改,以及修饰类成员数据。

2 extern 有哪些作用?

extern 有两种作用,下面分别详细介绍一下。

1. 在模块外使用全局变量

extern 可以置于变量或者函数前,如在头文件中:extern int g_val;,其声明的函数和变量可以在其他模块中使用,记住它是一个声明不是定义。也就是说 B 模块(编译单元)要是引用模块(编译单元) A 中定义的全局变量或函数时,它只要包含 A 模块的头文件即可。

2. 在 C++ 环境下使用 C 函数

C++ 语言是一种面向对象编程语言,支持函数重载,而 C 语言是面向过程的编程语言, 不支持函数重载,所以函数被 C++ 编译后在库中的名字与 C 语言的不同。如果声明一个 C 语言函数float f(int a,char b),C++ 的编译器就会将这个名字变成像 _f_int_char 之类的东西以支持函数重载。然而 C 语言编译器的库一般不执行该转换,所以它的内部名为 _f,这样连接器将无法解释 C++ 对函数 f() 的调用。

C++ 提供了 C 语言符号extern “C”来解决名字匹配问题,extern 后跟一个字符串来指定想声明的函数的连接类型,后面是函数声明。

extern "C" float f(int a,char b);

该语句的目的是告诉编译器 f() 是 C 连接的,这样 C++ 就不会转换函数名。应该到库中找名字 _f 而不是找 _f_int_char。 C++ 编译器开发商已经对 C 标准库的头文件作了 extern “C” 处理,所以可以用 include 直接引用这些头文件。

3 static 变量有哪些作用?

(1)在函数体内,被声明为静态的变量只初始化一次,以后该函数再被调用,将不会再初始化,这就使变量具有 “记忆” 功能。

(2)在模块内(但在函数体外),如果把一个变量或者函数声明为静态的,那么可以将其作用域被限制在本模块内,起一个 “隐藏” 的作用,避免命名冲突。

(3)默认初始化为 0,因为静态变量存储在静态数据区,而静态数据区中的所有字节默认值都是 0,某些时候这一特点可以减少程序员的工作量。比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加 ‘\0’ 太麻烦。如果把字符数组定义成静态的,就省去了这个麻烦。(全局变量也存储在静态数据区)

(4)在 C++ 中,在类中声明 static 变量或者函数。其初始化时使用作用域运算符来标明它所属类,因此,静态数据成员是类的成员,而不是对象的成员,这样就出现以下作用:

  • 类的静态成员函数是属于整个类而非类的对象,所以它没有 this 指针,这就导致了它仅能访问类的静态数据和静态成员函数;
  • 不能将静态成员函数定义为虚函数;
  • 由于静态成员函数没有 this 指针,所以就差不多等同于 nonmember 函数,结果就产生了一个意想不到的好处:成为一个 callback 函数,使得我们得以将 C++ 和 C-based X Window 系统结合,同时也成功的应用于线程函数身上。

4 new/delete 与 malloc/free 的区别是什么?

(1)new/delete 是操作符,而 malloc/free 是函数,在C语言中需要 <stdlib.h> 的支持;

(2)new 能够自动计算需要分配的内存空间,而 malloc 需要手工计算字节数;

(3)new 与 delete 直接带具体类型的指针,而 malloc 与 free 返回的是 void 类型的指针;

(4)new 是类型安全的,而 malloc 不是,例如int* p = new float[2],编译时就会报错; 而int* p = malloc(2 * sizeof(float)),编译时编译器就无法指出错误来;

(5)new 将调用构造函数,而 malloc 不能;delete 将调用析构函数,而 free 不能。

5 断言 ASSERT() 是什么?

ASSERT() —般被称为断言,它是一个调试程序时经常使用的宏。它定义在 <assert.h> 头文件中,通常用于判断程序中是否出现了非法的数据,在程序运行时它计算括号内的表达式的值。如果表达式的值为 false(0),程序报告错误,终止运行,以免导致严重后果,同时也便于查找错误;如果表达式的值不为 0,则继续执行后面语句。其用法如下:

ASSERT(n != 0); // 分母为0,为非法数据,表达式为false,程序报告错误,程序会终止运行
k = 10 / n;

需要注意的是,ASSERT()只在 Debug 版本中有,编译的 Release 版本则被忽略。还需要注意的一个问题是 ASSERT() 与 assert() 的区别,ASSERT() 是宏,而 assert() 是ANSI C标准中规定的函数,它与 ASSERT() 的功能类似,但是可以应用在 Release 版本中。

三、C/C++ 运算符

1 前置运算与后置运算有什么区别?

以 ++ 操作为例,对于变量 a, ++a 表示先增加内存中 a 的值,然后再把值放在装入寄存器中;a++ 表示先把 a 的值装入寄存器,然后再增加内存中 a 的值。

一般而言,当涉及表达式计算时,++a 是先将值增加 1,再返回其值;而 a++ 是先返回其值,再增加1。

2 a是变量,执行 (a++) += a 语句是否合法?

不合法。a++ 不能当做左值使用。++a 可以当左值使用。a++ 是先把a的值装入寄存器,然后再增加内存中 a 的值,此时左值是 a 的值,而值不能作为左值,所以非法。而 ++a 是先增加内存中 a 的值,然后再把值放在装入寄存器中,此时左值是 a,所以合法。

3 *p++与(*p)++等价吗?为什么?

因为优先级顺序的问题,*p++与(*p)++并不等价,前者先完成取值操作,然后对指针地址执行 ++ 操作;而后者先完成取值操作,然后对该值进行 ++ 运算。

四、位操作

1.1 一些结构声明中的冒号和数字是什么意思?

C 语言的结构体可以实现位段,它的定义形式是在一个定义的结构体成员后面加上冒号, 然后是该成员所占的位数。位段的结构体成员必须是 int 或者 unsigned int 类型,不能是其他类型。位段在内存中的存储方式是由具体的编译器决定的。

示例程序如下:

#include <stdio.h> 

typedef struct
{
int a:2;
int b:2;
int c:l;
}test; int main()
{
test t; t.a = 1; 
t.b = 3;
t.c = 1;
printf("%d %d %d %d\n",t.a, t.b, t.c, sizeof(test)); // 1 -1 -1 4 return 0;
}

由于 a 占两位,而 a 被赋值为 1,二进制就是 01,因此 %d 输出的时候输出 1;b 也占了两位,赋值为 3,二进制也就是 11,由于使用了 %d 输出,表示的是将这个 b 作为有符号 int 型来输出,这样的话二进制的 11 将会有一位被认为是符号位,并且两位的 b 也会被扩展为 int 类 型,也就是4字节,即 32 位。

1.2 如何实现位操作求两个数的平均值?

一般而言,求解平均数的方法就是将两者相加,然后除以 2,以变量 x 与 y 为例,两者的平均数为 (x+y)/2。

但是采用上述方法,会存在一个问题,当两个数比较大时,如两者的和大于了机器位数能够表示的最大值,可能会存在数据溢出的情况,而采用位运算方法则可以避免这一问题,而且位运算相比除法运算, 效率更高。

示例程序如下:

#include <stdio.h>

int main()
{
int x = 2147483647, y = 2147483647; printf("%d\n",(x+y)/2); // -1
printf("%d\n",(x&y)+((x^y)>>1)); // 2147483647 return 0;
}

1.3 如何求解整型数的二进制表示中 1 的个数?

方法一,程序代码如下:

#include <stdio.h> 

int func (int n)
{
int count=0;
while (n)
{
count += n & 0x1u ;
n >>= 1 ;
} return count;
} int main()
{
printf("%d\n",func(9999)); // 8 return 0;
}

判断每个数的二进制表示中每一位是否为 1,如果为 1,就在 count 上加 1,而循环的次数是常数,即 n 的位数。但该方法有一个缺陷,就是在 1 比较稀疏的时候效率会比较低。

方法二,程序代码如下:

#include <stdio.h> 

int func(int x)
{
int countx = 0;
while(x)
{
countx++;
x = x&(x-1);
} return countx;
} int main()
{
printf("%d\n", func(9999)); // 8 return 0;
}

为了理解这个算法的核心,需要理解以下两个操作:

(1)当一个数被减 1 时,它最右边的那个值为 1 的 bit 将变为 0,同时其右边的所有的 bit 都会变成 1;

(2)“&=”,位与并赋值操作。去掉已经被计数过的 1,并将该值重新设置给 n。这个算法循环的次数是 bit 位为 1 的个数,对 bit 为 1 比较稀疏的数来说,性能很好。

1.4 不能用 sizeof() 函数,如何判断操作系统是 16 位还是 32 位的?

方法一:一般而言,机器位数不同,其表示的数字的最大值也不同,根据这一特性,可以判断操作系统的位数。例如,运行如下代码:

#include <stdio.h> 

int main()
{
int i = 65536;
printf("%d\n",i); // 16位机器下输出0,32位机器下输出65536
int j = 65535;
printf("%d\n",j); // 16位机器下输出-1,32位机器下输出65535 return 0;
}

由于 16 位机器下,能够表示的最大数为65535,会出现越界情况。而在 32 位机器下,则不会出现溢出的情况,所以输出为正常输出。

方法二:对 0 值取反,不同位数下的 0 值取反,其结果不一样。运行如下代码:

#include <stdio.h> 

int main()
{
unsigned int a =〜0; if( a>65536)
printf("32 位\11");
else
printf(”16 位\11"); return 0;
}

1.5 嵌人式编程中,什么是大端?什么是小端?

大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中。

小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。

大小端在内存中的存放方式举例

一个 16bit 的 short 型 x,在内存中的地址为 0x0010,x 的值为 0x1122。那么 0x11 为数据高字节,0x22 为数据低字节。

  • 对于大端模式,就将 0x11 放在内存低地址中,即 0x0010 中;0x22 放在内存高地址中,即 0x0011 中。
  • 小端模式,就将 0x11 放在内存高地址中,即 0x0011 中;0x22 放在内存低地址中,即 0x0010 中。

我们常用的 X86 结构是小端模式,而 KEIL C51 则为大端模式。很多的 ARM,DSP 都为小端模式。有些 ARM 处理器还可以由硬件来选择是大端模式还是小端模式。

引申:如何判断计算机处理器是大端还是小端?

方法一:可以通过指针地址来判断,由于在32位计算机系统中,int 占 4 个字节,char 占 1 个 字节,所以可以采用如下做法实现该判断。

int fun()
{
int num = 0x12345678; // (char*)&num获得num的起始地址,*((char*)&num)是num的起始地址所指向的值。
return (*((char*)&num) == 0x12 )?1:0; // 本机返回1,为大端 返回0,为小端
}

方法二:联合体union的存放顺序是所有成员都从低地址开始存放。

// 判断系统是大端还是小端:通过联合体,因为联合体的所有成员都从低地址开始存放
int fun()
{
union test
{
int i;
char c;
}; test t;
t.i = 1; // 如果是大端,则t.c为0x00,则t.c!=1,返回0 是小端,则t.c为0x01,则t.c==1,返回1
return (t.c == 1);
}

[C++基础] 变量、关键字、运算符、位操作篇的更多相关文章

  1. python基础 - 变量与运算符

    变量与运算符 变量 定义一个变量 a = [1,2,3,4,5,6] print(a) # [1,2,3,4,5,6] 变量命名要求: 首字母不能是数字 只能包含字符数字下划线 不能是关键字 type ...

  2. 1106SQLserver基础--变量、运算符的使用,if...else,while语句

    数据库---变量(对数据库中的数据没有任何影响) 作用:临时存储数据的作用,起一个衔接的作用,为了方便理解存储过程. 例:Declare @hello varchar(20) Set @hello=’ ...

  3. 快速复习C语言 - 1变量与运算符

    变量与运算符 本篇以读者知道 int.char.float.double 等数据类型为前提条件. float 类型注意事项 float 类型数没有办法跟一个数真正比较是否相等,可以定义借助绝对值在一定 ...

  4. Java之--Java语言基础组成(关键字、标识符、注释、常量和变量、运算符)

    Java语言基础组成-关键字.标识符.注释.常量和变量.运算符 Java语言由8个模块构成,分别为:1.关键字:2.标识符(包名.类名.接口名.常量名.变量名等):3.注释:4.常量和变量:5.运算符 ...

  5. 第二十一节:Java语言基础-关键字,标识符,注释,常量和变量,运算符

    Java语言基础-关键字,标识符,注解,常量和变量,运算符 class Demo { public static void main(String[] args){ System.out.printl ...

  6. java基础基础总结----- 关键字、标识符、注释、常量和变量、运算符、语句、函数、数组(三)

    Java语言基础组成:关键字.标识符.注释.常量和变量.运算符.语句.函数.数组 一.标识符 标识符是在程序中自定义的一些名称,由大小写字母[a-zA-Z],数字[0-9],下划线[ _ ],特殊字符 ...

  7. 数据库基础(变量、运算符、if语句、while语句)

    数据库基础(变量.运算符.if语句.while语句)   变量: 定义变量:declare @变量名 数据类型 变量赋值:set @变量名 = 值 输出:print 变量或字符串 SQL语言也跟其他编 ...

  8. Java基础语法01——变量与运算符

    本文是对Java基础语法的第一部分的学习,包括注释:标识符的命名规则与规范:变量的数据类型分类以及转换:以及六种运算符(算术.赋值.比较.逻辑.三元和位运算符).

  9. Java基础(变量、运算符)

    第2天 Java基础语法 今日内容介绍 u 变量 u 运算符 第1章 变量 1.1 变量概述 前面我们已经学习了常量,接下来我们要学习变量.在Java中变量的应用比常量的应用要多很多.所以变量也是尤为 ...

  10. 【Java基础】基本语法-变量与运算符

    基本语法-变量与运算符 关键字和保留字 关键字定义:被 Java 语言赋予了特殊含义,用做专门用途的字符串(单词). 关键字特点:关键字中所有字母都为小写. 用于定义数据类型:class.interf ...

随机推荐

  1. APS.NET MVC + EF (14)---项目框架搭建

    一:框架搭建     1:先创建Model. 2:创建数据访问接口层.IUserInfoDal 在该接口中定义了常见的方法CURD以及分页方法. public interface IUserInfoD ...

  2. vertx 异步编程指南 step8-使用RxJava进行反应式编程

    vertx 异步编程指南 step8-使用RxJava进行反应式编程 2018-04-23 13:15:32 zyydecsdn 阅读数 1212  收藏 更多 分类专栏: vertx   到目前为止 ...

  3. springboot启动报错,Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.

    报错: Error starting ApplicationContext. To display the conditions report re-run your application with ...

  4. 使用Fiddler监听java HttpURLConnection请求

    使用Fiddler监听java HttpURLConnection请求

  5. MySQL修炼之路一

    1. MySQL概述 1. 什么是数据库 存储数据的仓库 2. 都有哪些公司在用数据库 金融机构.游戏网站.购物网站.论坛网站 ... ... 3. 提供数据库服务的软件 1. 软件分类 MySQL. ...

  6. Linux服务管理之DHCP

    1.DHCP服务简介 DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)是一个局域网的网络协议,使用UDP协议工作, 主要有两个用途:给内部网络或网络 ...

  7. Nginx 高级配置--关于favicon.ico

    Nginx 高级配置--关于favicon.ico 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.浏览器会默认帮咱们访问官网的图标 1>.浏览器访问网站"htt ...

  8. AAC的RTP封装中的AU头分析

      解码器收到一个RTP的AAC流,发现RTP流里的音频里带有4个字节AU头,然后才是AAC的ADTS头.     这种情况之前已经出现过多次,每次我们都告知对方,不要往AAC前面加AU头,解码器不支 ...

  9. Codeforces Good Bye 2016 E. New Year and Old Subsequence

    传送门 题意: 给出一个长度为\(n\)的串,现在有\(q\)个询问,每个询问是一个区间\([l,r]\),要回答在区间\([l,r]\)中,最少需要删多少个数,满足区间中包含\(2017\)的子序列 ...

  10. Django-Model操作数据库

    查询 models.UserInfo.objects.all() models.UserInfo.objects.all().values('user') #只取user列 models.UserIn ...