P11-EXTI外部中断-1.jpg

一、数组和指针有什么联系?

指针就是指针,指针变量在 32 位系统下,永远占 4 个 byte,其值为某一个内存的地址。指针可以指向任何地方,但是不是任何地方你都能通过这个指针变量访问到。

数组就是数组,其大小与元素的类型和个数有关。定义数组时必须指定其元素的类型和个数。数组可以存任何类型的数据,但不能存函数

二、C语言中内存分配的方式有几种

1、静态存储区分配

内存分配在程序编译之前完成,且在程序的整个运行期间都存在,例如全局变量、静态变量等。

2、栈上分配

在函数执行时,函数内的局部变量的存储单元在栈上创建,函数执行结束时这些存储单元自动释放。

3、堆上分配

三、堆和栈的区别

栈:保存局部变量。栈上的内容只在函数的范围内存在,当函数运行结束,这些内容也会自动被销毁。其特点是效率高,但空间大小有限。

堆:由 malloc 系列函数或 new 操作符分配的内存。其生命周期由 free 或 delete 决定。

在没有释放之前一直存在,直到程序结束。其特点是使用灵活,空间比较大,但容易出错。

堆与栈实际上是操作系统对进程占用的内存空间的两种管理方式,主要有如下几种区别:

(1)管理方式不同。栈由操作系统自动分配释放,无需我们手动控制;堆的申请和释放工作由程序员

控制,容易产生内存泄漏;

(2)空间大小不同。每个进程拥有的栈的大小要远远小于堆的大小。

(3)生长方向不同。堆的生长方向向上,内存地址由低到高;栈的生长方向向下,内存地址由高到低。

(4)分配方式不同。堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。栈的分配系统会自动分配,用户也可以申请(通过alloca);

(5)分配效率(速度)不同。栈有底层指令实现,速度快;堆使用C库函数运算来申请和管理,速度慢;

(6)存放内容不同。栈存放的内容,函数返回地址(调用函数的指令的下一条指令)、相关参数、局部变量和寄存器内容等。 注意静态变量是存放在数据段或者BSS段,是不入栈的。 而堆中具体存放内容是由程序员来填充的。

四、static的用法(定义和用途)

1)用static修饰局部变量:使其变为静态存储方式(静态数据区),那么这个局部变量在函数执行完成之后不会被释放,而是继续保留在内存中。

2)用static修饰全局变量:使其只在本文件内部有效,而其他文件不可连接或引用该变量。

3)用static修饰函数:对函数的连接方式产生影响,使得函数只在本文件内部有效,对其他文件是不可见的(这一点在大工程中很重要很重要,避免很多麻烦,很常见)。这样的函数又叫作静态函数。使用静态函数的好处是,不用担心与其他文件的同名函数产生干扰,另外也是对函数本身的一种保护机制。

五、const的用法(定义和用途)

const主要用来修饰变量、函数形参和类成员函数:

1)用const修饰常量:定义时就初始化,以后不能更改。

2)用const修饰形参:func(const int a){};该形参在函数里不能改变

3)用const修饰类成员函数:该函数对成员变量只能进行只读操作,就是const类成员函数是不能修改成员变量的数值的。

被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

下面的声明都是什么意思?

const int a;

int const a;

const int *a;

int * const a;

int const * a const;

前两个的作用是一样,a是一个常整型数。

第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。

第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。

最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。

六、内存四区,什么变量分别存储在什么区域,堆上还是栈上?

在C++中,内存四区指的是程序运行时内存被划分成的四个主要区域,分别是栈区(Stack)、堆区(Heap)、全局/静态存储区(Global/Static)和常量存储区(Constant)。不同的变量类型和声明周期通常存储在不同的区域。

  1. 栈区(Stack):
    • 自动局部变量(auto variable):在函数内部定义的没有指定存储区域的局部变量,如int a;,它们会自动存储在栈上。
    • 函数参数:函数调用时传递的参数也是在栈上分配的。
    • 函数返回地址和局部变量:这些信息在函数调用过程中被存储在栈上。
  2. 堆区(Heap):
    • 动态分配的内存:使用new关键字在C++中或者在C中使用malloccallocrealloc等函数分配的内存都存储在堆上。这些内存直到使用delete(C++)或free(C)显式释放前都不会被自动清理。
    • 对象生命周期由程序员控制:通过new创建的对象,其生命周期不是由作用域决定的,而是由delete操作决定。
  3. 全局/静态存储区(Global/Static):
    • 全局变量:在所有函数体之外定义的变量,程序启动时分配,程序结束时释放。
    • 静态变量:在函数内部或类内部声明为static的变量,它们的存储期限是程序的整个运行期间,而不是函数调用的周期。
  4. 常量存储区(Constant):
    • 字符串常量:如const char* p = "hello world";中的"hello world"是存储在常量区的。
    • const修饰的全局变量:声明为const的全局变量也存储在这里。

不同的内存区域有不同的生命周期和访问特性,合理地使用这些区域对于程序的性能和资源管理都是非常重要的。在程序设计中,应当根据变量的特性和用途选择合适的存储区域。

C++和C语言中的内存分区的区别

C++和C语言在内存管理上有一些相似之处,但也存在差异。在C语言中,内存通常被分为三个主要区域:

  1. 栈区(Stack):用于存储局部变量、函数参数和返回地址等。栈上的内存分配和释放是自动的,由编译器在函数调用和返回时管理。
  2. 堆区(Heap):用于动态内存分配,通过malloccallocrealloc等函数分配的内存位于堆上。堆内存的分配和释放需要程序员手动管理。
  3. 静态存储区(Static/Global):用于存储全局变量和静态变量。这些变量在程序启动时分配,直到程序结束时才释放。

C语言中没有显式的常量存储区概念,而是将常量值直接嵌入到代码中,通常存储在只读的代码段。

相比之下,C++在C的基础上增加了对象的概念,并且引入了类和对象的生命周期管理。C++的内存四区通常指的是:

  1. 栈区(Stack):与C语言相同,用于自动局部变量和函数调用相关的数据。
  2. 堆区(Heap):与C语言相同,用于动态内存分配,但C++使用newdelete关键字进行对象的分配和释放。
  3. 全局/静态存储区(Global/Static):与C语言相同,用于全局变量和静态变量。
  4. 常量存储区(Constant):C++中明确区分了常量存储区,用于存储字面量常量和const修饰的全局变量。

总的来说,C++在内存管理上提供了更丰富的特性,特别是通过类和对象的概念,引入了构造函数和析构函数来管理对象的创建和销毁,这是C语言所没有的。此外,C++还支持局部对象和临时对象的自动存储期,这些都是C++特有的内存管理特性。

七、用变量a给出下面的定义

a) 一个整型数;

b)一个指向整型数的指针;

c)一个指向指针的指针,它指向的指针是指向一个整型数;

d)一个有10个整型的数组;

e)一个有10个指针的数组,该指针是指向一个整型数;

f)一个指向有10个整型数数组的指针;

g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数;

h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数

答案:

a)int a

b)int *a;

c)int **a;

d)int a[10];

e)int *a [10];

f) int a[10], *p=a;

g)int (*a)(int)

h) int( *a[10])(int)

八、volatile作用和用法

在C和C++中,volatile关键字用于声明一个变量,以告诉编译器这个变量的值可能会在程序的控制之外被改变。这通常发生在多线程应用中,或者当变量映射到硬件设备时,硬件可能会在程序没有明确写入的情况下改变变量的值。

volatile的作用是阻止编译器对声明为volatile的变量进行不必要的优化。编译器通常会假设在一个线程内,如果一个变量没有被显式地修改,那么它的值在读取时是不变的。然而,当一个变量是volatile时,编译器会被告知该变量的值可能会以不可预测的方式改变,因此编译器不会将该变量缓存在寄存器中,而是每次都直接从内存中读取它的值。

用法示例:

volatile int counter = 0;
void increment_counter(void) {
    counter++; // 这个操作可能会被硬件中断服务例程修改
}
int main() {
    while (counter < 10) {
        // 如果没有volatile关键字,编译器可能会优化这个循环,
        // 认为counter的值在循环中没有改变,导致无限循环
    }
    return 0;
}

在上面的例子中,counter变量可能会在一个中断服务例程中被修改,而这个中断服务例程是由硬件触发的,不是由程序控制的。因此,我们需要将counter声明为volatile,以防止编译器优化掉循环中的while检查,导致程序无法正确响应counter的变化。

需要注意的是,volatile并不保证变量的原子性操作,它只是告诉编译器不要对变量的访问进行优化。在多线程环境中,如果多个线程访问同一个volatile变量,仍然需要使用互斥锁(mutex)或其他同步机制来保证线程安全。

此外,volatile关键字并不适用于所有的优化问题。例如,它不会阻止编译器对访问volatile变量的指令进行重排。如果需要确保操作的顺序,应该使用内存屏障(memory barrier)或其他同步原语。

以下几种情况都会用到volatile:

1、并行设备的硬件寄存器(如:状态寄存器)

2、一个中断服务子程序中会访问到的非自动变量

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

九、变量的作用域(全局变量和局部变量)

全局变量:在所有函数体的外部定义的,程序的所在部分(甚至其它文件中的代码)都可以使用。全局变量不受作用域的影响(也就是说,全局变量的生命期一直到程序的结束)。

局部变量:出现在一个作用域内,它们是局限于一个函数的。局部变量经常被称为自动变量,因为它们在进入作用域时自动生成,离开作用域时自动消失。关键字auto可以显式地说明这个问题,但是局部变量默认为auto,所以没有必要声明为auto。

局部变量可以和全局变量重名,在局部变量作用域范围内,全局变量失效,采用的是局部变量的值。

十、sizeof 与strlen (字符串,数组)

1、如果是数组

#include<stdio.h>

int main()
{
	int a[5]={1,2,3,4,5};

	printf(sizeof 数组名=%d\n,sizeof(a));

	printf(sizeof *数组名=%d\n,sizeof(*a));
}

运行结果

sizeof 数组名=20
sizeof *数组名=4

2、如果是指针,sizeof只会检测到是指针的类型,指针都是占用4个字节的空间(32位机)。

sizeof是什么?是一个操作符,也是关键字,就不是一个函数,这和strlen()不同,strlen()是一个函数。

那么sizeof的作用是什么?返回一个对象或者类型所占的内存字节数。我们会对sizeof()中的数据或者指针做运算吗?基本不会。例如sizeof(1+2.0),直接检测到其中类型是double,即是sizeof(double) = 8。如果是指针,sizeof只会检测到是指针的类型,指针都是占用4个字节的空间(32位机)。

char *p = "sadasdasd";
sizeof(p):4
sizeof(*p):1//指向一个char类型的

除非使用strlen(),仅对字符串有效,直到’\0’为止了,计数结果不包括\0。

要是非要使用sizeof来得到指向内容的大小,就得使用数组名才行, 如

char a[10];
sizeof(a):10 //检测到a是一个数组的类型。

关于strlen(),它是一个函数,考察的比较简单:

strlen “\n\t\tag\AAtang

答案:11