面试---C/C++基础_c 基础面试-程序员宅基地

技术标签: 面试  C++  C  嵌入式  

1. 编译过程

预处理:预处理相当于根据预处理命令组装成新的C程序(头文件展开,宏定义替换等),不过常以.i为扩展名。

编译: 将得到的.i文件翻译成汇编代码.s文件。

汇编:将汇编文件翻译成机器指令,并打包成可重定位目标程序的.o文件,该文件是二进制文件,字节编码是机器指令。

链接:将引用的其他.o文件并入到我们程序所在的.o文件中,处理得到最终的可执行文件。

2. 宏定义和const

2.1 宏定义的使用

宏定义本身可以理解为字符的替换,在实际编程中有很多的应用,宏可以带参数,如:

  1. 宏定义给某个数bit3置位和清零
// An highlighted block
#define SET(a,b)  a|(0x1<<b);
#define CLR(a,b)  a&(~(0x1<<b));
  1. 宏定义一个求两个数最小值的函数
#define MIN(A,B) ((A)>(B))?B:A
  1. 宏定义求最大值函数,不能使用 >,<,if,语句
//宏定义参数需要用括号括起来,且表达式内部不能有空格
#define max(a,b)  (((a)-(b))&(1<<31))?(b):(a)
#define max(a,b)  (a-b)==abs(a-b)?a:b
#define max(a,b)  ((((long)((a)-(b)))&0x80000000)?(b):(a))
  1. 交换两个数的函数
#define swap(a,b) {a=a+b;b=a-b;a=a-b;}

2.2 const

常量定义必须初始化,对于局部对象,常量存放在栈区,对于全局对象,常量存放在全局/静态存储区。对于字面值常量,常量存放在常量存储区。
1. const的含义

const int a;  //整型常量
int const a;  //整型常量
const int *a; //指向整型常数的指针(指向的常数不可修改,指针本身可以修改)
int* const a; //指向整数的常指针(指针本身不可修改,指向的整数可以修改)
int const* a const; //指向常量的常指针(都不可修改)

2. 为什么使用const?
(1) 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的)。
(2) 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。
(3) 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。
3.const修饰成员函数的目的
const修饰成员函数表明该函数调用不会对对象作出任何更改,事实上,如果确认不会对对象作出修改就应该加上const限定,这样无论是const对象还是普通对象都可以调用该函数。

2.3 宏定义和const的区别(定义常量)

  1. 宏定义是在编译之前进行的,而const是在编译阶段处理的
  2. 宏定义不占用内存,而const定义的常量占用内存单元
  3. 宏定义没有具体的数据类型,而const有具体的数据类型
  4. 宏定义可以带参数进行定义,而const不能带参数定义

3. sizeof和strlen,char * 和 char[]的区别

  1. sizeof是运算符,strlen是函数
  2. strlen只能用char*做参数,且必须以"\0"结尾,sizeof可以用类型或函数做参数
  3. 数组做sizeof的参数不退化,做strlen的参数时退化为指针
  4. sizeof在编译时确定类型占内存的大小(大部分编译器),因此sizeof(x)可以用来定义数组维数,可以通过以下方式确定数组长度
int num[10] = {
     0,1,2,3,4,5,6,7,8,9 };
int len1 = sizeof(num); //len1 = 40
int len2 = sizeof(num) / sizeof(num[0]); //len2 = 10
  1. strlen在运行时计算字符串的长度
char str[20] = "0123456789";//存放在堆栈区,可以修改
char *p_str = "0123456789"; //存放在常量区,不可修改
char *p = str;
int len3 = strlen(str); //len3 = 10;
int len4 = sizeof(str); //len4 = 20;
int len5 = sizeof(*str);//len5 = 1
int len6 = sizeof(p);   //len6 = 4(32位编译器)
  1. 当适用于一个结构类型时或变量,sizeof 返回实际的大小;当适用一静态地空间数组,sizeof计算全部数组的字节数
  2. 数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址
    fun(char [8])
    fun(char [])
    都等价于 fun(char *)
  3. 在C++里参数传递数组永远都是传递指向数组首元素的指针,编译器不知道数组的大小,如果想在函数内知道数组的大小, 需要这样做:
    进入函数后用memcpy拷贝出来,长度由另一个形参传进去
fun(unsiged char *p1, int len)
{
    
  unsigned char* buf = new unsigned char[len+1]
  memcpy(buf, p1, len);
}

4. static关键字

只能在本文件中使用;局部静态变量保存在数据段,而普通变量分配在栈上,会因为函数栈帧的释放而被释放掉;类中成员加了static,该变量没有this指针,必须通过类名来访问

  1. 全局静态变量
    内存:存储在静态存储区,在整个程序运行期间一直存在。
    初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显示初始化)
    作用域:全局静态变量在声明他的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。

  2. 局部静态变量
    内存:静态存储区
    初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显示初始化)
    作用域:作用域仍为局部作用域,当他的函数或语句结束时,作用域结束,但他没有销毁,仍然驻留在内存中,只不过无法访问,直到再次运行其函数时被调用,而且值不变。

  3. 静态函数
    在函数的返回类型前加static修饰,那么这个函数只可在本cpp内使用,不会和其他cpp中的同名函数起冲突,也不能被其他文件所使用。

  4. 类的静态成员
    在类中,静态成员可以实现多个对象间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态成员是类的所有对象中共享的成员,而不是某个对象的成员。对多个对象来说,静态数据成员只存在一处,供所有对象公用。

  5. 类的静态函数
    和类的静态成员一样,都是所有对象共有的。因此,对静态函数的引用不需要用对象名。
    静态成员函数实现时不能直接引用类中的非静态成员,但可以直接引用类中的静态成员。如果静态成员函数中要引用非静态成员可以通过对象来引用,引用格式如下:
    <类名>::<静态成员函数名> (<参数表>)

5. C和C++的区别

设计思想上:
C++面向对象(继承、封装、多态),C面向过程,结构化
语法上:
C++增加许多类型安全的功能,比如强制类型转换。
C++支持范式编程(类模板、函数模板)。

c++调用c函数需要extern C,因为C语言没有函数重载。

6. memcpy,memset和strcpy

  1. strcpy
    strcpy的作用是拷贝字符串,当它遇到’\0’时结束拷贝,原型为:
extern char *strcpy(char *dest, char *src); 

就是把src的字符串复制到dest中,以’\0’结束,例如:

#include<string.h>
#include<iostream.h>

void main(){
    
	char a[20], c[]="where is offer?";
	strcpy(a,c);
	cout<<a<<endl; //where is offer?
}
  1. memcpy
    memcpy用来做内存拷贝,可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度;原型函数为
void *memcpy(void *dest, const void *src, size_t n);

功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置。例如:

#include <stdio.h>
#include <string.h>
int main() {
    
    char *s = "Golden Global View";//此字符串总共有18个字符,再加上一个结束符,总共存放19个字符
    char d1[20], d2[20];
    memcpy(d1, s, (strlen(s)+1));//用memcpy函数,把s中字符串的首地址拷贝到d中,连续复制19个字符
	memcpy(d2, s+14, 5); //从第14个字符(V)开始复制,连续复制4个字符(View)
    printf("%d\n", strlen(s));
    printf("%s\n", d1);

	printf("%s\n", d2);
    return 0;
}
  1. memset
    memset的作用是对一段内存空间全部设置为某个字符,常用在内存空间的初始化,其原型函数为
void *memset(void *s, int c, size_t n);//就是把已开辟内存空间s的首n个字节设置为c。

例如

#include <stdio.h>
#include <memory.h>
#include <string.h>
int main()
{
    
	char s[] = "Golden Global View";
	memset(s, 'G', 6);//把s中的前6个字符改成G,也就是把Golden改成GGGGGG
	printf("%s\n", s);//输出为GGGGGG Global View
	return 0;
}

7. 大端、小端

0x3132(0x32是低位,0x31是高位),把它赋值给一个short变量,那么它在内存中的存储可能有如下两种情况:

(1)、大端字节(Big-endian):

----------------->>>>>>>>内存地址增大方向

short变量地址

  0x1000                 0x1001
	0x31                   0x32

大端->高位在前->正常的逻辑顺序

(2)、小端字节(little-endian):

----------------->>>>>>>>内存地址增大方向

short变量地址

   0x1000                 0x1001
     0x32                   0x31

小端->低位在前->与正常逻辑顺序相反

8. volatile与register修饰符

1. volatile
作用是确保本条指令不会因编译器的优化而省略,且要求==每次直接从内存中读值,==而不是读取寄存器保存的副本
volatile表示这个变量会被意想不到的改变,每次用他的时候都会小心的重新读取一遍,不适用寄存器保存的副本。
(1) 并行设备的硬件寄存器(如:状态寄存器)
(2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
(3) 多线程应用中被几个任务共享的变量

2. register
建议编译器使用寄存器来优化对变量的存取。register修饰符暗示编译程序相应的变量将被频繁地使用,如果可能的话,应将其保存在CPU的寄存器中,以加快其存储速度

9. C++类型转换

const_cast, static_cast, dynamic_cast, reinterpret_cast
1. const_cast
用于将const变量转为非const
2. static_cast
用于各种隐式转换,如非const转const,void* 转指针,static_const可用于多态向上转化,如果向下转能成功但是不安全,结果未知
3. dynamic_cast
用于动态类型转换。只能用于含有虚函数的类,用于类层次间的向上和向下转化。只能转指针或引用。向下转换时,如果是非法的对于指针返回NULL,对于引用抛出异常。要深入了解内部转换的原理:它通过判断在执行到该语句的时候变量的运行时类型和要转换的类型是否相同来判断是否能够进行向下转换。
4. reinterpret_cast
几乎什么都能转,比如int转指针,但是不安全
5. 不用c强制转换的原因?
容易出错,没有错误检查。
6.隐式类型转换
对于内置类型,低精度的变量给高精度变量赋值会发生隐式类型转换。
对于只存在单个参数的构造函数来说,函数调用可以直接使用该参数传入,编译器会自动调用其构造函数生成临时对象。
7.RTTI
运行时类型检查,在C++层面主要体现dynamic_cast和typeid,VS中虚函数表的-1位置存放了指向type_info的指针。对于存在虚函数的类型,typeid和dynamic_cast都会查询type_info

10. 指针和引用

指针和引用的区别

  1. 指针有自己的内存空间,引用只是一个别名
  2. 使用sizeof,指针返回内存空间的大小(一般4个字节),引用是被引用对象的大小
  3. 指针可以被初始化为NULL,也可以不初始化,引用必须初始化且引用一个已有对象。
  4. 作为参数传递时,指针需要被解引用才可以对对象进行操作,而直接对引用的修改一定改变被引用的对象。
  5. 可以有const指针,不能有const引用
  6. 指针在使用中可以指向其它对象,但引用只能是一个对象的引用,不能改变
  7. 指针可以有多级,引用止于一级
  8. 指针和引用使用++时含义不同
  9. 如果返回动态分配的内存或对象,必须使用指针,引用可能引起内存泄漏

C++为什么引入引用?

操作指针时存在的问题:操作空指针、操作野指针和不经意间改变指针的值,引用可以解决这几个问题,因为引用必须初始化引用某个对象,且不能改为引用其他对象。

11. C++中的智能指针

智能指针的作用是管理一个指针,因为存在以下情况:申请的空间在函数结束时忘记释放,造成内存泄漏。使用智能指针可以很大程度上避免这个问题,因为智能指针就是一个类,当超出了类的作用域,类会自动调用析构函数释放资源。所以智能指针的作用是在函数结束时自动释放内存空间,不需要手动释放。
1. auto_ptr(C++11已抛弃)
采用所有权模式。

auto_ptr<string> p1(new string("I reigned lonely as a cloud"));
auto_ptr<string> p2;
p2 = p1; //auto_ptr不会报错

p2剥夺了p1的所有权,当程序运行到p1时会报错。所以auto_ptr的缺点是存在潜在的内存崩溃问题。
2. unique_ptr(替换auto_ptr)
unique_ptr实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。它对于避免资源泄漏(例如“new创建对象后因为发生异常而忘记调用delete”)特别有用。

unique_ptr<string> p3(new string("auto"));
unique_ptr<string> p4;
p4 = p3; //此时会报错!

编译器认为p4 = p3 非法,避免了p3不再指向有效数据的问题。因此,unique_ptr更安全。
另外unique_ptr还有更聪明的地方:当程序试图将一个unique_ptr赋给另一个时,如果源unique_ptr是个临时右值,编译器允许这么做;如果源unique_ptr将存在一段时间,编译器将禁止这么做,比如:

unique_ptr<string> pu1(new string ("hello world"));
unique_ptr<string> pu2;
pu2 = pu1; // #1 not allowed

unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string ("You")); // #2 allowed

注:如果想执行#1类似的操作,要安全的重用这种指针,可给它赋新值,使用std::move(),例如:

unique_ptr<string> ps1, ps2;
ps1 = demo("hello");
ps2 = move(ps1);
ps1 = demo("alexia");
cout << *ps2 << *ps1 << endl;

3. shared_ptr
shared_ptr实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时释放。从名字share可以看出该资源被多个指针共享,它使用计数机制来表明资源被几个指针共享。除了可以通过new来构造,还可以通过传入auto_ptr,unique_ptr,weak_ptr来构造。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,资源被释放。
shared_ptr是为了解决auto_ptr在对象所有权上的局限性(auto_ptr是独占的),在使用引用计数的机制上提供了可以共享所有权的智能指针。
其成员函数包括:
use_count返回引用计数的个数
unique返回是否独占所有权(use_count为1)
swap交换两个shared_ptr对象(即交换所拥有的对象)
reset放弃内部对象的所有权或拥有对象的变更,会引起原有对象引用计数的减少
get返回内部对象(指针),由于已经重载了()方法,因此和直接使用对象是一样的。如

shared_ptr<int> sp(new int(1));

sp和sp.get()是等价的
4. weak_ptr
weak_ptr是一种不控制对象生命周期的智能指针,它指向一个shared_ptr管理的对象,进行该对象的内存管理,weak_ptr只是该对象的一个访问手段。weak_ptr设计的目的是为配合shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起引用计数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的死锁问题。如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。例如:

class B;
class A {
public:
	shared_ptr<B> pb_;
	~A() {
		cout<<"A delete\n";
	}
};
class B {
public:
	shared_ptr<A> pa_;
	~B() {
	cout<<"B delete\n";
	}
};
void fun() {
	shared_ptr<B> pb(new B());
	shared_ptr<A> pa(new A());
	pb->pa_ = pa;
	pa->pb_ = pb;
	cout<<pb.use_count()<<endl;
	cout<<pa.use_count()<<endl;
}
int main() {
	fun();
	return 0;
}

可以看到fun函数中pa,pb互相引用,两个资源的引用计数为2,当要跳出函数时,智能指针pa,pb析构时两个资源引用计数会减一,但是两者的引用计数还为1,导致跳出函数时资源没有被释放(A,B的析构函数没有被调用),如果把其中一个改为weak_ptr就可以了,我们把类A里面的

shared_ptr pb_; 

改为

weak_ptr pb_;

这样的话,资源B的引用开始就只有1,当pb析构时,B的计数变为0,B得到释放,B释放的同时也会使A的计数减一,同时pa析构时使A的计数减一,那么A的计数为0,A得到释放。
注意我们不能通过weak_ptr直接访问对象的方法,比如对象中有一个方法print(),我们不能这样访问

pa->pb_->print();

pb_是一个weak_ptr,应该先把它转化为shared_ptr,如:

shared_ptr p = pa->pb_.lock();
p->print();

12. 内存泄漏?避免的方法?

程序中已动态分配的堆内存由于某种原因没有得到释放或者无法释放,造成系统资源的浪费,导致程序运行速度减慢甚至崩溃
避免的方法:
首先程序员要养成良好习惯,保证malloc/new和free/delete匹配;
检测内存泄漏的关键原理就是,检查malloc/new和free/delete是否匹配,一些工具也就是这个原理。要做到这点,就是利用宏或者钩子,在用户程序与运行库之间加了一层,用于记录内存分配情况。
windows工具:VC自带的CRT:_CrtCheckMemory 调试器和 CRT 调试堆函数,Visual Leak Detector,bounds checker
linux调试工具:MEMWATC Valgrind memcheck

13.为什么析构函数必须是虚函数?为什么默认的析构函数不是虚函数?

将可能被继承的父类的析构函数设置为虚函数,可以保证我们new一个子类对象,然后使用基类指针指向该子类对象时,释放基类指针可以释放子类的空间,防止内存泄漏。
c++默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针,会占用额外的内存。而对于不会被继承的类来说,其析构函数如果是虚函数,就会浪费内存。因此默认的析构函数不是虚函数。而只有需要当作父类时,设置为虚函数。
析构函数与构造函数对应,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统会自动执行析构函数。

析构函数名也应与类名相同,只是在函数名前面加一个位取反符~,例如~stud( ),以区别于构造函数。它不能带任何参数,也没有返回值(包括void类型)。只能有一个析构函数,不能重载

如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。

如果一个类中有指针,且在使用的过程中动态的申请了内存,那么最好显示构造析构函数,在销毁类之前,释放掉申请的内存空间,避免内存泄漏。

类析构顺序:1)派生类本身的析构函数;2)对象成员析构函数;3)基类析构函数。

14. 函数指针

1.定义:指向函数的指针
C编译时,每个函数都有一个入口地址,该入口地址就是函数指针指向的地址。可以用该指针调用函数
2.用途:调用函数和做函数的参数,比如回调函数
3.实例

char *fun(char *p){
    }   //函数指针fun
char *(*pf)(char *p){
    };//函数指针pf
pf = fun;    //函数指针pf指向函数fun
pf(p);       //通过函数指针调用函数fun

15. 静态函数和虚函数的区别

静态函数在编译的时候就已经确定运行时机,虚函数在运行的时候动态绑定。虚函数因为用了虚函数表机制,调用的时候会增加以此内存开销

16. 重载和覆盖(重写)

重载:函数名相同,参数列表不同(个数,类型),返回值没有要求,需要在同一作用域中。
重写:函数名相同,参数列表相同,子类继承父类时重新定义父类中的虚函数

17. 虚函数和多态

静态:函数重载,编译时已经确定。
动态:虚函数机制,运行期间动态绑定。例如:一个父类指针指向子类对象,使用父类的指针调用子类重写过的父类中的虚函数的时候,会调用子类重写后的函数。
虚函数的实现:在有虚函数的类中,类的最开始部分是一个虚函数表的指针,这个指针指向一个虚函数表,表中放了虚函数的地址,实际的虚函数在代码段(.text)中。当子类继承了父类的时候也会继承其虚函数表,当子类重写父类中虚函数时候,会将其继承到的虚函数表中的地址替换为重新写的函数地址。使用了虚函数,会增加访问内存开销,降低效率。

18. 函数的调用

  1. 每一个函数都会分配函数栈,在站内进行函数执行过程。2调用前先把返回地址压栈,然后把当前函数的esp指针压栈。
  2. 函数参数的压栈顺序为从右到左
  3. 处理返回值时,生成一个临时变量,把它的引用作为函数参数传入函数内。
  4. 拷贝赋值函数的形参能否值传递?
    不能,如果是这种情况下,调用拷贝构造函数的时候,首先要将实参传递给形参,传递过程中有啊哟调用拷贝构造函数。形成重复调用,最终栈溢出。

19. new/delete和malloc/free的区别

new/delete是c++的关键字,自动分配内存空间,调用构造函数和析构函数
malloc和free是c语言的库函数,使用必须指明申请内存空间的大小

20. 构造函数,拷贝构造函数,赋值构造函数

20.1 构造函数

构造函数是一种特殊的类成员函数,是当创建一个类的对象时,它被调用来对类的数据成员进行初始化和分配内存。(构造函数的命名必须和类名完全相同)
构造函数可以显式调用,也可以自动调用。

20.2 拷贝构造函数

拷贝构造函数是C++独有的,它是一种特殊的构造函数,用基于同一类的一个对象构造和初始化另一个对象。

当没有重载拷贝构造函数时,通过默认拷贝构造函数来创建一个对象

A a;

A b(a); //都是拷贝构造函数来创建对象b

A b=a;  //都是拷贝构造函数来创建对象b

强调:这里b对象是不存在的,是用a 对象来构造和初始化b的!!

在C++中,3种对象需要复制,此时拷贝构造函数会被调用

1)一个对象以值传递的方式传入函数体

2)一个对象以值传递的方式从函数返回

3)一个对象需要通过另一个对象进行初始化

什么时候编译器会生成默认的拷贝构造函数?

1)如果用户没有自定义拷贝构造函数,并且在代码中使用到了拷贝构造函数,编译器就会生成默认的拷贝构造函数。但如果用户定义了拷贝构造函数,编译器就不在生成。

2)如果用户定义了一个构造函数,但不是拷贝构造函数,而此时代码中又用到了拷贝构造函数,那编译器也会生成默认的拷贝构造函数。

因为系统提供的默认拷贝构造函数工作方式是内存拷贝,也就是浅拷贝。如果对象中用到了需要手动释放的对象,则会出现问题,这时就要手动重载拷贝构造函数,实现深拷贝。

下面说说深拷贝与浅拷贝:

浅拷贝:如果复制的对象中引用了一个外部内容(例如分配在堆上的数据),那么在复制这个对象的时候,让新旧两个对象指向同一个外部内容,就是浅拷贝。(指针虽然复制了,但所指向的空间内容并没有复制,而是由两个对象共用,两个对象不独立,删除空间存在)

深拷贝:如果在复制这个对象的时候为新对象制作了外部对象的独立复制,就是深拷贝。需要手动编写拷贝构造函数实现深拷贝

拷贝构造函数重载声明如下:

**A (const A& other)**

20.3 赋值构造函数

当一个类的对象向该类的另一个对象赋值时,就会用到该类的赋值函数。

当没有重载赋值函数(赋值运算符)时,通过默认赋值函数来进行赋值操作

A a;

A b;

b=a; 

强调:这里a,b对象是已经存在的,是用a 对象来赋值给b的!!

赋值运算的重载声明如下:

 A& operator = (const A& other)

参考原文
1.2.3.

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/DontASK/article/details/99704859

智能推荐

用 Ninja and GN 来加速 C++构建-程序员宅基地

文章浏览阅读1.8k次,点赞2次,收藏4次。NinjaNinja 原意是忍者的意思,它是一个专注于速度的小型构建工具。它是一个构建系统。 它将文件的相互依赖关系(通常是源代码和输出可执行文件)作为输入,并快速编排构建它们。运行Ninja,默认情况下,它会在当前目录中查找名为 build.ninja 的文件并构建所有过时的目标。 您可以指定要构建的目标(文件)作为命令行参数。还有一个特殊的语法 target^ 用于将目标指定为某个规则的..._c++ gn构建

Objective-c 数字对象(NSNumber)_oc 如何判断一个nsnumber对象是一个什么类型的值-程序员宅基地

文章浏览阅读741次。在Objective-c中有int的数据类型,那为什么还要使用数字对象NSNumber?这是因为很多类(如NSArray)都要求使用对象,而int不是对象。NSNumber就是数字对象,我们可以使用NSNumber对象来创建和初始化不同类型的数字对象。如:[cpp] view plaincopy#import NS_oc 如何判断一个nsnumber对象是一个什么类型的值

我做了个AI碎片知识管理工具...-程序员宅基地

文章浏览阅读565次,点赞25次,收藏13次。我做了个AI碎片知识管理工具,只为让你不再为信息爆炸而焦虑。作为一名独立创业者,我每天都要面对大量的信息。各种文章、视频、网页,像潮水般涌来,让我应接不暇。我尝试过很多方法来管理这些信息,比如用笔记软件、收藏夹、标签等等。但这些方法都治标不治本,要么效率低下,要么难以找到关键信息。。。

windows下docker下安装docker-compose以及配置python环境_windows docker-compose-程序员宅基地

文章浏览阅读6.1k次。docker desktop这东西的更新迭代非常快,通常如果安装失败的原因基本上都是因为你看的博客或者视频太老所导致的,其实自己使用的情况下大多数不需要安装Linux虚拟机,只需要通过docker就可以部署一系列分布式应用1.去docker下载 docker desktop,并注册账号,docker登录的时候用,这教程网上一堆,就不做详细介绍了,本文主要介绍海豚调度的python环境搭建使用,顺嘴要说一下,首先进到海豚调度官网。_windows docker-compose

aarch64-linux-gnu-gcc: error: unrecognized argument in option ‘-mabi=aapcs-linux‘-程序员宅基地

文章浏览阅读6k次,点赞2次,收藏5次。aarch64-linux-gnu-gcc: error: unrecognized argument in option '-mabi=aapcs-linux'aarch64-linux-gnu-gcc: note: valid arguments to '-mabi=' are: ilp32 lp64aarch64-linux-gnu-gcc: error: unrecognized c..._gcc: error: unrecognized argument in option ‘-mabi=aapcs-linux’

odoo16 安装-程序员宅基地

文章浏览阅读840次,点赞11次,收藏14次。brew install [email protected] -- odoo16 如果用python3.12 - 会报错。npm install -g less less-plugin-clean-css #使用node安装依赖。brew unlink [email protected] 可以通过这个, 指导选用版本。Pst 安装路径:/opt/homebrew/var/postgresql@14。python3 -m venv odoo-env —创建虚拟环境。./odoo-bin -s -- 启动odoo。

随便推点

如何在Android模拟器中模拟GPS位置?_android 模拟器gpx/kml-程序员宅基地

文章浏览阅读1.2w次。我想在Android模拟器中获取经度和纬度以进行测试。 谁能指导我如何实现这一目标? 如何将仿真器的位置设置为测试位置? _android 模拟器gpx/kml

cisp软考书籍【注册信息安全专业人员培训教材】_cisp书籍-程序员宅基地

文章浏览阅读4.9k次,点赞3次,收藏4次。书名:注册信息安全专业人员培训教材作者:朱胜涛,温哲出版社:北京师范大学出版社出版日期:2019-05-01ISBN:9787303246526版次:1装帧:平装-胶订开本:16开商品重量:0.4kg内容提要本书是中国信息安全测评中心在十多年信息安全职业教育的基础上,结合我国关键信息基础设施安全保障需求,以信息安全保障为核心,按照信息系统生命周期各阶段的工作,划分并构建了信息安全保障、网络安全监管、信息安全管理、信息安全评估、安全工程与运营、业务连续性、信息安全支撑技*、物理与网络通信._cisp书籍

vue3中文件下载_vue3 解析responseentity 完成下载-程序员宅基地

文章浏览阅读6.3k次,点赞3次,收藏12次。vue3中文件下载可以用原生数据流下载(axios)发个请求从后台拿数据流/*** 例如*/ axios({ method:'post',//请求方式 url:url,//请求地址 responseType:'blob'//文件流将会被转成blob }).then(res => { const blob = new Blob([res.data]);//处理文档流 const fileName = '帮助文档.docx'; const dow_vue3 解析responseentity 完成下载

bug宝典JAVA篇 Caused by: org.quartz.JobPersistenceException: Couldn‘t store trigger_couldn't store trigger 'default.mt_1ohl5mq0w32i5' -程序员宅基地

文章浏览阅读3w次,点赞21次,收藏16次。我将定时任务中改动了一个类的路径,结果服务器起来的时候出现这样的错误org.springframework.context.ApplicationContextException: Failed to start bean 'scheduler'; nested exception is org.springframework.scheduling.SchedulingException: Coul_couldn't store trigger 'default.mt_1ohl5mq0w32i5' for 'default.usersessionti

用imu_utils标定IMU-程序员宅基地

文章浏览阅读812次。https://blog.csdn.net/fang794735225/article/details/92804030#commentBox_imu_utils标定原理

pokepay实体visa卡绑定大陆支付宝线上消费测评#usdt #visa #mastercard-程序员宅基地

文章浏览阅读167次。pokepay实体visa卡绑定大陆支付宝线上消费测评#usdt #visa #mastercard https://m.baidu.com/video/page?