一、变量篇

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^312^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. PLSQL 登录时弹出(没有登录)空白提示框

    如题,在登录的时候莫名出现了plsql登录时弹出(没有登录)的空白提示框,在网上找了很多方法之后都不行,然后发现plsql的 oracle主目录名莫名的成了空,然后直接重新把它的目录设置上 重启pls ...

  2. JAVA WEB面试总结

    本文目录: 1. 什么是cookie 2. 什么是session 3.什么是Servlet,Servlet生命周期方法 4.JSP隐含对象 5.JSP的四个域对象的作用范围 6.转发和重定向的区别 7 ...

  3. pom.xml管理jar包——安全性框架配置文件

    <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> &l ...

  4. Flask路由系统

    Flask路由系统 我们之前了解了路由系统是由带参数的装饰器完成的. 路由本质:装饰器和闭包实现的. 设置路由的两种方式 第一种: @app.route('/index') def index(): ...

  5. JavaScript面向对象①

    什么是对象 对象是一个整体,对外提供一些操作 什么是面向对象 使用对象时,只关注对象提供的功能,不关注其内部细节:比如jQuery 面向对象编程(OOP)的特点(自己理解的特点:把书本上多态放在类继承 ...

  6. 微信小程序开发--常用开发实例

    一.常用商品列表的换行排布 <view class="box_max"> <view class="box_min">限时秒杀</ ...

  7. iOS 原生推送(APNS)的实现

    1.前期准备工作 创建你的APNs keys 或者 创建推送证书,这两个创建一个即可实现推送.这两个创建一个即可实现推送.这两个创建一个即可实现推送.重要的事情说三遍,我看评论区有小伙伴误解. 1.  ...

  8. Elasticsearch高版本安装head插件

    安装Elasticsearch 1.安装Elasticsearch-6.5.4.tar.gz [merce@info5 ~]$ cd /appmerce/zrapp/ [merce@info5 zra ...

  9. 第一部分day1-变量、运算

    变量:为了存储程序运算过程中的一些中间 结果,为了方便日后调用常量:固定不变的量,字符大写 变量的命名规则 1.字母数字下划线组成2.不能以数字开头,不能含特殊字符和空格3.不能以保留字命名4.不能以 ...

  10. Alipay支付宝调用错误:Call to undefined function openssl_sign()

    打开php.ini,找到这一行 ;extension=php_openssl.dll,将前面的“;”去掉: 重启服务器.