【C++】混淆点


sizeof、strlen、64位(32位)计算机基础类型字节数

  • sizeof不是一个函数,是一个运算符
  • sizeof只能用于计算占用栈中内存的大小
类型32位64位
char11
short int22
int44
long int48
long long int88
float44
double88
指针48
指针字节:只与机器有关. 如果你的机器是16位寻址的, 那指针就是16位的,2个字节; 如果是32位寻址的, 指针也是32位的,4个字节;如果寻址是64位的,指针也是64位,8个字节,
#include <__msvc_all_public_headers.hpp>
using namespace std;

void Fun(char str_temp[2]) { // 编译器看为str_temp[]
	//数组名作为函数参数传递给调用函数后,就失去了原有特性,
	//退化成一般指针,多了自增、自减操作,但sizeof数组名不能再得到原数组容量的大小。
	printf("%d\n", sizeof(str_temp)); //4 退化为一般指针
	printf("%d\n", strlen(str_temp)); //5 hello大小
	cout << str_temp[4] << endl; // 'o'字符,忽略形参大小
}
int main() {
	char str[] = "hello"; //数组名不是真正意义上的指针,没有自增、自减等操作
	string s = "hello";
	printf("%d\n", sizeof(str)); //6 包括'\0'
	printf("%d\n", sizeof(s)); //28 动态分配内存,有预留空间
	printf("%d\n", strlen(str)); //5
	//printf("%d\n", strlen(s)); // 不能用于字符串

	char* p = str;
	char* p1;
	printf("%d\n", sizeof(p)); //4 指针在32位中4个字节
	printf("%d\n", sizeof(p1)); //4 指针在32位中4个字节
	printf("%d\n", strlen(p)); //5 所指字符串长度

	Fun(str);

	char str1[100] = "hello";
	//char str2[]; //未定义数组长度必须初始化
	printf("%d\n", sizeof(str1)); //100 未初始化也分配内存
	for (int i = 0; i < 100; ++i) {
		cout << str1[i]; //helloaaaa... 默认初始化为a
	}
	cout << endl;

	system("pause");
	return 0;
}
    void* p = malloc(100);
	cout << sizeof(p) << endl; //4

#ifdef、#pragma once作用

  • 防止头文件在被多次引用的时候重复定义,这是C和C++的所有编译器通用的。但是微软的编译器还提供了另外一种方式#pragma once
  • 因为#ifdef …这种方法是利用宏定义特性来保证不会被重复引用的,但是#ifdef 这个宏定义可以在文件的任何地方使用,所以编译器必须将文件读完才能完成工作。但是#pragma once是单独的一个宏定义,编译器只要可以立即标记,所以第二种更快,但无法跨平台
  • #pragma once //防止头文件重复包含
指令描述
#include包含一个源代码文件
#define定义宏
#undef取消已定义的宏
#ifdef若宏已经定义,则返回真
#ifndef若宏没有定义,则返回真
#if若给定条件为真,则编译下面代码
#else#if的替代品
#elif若前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif结束一个#if #else 条件编译块
#error停止编译,并输出错误信息

new/delete的用法

  • 动态分配数组时,只需指定类型和数组长度,不必为数组对象命名,new表达式返回指向新分配数组的第一个元素的指针
1.创建动态数组
//分配了一个含有10个int型元素的数组,并返回指向该数组第一个元素的指针。
//在自由存储区创建的数组对象是没有名字的,程序员只能通过其地址间接地访问堆中的对象
int *p=new int[10];
//可使用跟在数组长度后面的一对空圆括号,对数组元素作值初始化:
int *pis = new int[10]();  //数组元素都默认设置为0

2. 动态创建单个对象
//对于提供了默认构造函数的类类型,没有必要进行值初始化。
//如果没有显示初始化动态创建的对象,则对于类类型的对象,用该类的默认构造函数初始化,对于内置类型的对象则无初始化。
int *pi = new int(1024); //初始化1024
string *ps = new string(10, '9'); //初始化10个9
string *ps = new string(); //有默认构造的初始化
int *pi = new int();       //内置类型无默认初始化

3. 动态空间的释放
//该语句释放上面所创建的动态int型数组所占有的存储空间。
//在关键字delete和指针之间的[ ]告诉编译器该指针指向的是自由存储区中的数组,而并非单个对象
delete [ ] pis;

//该命令释放了pi指向的int型对象所占用的内存空间。删除指针后,
//该指针变成悬垂指针(dangling pointer).悬垂指针指向曾今存放对象的内存
//但该对象已经不存在了。悬垂指针往往导致程序错误,而且很难检测出来。
//一旦删除了指针所指向的对象,立即将指针置为0,这样就非常清楚地表明指针不再指向任何对象。
delete pi;
pi=nullptr; //消除悬垂指针


//struct RandomListNode {
//    int label;
//    struct RandomListNode *next, *random;
//    RandomListNode(int x) :
//            label(x), next(NULL), random(NULL) {
//    }
//};
unordered_map<RandomListNode*, RandomListNode*> hash_map;
for(RandomListNode* p = pHead; p != nullptr; p = p->next)
{
    hash_map[p] = new RandomListNode(p->label);  // 有参构造的初始化
}

malloc/free用法

  • 和new一样开辟到堆上
  • 头文件:#include <malloc.h> 或 #include <alloc.h> (注意:alloc.h 与 malloc.h 的内容是完全一致的)
  • 说明:malloc 向系统申请分配指定size个字节的内存空间。返回类型是 void* 类型。void* 表示未确定类型的指针。C,C++规定,void* 类型可以强制转换为任何其它类型的指针。
char* p=(char *)malloc(100);
free(p);

malloc与new的不同点:

  • new 返回指定类型的指针,并且可以自动计算所需要大小而 malloc 则必须由我们计算要字节数,并且在返回后强行转换为实际类型的指针。
  • 另外有一点不能直接看出的区别是,malloc 只管分配内存,并不能对所得的内存进行初始化,所以得到的一片新内存中,其值将是随机的。
int* p = (int*) malloc (sizeof(int));
int* p = (int*) malloc (sizeof(int)100); //分配可以放得下100个整数的内存空间。
struct ListNode* node = (ListNode*)malloc(sizeof(struct ListNode));

distance()的用法

  • distance()是常用的一个迭代器操作函数,用来计算两个迭代器之间的距离,它可以接受任何类型的迭代器 distance ( map.begin(), map.end() )
	vector<int> vec;
	vec.push_back(1);
	vec.push_back(2); 
	vec.push_back(3);
	vec.push_back(4);
	int a = distance(vec.begin(), vec.end());//4-0=4

分号;的用法

加分号的情况

语句结束加分号(否则编译器不知道在哪里结束语句,编译器不识别换行,写代码时换行和退格只是为了看着舒服,但本质上代码是写给编译器看的)

  • 声明语句后加分号(也是一种语句)
  • 结构体、类定义后加分号(也是一种语句)
  • 函数声明要加分号

不加分号的情况

  • 预处理命令后不加分号(编译器根据#知道这是预处理语句)
  • 函数结束后不加分号(编译器根据大括号知道函数结束了,加分号也不会报错,但没有意义)

cstdio和stdio.h的区别

  • cstdio是将stdio.h的内容用C++头文件的形式表示出来。stdio.h是C标准函数库中的头文件,即:standard buffered input&output。
  • stdio 和 stdio.h是有差别的,并不是同样的文件。stdio.h是以往的C和C++ 的头文件,cstdio是标准C++(STL),且cstdio中的函数都是定义在一个名称空间std里面的,如果要调用这个名字空间的函数,必须得加std::或者在文件中声明using namespace std

size_t 的意义与作用

size_t 的意义与作用

32 位架构中被普遍定义为:
typedef unsigned int size_t;64 位架构中被定义为:
typedef unsigned long size_t;
  • 从定义可以看出,size_t 是一种无符号的整型(unsigned int、unsigned long、unsigned long long),取值范围是目标平台下最大的可能范围。sizeof 关键字的返回类型就是 size_t。
#include <stdio.h>

int main() {
    printf("Int size: %d", sizeof(int));
}

// Int size: 4

  • 使用 size_t 来代替 int 或 unsigned 可以保证在同一个平台中,始终得到一个数据类型或变量的字节大小,保证了程序对该数据类型或变量的统计方式始终一致,不会因为平台的改变而出现错误。
  • 当你看到一个对象声明为 size_t 类型时,你马上就知道它用于代表字节大小及数量,或者表示数组索引,而不是一个普通的算术值。

子类和父类对象的转换

1 指针或引用的 向上转换、向下转换

从 父类 到 子类 的转换是 向下转换
从 子类 到 父类 的转换是 向上转换

2 普通指针的转换

  • 转换规则
    向上转换可 隐式地 进行,即,无需进行强制类型转换。
    向下转换必须使用 dynamic_cast 进行强制类型转换。

  • 转换细节
    参考下面的博客。
    从内存的角度剖析,转换后如何访问指针指向的成员。
    代码实例展示不同转换之间的区别。
    转换细则的总结

  • 注意事项
    父类的指针不能赋值给子类的指针。但是通过强制类型转换,也可以将父类指针强制转换成子类指针后再赋值给子类指针。只是在这种情况下,程序员需要保证被转换的父类指针本来就指向一个子类的对象,这样才是安全的,否则很容易出错。
    指针调用的是父类方法 还是子类方法,看的是指针的类型,而不是指针所指对象的类型。
    虽然子类指针指向的是一个基类对象,但这并不影响子类成员变量的地址计算方式。

3 智能指针的转换

转换规则
向上转换可以隐式转换。
向下转换必须使用 dynamic_pointer_cast 进行转换。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇光_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值