02、宏定义、数组指针、关键字、存储空间

目录

一、编译、宏定义

二、数组与指针

三、关键字(volatile、static、const、Union、#define)

四、C语言的存储空间​编辑


一、编译、宏定义

1、高级语言源程序生成机器代码的过程

预处理 ——> 编译 ——> 汇编 ——> 链接

预处理:#define的宏定义、#include的文件包含、#ifdef/#ifndef, #endif, #if/#else的条件编译。

编译:将代码转化为汇编代码

汇编:将汇编代码转化为目标文件(二进制文件)

链接:将目标文件转换为可执行文件

2、linux环境下的开发板执行

1、交叉编译:arm-linux-gcc Hello.c -o Hello(直接转换为可执行文件)

2、修改属性:chmod 111 Hello(111表示可执行,777表示可读写可执行)

3、执行可执行文件:./Hello

3、#include <file.h> 与 #include “file.h”的区别?

1、#include <file.h>直接从编译器自带的函数库中寻找文件;
2、 #include “file.h”先从自定义的文件中找,如果找不到再从函数库中寻找文件

4、.h头文件中的ifndef/define/endif 的作用?

防止该头文件被重复利用

5、宏定义的应用

(1)用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题)(ul:无符号长整型)

#define SECONDS_PER_YEAR    (60 * 60 * 24 * 365)UL

(2)交换两个参数值的“标准”宏.

#define SWAP(a,b)    (a)=(a)+(b); (b)=(a)-(b); (a)=(a)-(b)

(3)写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。

#define MIN(A, B)    ((A)>=(B)) ? (B) : (A)

(4)已知一个数组table,用一个宏定义,求出数据的元素个数

#define NTBL(tab)    (sizeof(tab) / sizeof(tab[0]))

6、嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置 a 的 bit3,第二个清除 a 的bit3。在以上两个操作中,要保持其它位不变。

#define BITS_SET(v) v|=(0x01<<3)
#define BITS_CLR(v) v&=~(0x01<<3)

7、C语言中的死循环

while(1) //方案1
{
    ;
}
for( ;1 ;) //方案2
{
    ;
}
Loop:
…
goto Loop; //方案3

8、不能做 switch() 的参数类型

答:实型(即带小数的都不行)

9、带参的宏的有优缺点

(1)优点:不存在类型转换和运算问题,只是替换,不占运行时间

(2)缺点:程序增多,占编译时间

10、带参函数

(1)优点:程序减少,不占编译时间

(2)缺点:存在类型转换和运算问题,占用占运行时间

11、typedef,#define 定义数据类型,两者有什么不同?哪一种更好一点?

#define在预编译的时候做简单的字符替换处理。

typedef是在编译的时候进行的处理,并不是做简单的字符替换,而是同定义一个变量一样声明一个数据类型,然后用它去定义这种数据类型的变量。

typedef char* pStr1;
#define pStr2 char*
pStr1 s1, s2;
pStr2 s3, s4;

在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。上例中define语句必须写成 pStr2 s3, *s4; 这这样才能正常执行。

二、数组与指针

1、定义变量

a)一个整型数 //int a;
b) 一个指向整型数的指针 //int *a;
c) 一个指向指针的的指针,它指向的指针是指向一个整型数 //int **a;
d) 一个有10个整型数的数组 //int a[10];
e) 一个有10个指针的数组,该指针是指向一个整型数的 // int *a[10];
f) 一个指向有10个整型数数组的指针  // int *a[10];
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数  //int (*a)(int );
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数  //int (*a[10])(int );

2、指针常量和常量指针的区别

const c=10,则不能对c修改

指针常量:int* const p2 = &a;const修饰p2(指针,指针的指向位置不能改变

常量指针:const int *p3,const修饰int(整型变量),不能对值进行修改,

指针常量

char* const p = "hello"; //指针常量
p[0] = 'X'; //正确! 允许修改字符串, 因为该字符串不是常量
p = p2; //错误! 指针是常量, 不许修改p的指向

常量指针 

const char* p = "hello"; //指向 "字符串常量"
p[0] = 'X'; //错误! 想要修改字符串的第一个字符. 但是常量不允许修改
p = p2; //正确! 让p指向另外一个指针.

3、什么叫野指针产生的原因,如何规避

答:野指针就是 指针指向的位置是不可知的。
原因:
1、指针变量未初始化
2、指针释放后之后未置空(指针所指向的变量 在指针之前被销毁)
3、指针操作超越变量作用域
规避:
1、初始化时置 NULL
2、释放时置 NULL

4、数组和指针的区别

数组:在静态存储区被创建(如全局数组),或在栈上被创建。

指针:可以随时指向任意类型的内存块。

5、引用和指针有什么区别?

(1)用必须初始化,指针不必;

(2)引用处初始化后不能改变,指针可以被改变;

(3)不存在指向空值的引用,但存在指向空值的指针;

6、char* s="AAA"; printf("%s",s); s[0]='B'; printf("%s",s); 有什么错?

"AAA"是字符串常量。s是指针,指向这个字符串常量,所以声明s的时候就有问题。 cosnt char* s="AAA"; 然后又因为是常量,所以对是s[0]的赋值操作是不合法的。

三、关键字(volatile、static、const、Union、#define

1、关键字volatile有什么含义?给出三个不同的例子

(1)防止变量被编译器优化。

(2)被volatile修饰的变量,编译器不会去假设该变量的值

(3)当优化器每次用到该变量的值时,都会去变量的原始地址去读取这个变量的值,而不是使用保存在寄存器中的备份值。

例子:

(1)并行设备的硬件寄存器。

(2)一个中断服务子程序中的非自动变量。

(3)多线程应用中被几个线程任务共享的变量。

2、关键字static的作用是什么?(在C语言和c++的作用)

在C语言中
(1)修饰函数、全局变量时,只能在本文件内使用

(2)修饰局部变量时,该变量生命周期延长到程序结束。如果没有被初始化,默认为0,若已被初始化,则只能初始化一次。

在C++中

(1)被static修饰的成员变量在本质上是全局变量,所以需要在类的外部进行定义。

(2)被static修饰的成员函数没有this指针,可以通过类名::函数名进行调用

3、static全局变量、局部变量、函数与普通的全局变量、局部变量、函数有什么区别?

(1)全局变量:static修饰只能初使化一次,且不能在其他文件单元中被引用;

(2)局部变量: static修饰只能初始化一次,下一次依据上一次结果值,生命周期从程序开始到程序结束。

(3)函数:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝。

4、关键字const有什么含意?

(1)可以定义const常量

(2)const可以修饰函数的参数、返回值,甚至函数的定义体。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

5、Union与struct的区别。(故而常用struct)

1.在存储多个成员信息时,编译器会自动给struct每个成员分配存储空间,struct 可以存储多个成员信息,而Union每个成员会用同一个存储空间,只能存储最后一个成员的信息。

2.都是由多个不同的数据类型成员组成,但在任何同一时刻,Union只存放了一个被先选中的成员,而结构体的所有成员都存在。

3.对于Union的不同成员赋值,将会对其他成员重写,原来成员的值就不存在了,而对于struct 的不同成员赋值 是互不影响的。

6、请说出const与#define 相比,有何优点?(故而常用const)

1const定义的是只读变量,#define为宏替换

2const不会改变变量的存储位置,#define定义的宏存储在代码段

3const 常量有数据类型,而宏常量没有数据类型。

4、编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误

7、定义宏时需要注意什么?

(1) 在定义#define 命令时,注意<宏名>和<字符串>之间用空格分开,而不是用等号连接。

(2) 使用#define定义的标识符不是变量,它只用作宏替换,因此不占有内存。   

(3) 习惯上用大写字母表示<宏名>

(4) 定义宏函数时不能有数据类型。


四、C语言的存储空间

静态区也叫做bss段,常量区也叫做初始化数据区域

1.程序的内存分配答:
1)栈区(stack)----由编译器自动分配释放,存放函数的参数值,局部变量等。
2)堆区(heap)----一般由程序员分配释放。分配使用new和malloc,释放使用deleted和free

3)全局区(静态区)(static)----全局变量和静态娈量是存放在一块的。初始化的在一块区域,未初始化存放在另一块区域(BSS)。
4)常量区----存放常量字符串。
5)程序代码区----存放函数体的二进制代码。

1.全局变量和局部变量在内存中是否有区别?如果有,是什么区别?

1)全局变量储存在静态数据区,局部变量在堆栈中。
2)全局变量的作用域是整个函数,局部变量的作用域是声明该变量的函数


⒉局部变量能否和全局变量重名?

能,局部会屏蔽全局。要用全局变量,需要使用"::"


3.全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?

可以,在不同的C文件中以static形式来声明同名全局变量。只能有一个C文件中对此变量赋初值,此时连接不会出错。


4.如何引用一个已经定义过的全局变量?

用extern关键字在头文件中声明全局变量。

5、堆和栈的区别

Heap是堆,Stack是栈。

  (1)栈的空间由操作系统自动分配和回收,而堆上的空间由程序员申请和释放。

  (2)栈的空间大小较小,而堆的空间较大。

  (3)栈的地址空间往低地址方向生长,而堆向高地址方向生长。

  (4)栈的存取效率更高。程序在编译期间对变量和函数的内存分配都在栈上,

    且程序运行过程中对函数调用中参数的内存分配也是在栈上。

五、字节

1、32位机器和64位机器

2、char和unsigned char的区别

char能表示-128~127, unsigned char没有符号位,因此能表示0~255

猜你喜欢

转载自blog.csdn.net/weixin_45981798/article/details/129934503