0
点赞
收藏
分享

微信扫一扫

JTS Technical Specs翻译

凶猛的小白兔 2023-09-10 阅读 48

C/C++基础知识

数组和指针的区别

  1. 退化的意义:C语言只会以值拷贝的方式传递参数,参数传递时,如果拷贝整个数组,效率会大大降低,并且在参数位于栈上,太大的数组拷贝将会导致栈溢出。
  2. 因此,C语言将数组的传参进行了退化。将整个数组拷贝一份传入函数时,将数组名看做常量指针,传数组首元素的地址。

指针数组和数组指针

指针数组本质是数组,数组指针本质是指针,谁优先级高,本质是谁

  1. 优先级问题:[]的优先级比*高
  2. 说明arr是一个数组,而int*是数组里面的内容
  3. 这句话的意思就是:arr是一个含有 * 和 int* 的数组
  1. 由于[]的优先级比*高,因此在写数组指针的时候必须将*arr用括号括起来
  2. arr先和*结合,说明p是一个指针变量
  3. 这句话的意思就是:指针arr指向一个大小为8个整型的数组。

字符数组和字符串常量

char arr[]=“hello”; //字符数组
char *arr2=“hello”; //字符串常量

const char arr[]=“hello”; //这里hello本来是在栈上的,但是编译器可能会做某些优化,将其放到常量区
const char *arr2=“hello”; //字符串hello保存在常量区,const本来是修饰arr2指向的值不能通过arr2去修改,但是字符串hello在常量区,本来就不能改变,所以加不加const效果都一样

inline和宏的区别

  • inline是在编译期间展开,宏在预处理时替换
  • inline函数直接嵌入到目标代码内,宏只是简单的文本替换
  • inline函数会进行类型安全检查和参数有效性检查,而宏不会
  • inline函数是函数,宏不是
  • 宏定义时有可能产生二义性,而inline函数不会
  • inline可以不展开,只是对编译器的建议,而宏必须展开

引用和指针的区别

C++中class和struct的区别

C中的strcut不能有函数,但C++中可以。C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能。struct能包含成员函数吗? 能!struct能继承吗? 能!!struct能实现多态吗? 能!!!

new/delete和malloc/free区别

malloc和calloc间的主要区别在于后者在返回指向内存的指针之前把它初始化为0。另一个区别是calloc的参数包括所需的元素的数量和每个元素的字节数

new运算符的原理

  • 内存分配

    • 调用相应的 operator new(size_t) 函数,动态分配内存。如果 operator new(size_t) 不能成功获得内存,则调用 new_handler()函数用于处理new失败问题。如果没有设置 new_handler() 函数或者 new_handler() 未能分配足够内存,则抛出 std::bad_alloc 异常
  • 构造函数

    • 在分配到的动态内存块上 初始化相应类型的对象(构造函数)并返回其首地址。如果调用构造函数初始化对象时抛出异常,则自动调用 operator delete(void*, void*) 函数释放已经分配到的内存。

malloc的内存分配机制

malloc内存分配机制是怎么样的,在哪里分配内存,最大可以申请多大的内存?

栈和堆的区别

  • 管理方式不同
    • 对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
  • 空间大小不同
    • 一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M
  • 能否产生碎片不同
    • 对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出
  • 生长方向不同
    • 对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长

面向对象和面向过程的区别

面向对象就是高度实物抽象化、面向过程就是自顶向下的编程

const关键字(不可修改)

static关键字(对外不可见)

extern关键字

extern关键字主要修饰变量或函数,表示该函数可以跨文件访问,或者表明该变量在其他文件定义,在此处引用.

  1. 在c头文件中通过#ifdef __cplusplus extern “C” { #endif来定义
  2. 在对应的c文件中实现
  3. 在cpp文件中通过“extern “C” 函数名”调用,或者包含c头文件
    注意: extern的引用方式比包含头文件要简洁得多!extern的使用方法是直接了当的,想引用哪个函数就用extern声明哪个函数。这样做的一个明显的好处是,会加速程序的编译(确切的说是预处理)的过程,节省时间,但若需要调用的函数太多,还是直接包含头文件吧.

volatile关键字

explicit关键字

explicit关键字的作用就是防止对象间实现 = 赋值(赋值构造函数),防止类构造函数的隐式自动转换,类构造函数默认情况下即声明为implicit(隐式),

另外explicit只用于单参数的构造函数,或者除了第一个参数以外的其他参数都有默认值.

class Person{
public:
    Person(){
    }
    //有参构造初始化数据
    explicit Person(const char* str_){
        str = (char *)malloc(sizeof(char)*100);
        strcpy(str,str_);
    }
    ~Person() {
        if (str != NULL){
            free(str);
            str = NULL;
        }
    }
    char *str;
};
void test05(){
    //Person p = "abc"; 隐式调用
    Person p ("abc"); //显式调用
}

类成员属性

类的成员有三个权限:公有权限(public),私有C权限(private),保护权限(protected)。

大小端序的定义和代码判断

定义

一个16进制的地址,存放在内存中从低地址开始存储,如16进制的地址为0x1234,对于地址而言,从右往左是从低到高

  • 大端
    • 若16进制的低地址存放在内存的高地址,则为大端字节序,34存储在高位,12存储在低位
  • 小端
    • 若16进制的低地址存放在内存的低地址,则为小端字节序,12存储在高位,34存储在低位

代码判断

可以通过联合体来判断,联合体是同一块内存被联合体中的所有成员公用,如果后续成员对内存重新赋值,会覆盖内存中原有数据

union U{
	int a;
	char b;
};

int main(){
	U u;
	u.a = 0x01020304;

	if (u.b == 0x04){
		cout << u.b << endl; //输出char字符
		cout << "little" << endl;
	}

	else if (u.b == 0x01){
		cout << u.b << endl;
		cout << "big" << endl;
	}
	system("pause");
	return 0;
}

代码判断32位和64位系统

写一个指针,输出指针所占的字节大小

线程A和线程B不加锁,对同一个变量,各自执行 i++ 100次,结果是啥

最小值2,最大值200。

最小值2

  • 初始条件,内存值为0
  • A线程从内存中取出值,进行第一次++操作,还未写回内存就被挂起,此时A寄存器 1 ,内存 0 ;
  • B线程从内存中取出值,进行第一次++操作,还未写回内存就被挂起,此时B寄存器 1 ,内存 0 ;
  • A连续执行到第99次++操作,每次都写回到内存,此时A寄存器 99 ,内存 99 ;
  • B继续执行,将自己寄存器的值写回到内存,覆盖了内存中的99,此时B寄存器 1 ,内存 1 ;
  • A执行第100次++操作,取出内存中的值1,然后++,还未写回内存就被挂起,此时A寄存器 2 ,内存 1 ;
  • B执行完自己所有剩余操作,此时B寄存器为100, 内存为100;
  • A继续执行,将自己的寄存器的值写回内存,内存为2。

最大值200

每个线程的每次执行都将值写回内存

C++指定内存分配对象

placement new

首先分配足够大的内存;然后用placement new语法生成对象:new(ptr) xxx(),其中ptr是足够容纳所指对象的指针。

class Person {
private:
    int age;
    std::string name;
public:
  // methods
};
 
int main(int argc, char** argv) {
    auto mem = malloc(sizeof(Person));
    auto p = new(mem) Person();
    p->show();
    free(mem);
    return 0;
}
/*
Person
Lily's age is 10
*/

allocator

先分配内存,然后在其上装载对象,可以使用allocatorallocator定义在头文件中,能对指定类型分配合适的内存,并可手动调用对象的构造函数和析构函数。

class Person {
private:
  int age;
  std::string name;
public:
    Person() {
        age = 10;
        name = "Lily";
        cout << "Person" << endl;
    }
    void show() {
        cout << name << "'s age is " << age << endl;
    }
    ~Person() {
        cout << "~Person" << endl;
    }
};

int main() {
  std::allocator<Person> alloc;
  auto p = alloc.allocate(1); // 分配一个Person对象的内存
  alloc.construct(p);   // 调用Person的构造函数,如果构造函数有参数,参数写在p之后
  // p 现在是一个指向Person的指针,且其指向对象被初始化过
  // 对p进行一些操作
    p->show();
  // 销毁对象,但不释放内存,等同于调用p->~Person()
  alloc.destroy(p);
  // 释放内存
  alloc.deallocate(p, 1);
  return 0;
}
/*
Person
Lily's age is 10
~Person
*/
举报

相关推荐

0 条评论