C++学习笔记

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*转换为其他类型;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值