一、内存
1.五大区
一个由C/C++编译的程序占用的内存分为以下几个部分 :堆区、栈区、全局区(静态区)、文字常量区、代码区五部分。
在执行一个C/C++语言程序时,此程序将拥有唯一的“内存四区”——栈区、堆区、全局区、代码区。每个程序都有唯一的四个内存区域。
1、一个可执行程序在存储(没有调入内存)时分为代码段、静态区和文字常量区三部分;
2、可执行程序(调入内存后)在运行时又多出两个区域:栈区和堆区。
int a = 0; 存放在全局初始化区
char *p1; 存放在全局未初始化区
main()
{
int b; 栈区
char s[] = "abc"; 栈区
char *p2; 栈区
char *p3 = "123456"; 123456\0在常量区,p3在栈区。
static int c =0; 全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的
"123456"优化成一个地方。
}
2.全局区的一些细节
全局区也叫静态区,全局区比较特殊,里面还分成了全局变量区,静态变量区,常量区。全局变量区用来存放全局变量,静态变量区用来存放带有static修饰的变量(包括静态局部变量和静态全局变量),只要含有static就存在这个区。全局变量和静态变量的主要区别是看作用域。常量区是用来存放字符常量的,还有const修饰的全局变量的,const 修饰的局部变量不存在这里,别搞混了。全局区存放的一切都是由操作系统管理,等程序结束由操作系统释放。常量区里存放的数据不可更改,就算你用指针也不行,你可能会说const修饰的局部变量都可以用指针改,但是局部变量可不是存放在常量区,这点搞清楚。
3. 堆区栈区的区别
* 生长方向不同,堆区栈区空间都是弹性大小,堆区从高到低,栈区从低到高
* 分配方式不同,堆区是人为申请的,栈区是操作系统根据类型自动分配
* 分配效率不同,栈区变量在函数启动时,就会分配好空间,堆区空间,执行到代码时才会分配
* 存储内容不同,栈区相对来说空间小,通常存放临时变量,堆区空间大,适合创建大数据量
* 管理方式不同,堆区人为管理,人为回收空间,栈区是操作系统自动管理
* 能否产生碎片,栈区不会产生碎片,堆区人为可能会产生碎片。
二、指针
1.一些基本概念
指针:就是一个装地址的变量 指针自身也是有地址的 指针里面装的地址目的是可以访问该地址指向的空间。
这里详细可见【c++】指针_c++指针_小田加油!!的博客-CSDN博客
1.定义指针
语法:数据类型 *指针变量名;eg:int *p;
让指针记录变量a的地址 p=&a;
2.使用指针
可以通过解引用(指针前面加个*)的方式来找到指针指向的内存,即代表的是指针指向内存中的数据。
3.const修饰指针
1.const修饰指针----常量指针
eg:int a=10;int b=20; const int *p=&a;(const后面是int *,所以*的操作不能做)
特点:指针的指向可以修改,指针指向的值不可以修改
*p=20;--错
p=&b;--对
2.const修饰常量----指针常量
eg:int * const p=&a;(const后面是p,所以p的操作是错的)
特点:指针的指向不可以修改,指针指向的值可以修改
3.即修饰指针又修饰常量
eg:const int * const p=&a;(const后面是int *,const 后面也是p)
特点:指针的指向不可以修改,指针指向的值不可以修改
2.一些与指针相关的例题
1.
void fun(char * q)
{
q=malloc(100);
}
int main(){
char * p =NULL;
fun(p);
strcpy(p,"Hello");
}
这样的话会直接崩溃,C语言里,fun(p)是函数参数传递,只有值传递和地址传递,C++才有引用。
这里p是char *指针 q也是char*指针,二者类型相同,就是把内容给拷贝过去,是值传递。
要改成这样的形式才能够正常的传递。
void fun(cahr ** q)
{
*q=malloc(100);
}
int main()
{
char * p =NULL;
fun(&p);
strcpy(p,"Hello");
}
2.
void fun(char * q)
{
strcpy(q,"Hello");
}
int main(){
char * p =malloc(100);
fun(p);
printf(p);
}
这个没啥问题 可以正常传递。
3.
char* fun()
{
char q[100];
return q;
}
int main()
{
char*p=NULL;
p=fun();
strcpy(p,"Hello");
}
这个不行,这个申请的空间是在栈区申请的,出了栈区就被回收掉了,所以没法运行。
三、数组
1.一些数组的基本概念
以一维数组 array[3] = {0, 1, 2} 为例。
1、数组的首元素地址
&array[0]
2、数组的地址
&array
3、数组名 array 代表数组首元素的地址
即 array 与 &array[0] 等价
2.一些与数组相关的问题
1.int arr[ ]={1,2,3,4,5};
这里*(arr+1)=2;
*(arr++)=? 这个是错的
arr只是数组第一个元素的首地址
+1可以 ++不行 因为++相当于 arr=arr+1 是个赋值过程 但是她是常量 不能赋值
*(&arr+1)=? 这里的话
&arr相当于整个数组的首地址 &arr这里是int[5],5个int的空间, 也是整个数组的指针 *(&arr+1) 会直接偏移到整个数组的后面 你就不知道是啥了
2.*((int*)((int)&arr+1))
这里的+1不是偏移 就只是数字+1
这里有两个考点,一个是强制类型转换,一个是大小端。
注意整个数组的首地址跟首个元素的首地址值是一样的
小端存储 低字节存进地址里
0000 0001 | 0000 0000 | 0000 0000 | 0000 0000 | 0000 0010 | 0000 0000 | 0000 0000 | 0000 0000 |
0x10 0x11 12 13
这里的话就是地址+1 即首地址变成0x11
然后是*访问地址 地址是int型的 所以要访问4个字节
访问到的就是
0000 0000 | 0000 0000 | 0000 0000 | 0000 0010 |
又因为是小端存储,读的时候读取到的就是0000 0010 0000 0000 0000 0000 0000 0000
即2^25
3.二维数组
int a[5][2]={1,2,3,4,5,6};
int (*p)[2]=a;
a就是首元素的首地址 就是{1,2} 是一个int[2]型的 故其指针是int(*p)[2]。
这里如果是int a[5][2]={{1,2},{3,4},{5,6}};
a[1][0]=3;
但是如果是int a[5][2]={(1,2),(3,4),(5,6)};
a[1][0]=6。这里是经典的逗号表达式。因此实际上a[5][2]={2,4,6};
逗号表达式,是c语言中的逗号运算符,优先级别最低,它将两个及其以上的式子联接起来,从左往右逐个计算表达式,整个表达式的值为最后一个表达式的值。如:(3+5,6+8)称为逗号表达式,其求解过程先表达式1,后表达式2,整个表达式值是表达式2的值,如:(3+5,6+8)的值是14;a=(a=3*5,a*4)的值是60,其中(a=3*5,a*4)的值是60, a的值在逗号表达式里一直是15,最后被逗号表达式赋值为60,a的值最终为60。
四、找到只出现一次的数字
1.一组数据,其中一个元素仅出现一次,其他元素均出现两次,请找出只出现一次的元素
这里的话可以用三种方法
1.排序法
2.计数法
3.异或法
2.一组数据,其中有两个元素仅出现一次,其他元素均出现两次,请找出只出现一次的元素
在异或法的基础上进行,步骤为
1.整体进行异或
2.找到非0位
3.根据非0位分组各组异或