
一、数组和指针有什么联系?
指针就是指针,指针变量在 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)。不同的变量类型和声明周期通常存储在不同的区域。
- 栈区(Stack):
- 自动局部变量(auto variable):在函数内部定义的没有指定存储区域的局部变量,如
int a;
,它们会自动存储在栈上。 - 函数参数:函数调用时传递的参数也是在栈上分配的。
- 函数返回地址和局部变量:这些信息在函数调用过程中被存储在栈上。
- 自动局部变量(auto variable):在函数内部定义的没有指定存储区域的局部变量,如
- 堆区(Heap):
- 动态分配的内存:使用
new
关键字在C++中或者在C中使用malloc
、calloc
、realloc
等函数分配的内存都存储在堆上。这些内存直到使用delete
(C++)或free
(C)显式释放前都不会被自动清理。 - 对象生命周期由程序员控制:通过
new
创建的对象,其生命周期不是由作用域决定的,而是由delete
操作决定。
- 动态分配的内存:使用
- 全局/静态存储区(Global/Static):
- 全局变量:在所有函数体之外定义的变量,程序启动时分配,程序结束时释放。
- 静态变量:在函数内部或类内部声明为
static
的变量,它们的存储期限是程序的整个运行期间,而不是函数调用的周期。
- 常量存储区(Constant):
- 字符串常量:如
const char* p = "hello world";
中的"hello world"
是存储在常量区的。 - const修饰的全局变量:声明为
const
的全局变量也存储在这里。
- 字符串常量:如
不同的内存区域有不同的生命周期和访问特性,合理地使用这些区域对于程序的性能和资源管理都是非常重要的。在程序设计中,应当根据变量的特性和用途选择合适的存储区域。
C++和C语言中的内存分区的区别
C++和C语言在内存管理上有一些相似之处,但也存在差异。在C语言中,内存通常被分为三个主要区域:
- 栈区(Stack):用于存储局部变量、函数参数和返回地址等。栈上的内存分配和释放是自动的,由编译器在函数调用和返回时管理。
- 堆区(Heap):用于动态内存分配,通过
malloc
、calloc
、realloc
等函数分配的内存位于堆上。堆内存的分配和释放需要程序员手动管理。 - 静态存储区(Static/Global):用于存储全局变量和静态变量。这些变量在程序启动时分配,直到程序结束时才释放。
C语言中没有显式的常量存储区概念,而是将常量值直接嵌入到代码中,通常存储在只读的代码段。
相比之下,C++在C的基础上增加了对象的概念,并且引入了类和对象的生命周期管理。C++的内存四区通常指的是:
- 栈区(Stack):与C语言相同,用于自动局部变量和函数调用相关的数据。
- 堆区(Heap):与C语言相同,用于动态内存分配,但C++使用
new
和delete
关键字进行对象的分配和释放。 - 全局/静态存储区(Global/Static):与C语言相同,用于全局变量和静态变量。
- 常量存储区(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