Cpp Learning
内存
内存分区
静态数据区data segment
存储于数据段的数据(全局初始化、静态初始化数据、文字常量(char、string))的生命周期为整个程序运行过程。
栈区stack
先进先出内存结构,编译器自动分配释放,存放函数的函数值、返回值、局部变量等,在程序的运行过程中实时加载和释放。
堆区heap
堆区的容量远远大于栈区,动态分配。堆区位于BSS区和栈区之间。手动分配和释放,若未释放则程序结束时系统回收。malloc/free、new/delete。。。
全局静态变量:程序运行前分配内存;
局部静态变量:局部使用;
分配内存
头文件:#include <stdio.h>
#ifndef _CRT_ALLOCATION_DEFINED
#define _CRT_ALLOCATION_DEFINED
void *__cdecl calloc(size_t _NumOfElements,size_t _SizeOfElements);
void __cdecl free(void *_Memory);
void *__cdecl malloc(size_t _Size);
void *__cdecl realloc(void *_Memory,size_t _NewSize);
_CRTIMP void *__cdecl _recalloc(void *_Memory,size_t _Count,size_t _Size);
malloc
堆区分配空间。分配的空间未初始化。
param:
size_t _Size:空间大小,单位字节。
return:
成功:分配空间的起始地址;
失败:nullptr
例:malloc(sizeof(int) *10);
calloc
在内存动态存储区中分配_NumOfElements块长度为_SizeOfElements字节的连续区域,calloc将分配的内存置0。
param:
size_t _NumOfElements:内存单元数量
size_t _SizeOfElements:每个内存单元的大小(字节)
return:
成功:分配空间的起始地址;
失败:nullptr。
realloc
重新分配用malloc或者calloc在堆中分配内存空间的大小。
realloc不会自动清理增加的内存,需要手动清理,如果指定的地址后面有连续的空间,那么就会在已有地址基础上增加内存,如果指定的地址后面没有空间,那么realloc会重新分配新的连续内存,把旧内存的值拷贝到新内存,同时释放旧内存。
param:
void *_Memory:为之前用malloc或calloc分配的内存地址,如果此参数为nullptr,那么和realloc与malloc功能一致。
size_t _NewSize:重新分配内存的大小,单位:字节。
return:
成功:重新分配的堆内存地址;
失败:nullptr。
void testMalloc() {
int *p = reinterpret_cast<int *>(malloc(sizeof(int) * 10));
for (int i = 0; i < 10; i++) {
printf("%d", p[i]);
}
}
// output:
-1163005939-1163005939-1163005939-1163005939-1163005939-1163005939-1163005939-1163005939-1163005939-1163005939
void testCalloc() {
int *p = reinterpret_cast<int *>(calloc(10, sizeof(int)));
for (int i = 0; i < 10; i++) {
printf("%d ", p[i]);
}
}
// output:
0 0 0 0 0 0 0 0 0 0
void testRealloc() {
int *p = reinterpret_cast<int *>(malloc(sizeof(int) * 10));
printf("%d\n", p);
for (int i = 0; i < 10; i++) {
p[i] = i + 1;
printf("%d ", p[i]);
}
printf("\n");
int *p2 = reinterpret_cast<int *>(realloc(p, sizeof(int)*20));
printf("%d\n", p2);
for (int i = 0; i < 20; i++) {
p2[i] = i + 1;
printf("%d ", p2[i]);
}
}
//output:
17178032
1 2 3 4 5 6 7 8 9 10
17178032
1 2 3 4 5 6 7 8 9 10 -1163005939 -1163005939 -1163005939 -1163005939 -1163005939 -1163005939 -1163005939 -1163005939 -1163005939 -1163005939
数据类型
typedef
const
不能直接修改,可以用指针间接修改。
字符串
相同字符串地址相同。
char * p1 = "Hello World";
char * p2 = "Hello World";
char * p3 = "Hello World";
printf("%d\n", p1);
printf("%d\n", p2);
printf("%d\n", p3);
//输出:
4231169
4231169
4231169
字符串以‘\0’结尾。复制时也要加上‘\0’。
strlen:到第一个‘\0’结束。
char str1[] = "hello\012world"; // \012表示八进制的\10:换行
printf("%s\n", str1);
printf("%d\n", sizeof(str1));
printf("%d\n", strlen(str1));
//output
hello
world
12
11
sprintf:字符串格式化
头文件:#include <stdio.h>
__mingw_ovr
__attribute__((__format__ (gnu_printf, 2, 3))) __MINGW_ATTRIB_NONNULL(2)
int sprintf (char *__stream, const char *__format, ...)
{
int __retval;
__builtin_va_list __local_argv; __builtin_va_start( __local_argv, __format );
__retval = __mingw_vsprintf( __stream, __format, __local_argv );
__builtin_va_end( __local_argv );
return __retval;
}
一般格式:sprintf(dst, format, input);
void testSprintf() {
char buf[64];
memset(buf, 0, 64);
sprintf(buf, "Today is %s.", "Monday");
cout << buf;
};
// Today is Monday.
常用函数
1、substr
函数
栈的生长方向
栈底是高地址,栈顶是低地址。(先入栈的地址高,后入栈的地址低)
void A() {
int a = 10; // 栈底 高地址
int b = 10;
int c = 10;
int d = 10; // 栈顶 低地址
printf("%d ", &a);
printf("%d ", &b);
printf("%d ", &c);
printf("%d\n", &d);
}
// output
6421996 6421992 6421988 6421984
void B() {
int a = 0x11223344;
char* p = dynamic_cast<char *>(&a);
printf("%x ", *p); // 44 低位字节 低地址
printf("%x ", *(p+1));
printf("%x ", *(p+2));
printf("%x\n", *(p+3)); // 11 高位字节 高地址
}
// output
44 33 22 11
内存存储方式
高位字节数据(尾数据) – 高地址
低位字节数据(头)-- 低地址
小端对齐
指针与引用
指针
char *p = 0x123456; // 指向地址为123456的内存
查看结构体的内存步长:使用offsetof函数(头文件#include <stddef.h>)
struct Person{
int a; // 0-3
char b; // 4-7
char buf[64]; // 8-71
int d; // 72-75
};
int main()
{
struct Person p = {10, 'b', "Hello", 100};
printf("int的大小为:%d\n", sizeof(int));
printf("char的大小为:%d\n", sizeof(char));
printf("char[64]的大小为:%d\n", sizeof(char[64]));
printf("Person的大小为:%d\n", sizeof(Person));
printf("d的偏移量为:%d\n", offsetof(struct Person, a));
printf("b的偏移量为:%d\n", offsetof(struct Person, b));
printf("buf的偏移量为:%d\n", offsetof(struct Person, buf));
printf("d的偏移量为:%d\n", offsetof(struct Person, d));
printf("d的值为:%d\n", *(int*)((char*)&p + offsetof(struct Person, d)));
return 0;
}
// output
int的大小为:4
char的大小为:1
char[64]的大小为:64
Person的大小为:76
d的偏移量为:0
b的偏移量为:4
buf的偏移量为:5
d的偏移量为:72
d的值为:100
间接赋值
1、两个变量
void test01(int *p) {
printf("函数传参赋值前: p = %d *p = %d;\r\n", p, *p);
*p = 1000;
}
int main(){
int a = 100;
printf("&a = %d a = %d\n", &a, a);
printf("1. 变量赋值\r\n");
int *pA = nullptr;
pA = &a;
printf("变量赋值前: &a = %d a = %d; pA = %d *pA = %d\r\n", &a, a, pA, *pA);
*pA = 101;
printf("变量赋值后: &a = %d a = %d; pA = %d *pA = %d\r\n", &a, a, pA, *pA);
test01(&a);
printf("函数传参赋值后: &a = %d a = %d; pA = %d *pA = %d\r\n", &a, a, pA, *pA);
}
// output:
&a = 6421932 a = 100
1. 变量赋值
变量赋值前: &a = 6421932 a = 100; pA = 6421932 *pA = 100
变量赋值后: &a = 6421932 a = 101; pA = 6421932 *pA = 101
函数传参赋值前: p = 6421932 *p = 101;
函数传参赋值后: &a = 6421932 a = 1000; pA = 6421932 *pA = 1000
二级指针
强制类型转换
reinterpret_cast:可以将void*转换为其他类型;