当前位置: 首页 > 编程日记 > 正文

C和C++安全编码笔记:动态内存管理

4.1 C内存管理:

C标准内存管理函数:

(1).malloc(size_t size):分配size个字节,并返回一个指向分配的内存的指针。分配的内存未被初始化为一个已知值。

(2).aligned_alloc(size_t alignment, size_t size):为一个对象分配size个字节的空间,此对象的对齐方式是alignment指定的。alignment的值必须是实现支持的一种有效的对齐方式,size的值必须是alignment的整数倍,否则行为就是未定义的。此函数返回一个指向分配的空间的指针,如果分配失败,则返回一个空指针。

(3).realloc(void* p, size_t size):将p所指向的内存块的大小改为size个字节。新大小和旧大小中较小的值那部分内存所包含的内容不变,新分配的内存未作初始化,因此将有不确定的值。如果内存请求不能被成功分配,那么旧的对象保持不变,而且没有值被改变。如果p是一个空指针,则该调用等价于malloc(size);如果size等于0,则该调用等价于free(p),但这种释放内存的用法应该避免。

(4).calloc(size_t nmemb, size_t size):为数组分配内存,(该数组共有nmemb个元素,每个元素的大小为size个字节)并返回一个指向所分配的内存的指针。所分配的内存的内容全部被设置为0。

内存分配函数返回一个指向分配的内存的指针,这块内存是按照任何对象类型恰当地对齐的,如果请求失败,则返回一个空指针。连续调用内存分配函数分配的存储空间的顺序和邻接是不确定的。所分配的对象的生存期从分配开始,到释放时结束。返回的指针指向所分配的空间的起始地址(最低字节地址)。

free(void* p):释放由p指向的内存空间,这个p必须是先前通过调用aligned_alloc、malloc、calloc或realloc返回的。如果引用的内存不是被这些函数之一分配的或free(p)此前已经被调用过,将会导致未定义行为。如果p是一个空指针,则不执行任何操作

由C内存分配函数分配的对象有分配存储期限。存储期限是一个对象的属性,它定义了包含该对象存储的最低潜在生存期。这些对象的生存期并不限于创建它的范围内,因此,如果在一个函数内调用malloc,那么在该函数返回后,已分配的内存仍然存在。

对齐:完整的对象类型有对齐(alignment)要求,这种要求对可以分配该类型对象的地址施加限制。对齐是实现定义的整数值,它表示可以在一个连续的地址之间分配给指定的对象的字节数量。对象类型规定了每一个该类型对象的对齐要求。消除对齐要求,往往需要生成代码进行跨字边界域访问,或从更慢的奇数地址访问,从而减慢内存访问。

完整的对象(complete object):对象中可以包含其它对象,被包含的对象称为子对象(subobject)。子对象可以是成员子对象、基类子对象,或数组元素。如果一个对象不是任何其它对象的子对象,那么它被称为一个完整的对象。

对齐有一个从较弱的对齐到较强的对象(或更严格的对齐)的顺序。越严格的对齐,其对齐值越大。一个地址满足某一种对齐要求,也满足任何更弱的有效对齐要求。char、signed char、unsigned char类型的对齐要求最弱。对齐表示为size_t类型的值。每个有效对齐值都是2的一个非负整数幂。有效的对齐包括基本类型的对齐,加上额外的一组可选的实现定义的值。

基本对齐(fundamental alignment)小于或等于在所有上下文中由编译器支持的最大对齐。max_align_t类型的对齐与在所有上下文中由编译器支持的对齐大小相同。扩展对齐(extended alignment)大于max_align_t类型的对齐。一个具有扩展对齐要求的类型也称为超对齐(overaligned)类型。每个超对齐类型,要么是一个结构或联合类型,其中一个成员已应用扩展对齐,要么它包含这样的类型。如果实现支持,可以使用aligned_alloc函数分配比正常更严格的对齐的内存。如果一个程序要求比alignof(max_align_t)更大的对齐,那么这个程序是不可移植的,因为对超对齐类型的支持是可选的。

void test_aligned_alloc()
{const int arr_size = 11;// 分配16字节对齐的数据
#ifdef _MSC_VERfloat* array = (float*)_aligned_malloc(16, arr_size * sizeof(float));
#elsefloat* array = (float*)aligned_alloc(16, arr_size * sizeof(float));
#endifauto addr = std::addressof(array);fprintf(stdout, "pointer addr: %p\n", addr);fprintf(stdout, "char alignment: %d, float alignment: %d, max_align_t alignment: %d\n",alignof(char), alignof(float), alignof(max_align_t));
}

在C标准中引入_Alignas关键字和aligned_alloc函数的主要理由是支持单指令多数据(SIMD)计算。

4.2 常见的C内存管理错误:常见的与内存管理相关的编程缺陷包括:初始化错误、未检查返回值、对空指针或无效指针解引用、引用已释放的内存、对同一块内存释放多次、内存泄漏和零长度分配。

初始化错误:由malloc函数返回的空间中的值是不确定的。一个常见的错误是不正确地假设malloc把分配的内存的所有位都初始化为零。

// 读取未初始化的内存
void test_memory_init_error()
{// 初始化大的内存块可能会降低性能并且不总是必要的.// C标准委员会决定不需要malloc来初始化这个内存,而把这个决定留给程序员int n = 5;int* y = static_cast<int*>(malloc(n * sizeof(int)));int A[] = {1, 2, 3, 4, 5};for (int i = 0; i < n; ++i) {y[i] += A[i];}std::for_each(y, y+n, [](int v) { fprintf(stdout, "value: %d\n", v); });free(y);
}

不要假定内存分配函数初始化内存。不要引用未初始化的内存

清除或覆写内存通常是通过调用C标准的memset函数来完成的。遗憾的是,如果不在写后访问内存,编译器优化可能会默默地删除对memset函数的调用。

未检查返回值:内存分配函数的返回值表示分配失败或成功。如果请求的内存分配失败,那么aligned_alloc、calloc、malloc和realloc函数返回空指针

// 检查malloc的返回值
int* test_memory_return_value()
{// 如果不能分配请求的空间,那么C内存分配函数将返回一个空指针int n = 5;int* ptr = static_cast<int*>(malloc(sizeof(int) * n));if (ptr != nullptr) {memset(ptr, 0, sizeof(int) * n);} else {fprintf(stderr, "fail to malloc\n");return nullptr;}return ptr;
}

Null或无效指针解引用:用一元操作符”*”解引用的指针的无效值包括:空指针、未按照指向的对象类型正确对齐的地址、生存期结束后的对象的地址。

空指针的解引用通常会导致段错误,但并非总是如此。许多嵌入式系统有映射到地址0处的寄存器,因此覆写它们会产生不可预知的后果。在某些情况下,解引用空指针会导致任意代码的执行

引用已释放内存:除非指向某块内存的指针已设置为NULL或以其它方式被覆写,否则就有可能访问已被释放的内存。

// 引用已释放内存
void test_memory_reference_free()
{int* x = static_cast<int*>(malloc(sizeof(int)));*x = 100;free(x);// 从已被释放的内存读取是未定义的行为fprintf(stderr, "x: %d\n", *x);// 写入已经被释放的内存位置,也不大可能导致内存故障,但可能会导致一些严重的问题*x = -100;fprintf(stderr, "x: %d\n", *x);
}

从已被释放的内存读取是未定义的行为,但在没有内存故障时几乎总能成功,因为释放的内存是被内存管理器回收的。然而,并不保证内存的内容没有被篡改过。虽然free函数调用通常不会擦除内存,但内存管理器可能使用这个空间的一部分来管理释放或未分配的内存。如果内存块已被重新分配,那么其内容可能已经被全部替换。其结果是,这些错误可能检测不出来,因为内存中的内容可能会在测试过程中被保留,但在运行过程中被修改。

写入已经被释放的内存位置,也不太可能导致内存故障,但可能会导致一些严重的问题。如果该块内存已被重新分配,程序员就可以覆写此内存,一个内存块是专门(dedicated)为一个特定的变量分配的,但在现实中,它是被共享(shared)的。在这种情况下,该变量中包含最后一次写入的任何数据。如果那块内存没有被重新分配,那么写入已释放的块可能会覆写并损坏内存管理器所使用的数据结构。

多次释放内存:最常见的场景是两次释放(double-free)。这个错误是危险的,因为它会以一种不会立即显现的方式破坏内存管理器中的数据结构。

// 多次释放内存
void test_memory_multi_free()
{int* x = static_cast<int*>(malloc(sizeof(int)));free(x);// 多次释放相同的内存会导致可以利用的漏洞free(x);
}

内存泄漏:当动态分配的内存不再需要后却没有被释放时,就会发生内存泄漏。

零长度分配:C标准规定:如果所要求的空间大小是零,其行为是实现定义的:要么返回一个空指针,要么除了不得使用返回的指针来访问对象以外,行为与大小仿佛是某个非零值。

// 零长度分配:不要执行零长度分配
void test_memory_0_byte_malloc()
{char* p1 = static_cast<char*>(malloc(0));fprintf(stderr, "p1 pointer: %p\n", std::addressof(p1)); // 是不确定的free(p1);p1 = nullptr;char* p2 = static_cast<char*>(realloc(p1, 0));fprintf(stderr, "p2 pointer: %p\n", std::addressof(p2)); // 是不确定的free(p2);int nsize = 10;char* p3 = static_cast<char*>(malloc(nsize));char* p4 = nullptr;// 永远不要分配0个字节if ((nsize == 0) || (p4 = static_cast<char*>(realloc(p3, nsize))) == nullptr) {free(p3);p3 = nullptr;return;}p3 = p4;free(p3);
}

此外,要求分配0字节时,成功调用内存分配函数分配的存储量是不确定的。在内存分配函数返回一个非空指针的情况下,读取或写入分配的内存区域将导致未定义的行为。通常情况下,指针指向一个完全由控制结构组成的零长度的内存块。覆写这些控制结构损害内存所使用的数据结构。

realloc函数将释放旧对象,并返回一个指针,它指向一个具有指定大小的新对象。然而,如果不能为新对象分配内存,那么它就不释放旧对象,而且旧对象的值是不变的。正如malloc(0),realloc(p, 0)的行为是实现定义的。

不要执行零长度分配

4.3 C++的动态内存管理:在C++中,使用new表达式分配内存并使用delete表达式释放内存。C++的new表达式分配足够的内存来保存所请求类型的对象,并可以初始化所分配的内存中的对象。

new表达式是构造一个对象的唯一方法,因为不可能显示地调用构造函数。分配的对象类型必须是一个完整的对象类型,并且不可以(例如)是一个抽象类类型或一个抽象类数组。对于非数组对象,new表达式返回一个指向所创建的对象的指针;对于数组,它返回一个指向数组初始元素的指针。new表达式分配的对象有动态存储期限(dynamic storage duration)。存储期限定义了该对象包含的存储的生存期。使用动态存储的对象的生存期,不局限于创建该对象所在的范围。

如果提供了初始化参数(即类的构造函数的参数,或原始整数类型的合法值),那么由new操作符所分配的内存被初始化。只有一个空的new-initializer()存在时,”普通的旧数据”(POD)类型的对象是new默认初始化(清零)的。这包括所有的内置类型。

void test_memory_new_init()
{// 包括所有的内置类型int* i1 = new int(); // 已初始化int* i2 = new int; // 未初始化fprintf(stdout, "i1: %d, i2: %d\n", *i1, *i2);// 就地new没有实际分配内存,所以该内存不应该被释放int* i3 = new (i1) int;fprintf(stdout, "i3: %d\n", *i3);delete i1;delete i2;// 通常情况下,分配函数无法分配存储时抛出一个异常表示失败int* p1 = nullptr;try {p1 = new int;} catch (std::bad_alloc) {fprintf(stderr, "fail to new\n");return;}delete p1;// 用std::nothrow参数调用new,当分配失败时,分配函数不会抛出一个异常,它将返回一个空指针int* p2 = new(std::nothrow) int;if (p2 == nullptr) {fprintf(stderr, "fail to new\n");return;}delete p2;
}

就地new(placement new)是另一种形式的new表达式,它允许一个对象在任意内存位置构建。就地new需要在指定的位置有足够的内存可用。因为就地new没有实际分配内存,所以该内存不应该被释放。

分配函数:必须是一个类的成员函数或全局函数,不能在全局范围以外的命名空间范围中声明,并且不能把它在全局范围内声明为静态的。分配函数的返回类型是void*。

分配函数试图分配所请求的存储量。如果分配成功,则它返回存储块的起始地址,该块的长度(以字节为单位)至少为所要求的大小。分配函数返回的分配的存储空间内容没有任何限制。连续调用分配函数分配的存储的顺序、连续性、初始值都是不确定的。返回的指针是适当地对齐的,以便它可以被转换为任何具有基本对齐要求的完整对象类型的指针,并在之后用于访问所分配的存储中的对象或数组(直到调用一个相应的释放函数显示释放这块存储)。即使所请求的空间大小为零,请求也可能会失败。如果请求成功,那么返回值是一个非空指针值。如果一个指针是大小为零的请求返回的,那么对它解引用的效果是未定义的。C++在发起一个为零的请求时行为与C不同,它返回一个非空指针

通常情况下,分配函数无法分配存储时抛出一个异常表示失败,这个异常将匹配类型为std::bad_alloc的异常处理器。

如果用std::nothrow参数调用new,当分配失败时,分配函数不会抛出一个异常。相反,它将返回一个空指针

当一个异常被抛出时,运行时机制首先在当前范围内搜索合适的处理器。如果当前范围内没有这样的处理程序存在,那么控制权将由当前范围转移到调用链中的一个更高的块。这个过程一直持续,直到找到一个合适的处理程序为止。如果在任何级别中都没有处理程序捕获该异常,那么std::terminate函数被自动调用。默认情况下,terminate调用标准C库函数abort,它会突然退出程序。当abort被调用时,没有正常的程序终止函数调用发生,这意味着全局和静态对象的析构函数不执行。

在C++中,处理分配和分配失败的标准惯用法是资源获取初始化(Resource Acquisition Is Initializatiton, RAII)。RAII运用C++的对象生存期概念控制程序资源,如内存、文件句柄、网络连接、审计跟踪等。要保持对资源的跟踪,只要创建一个对象,并把资源的生存期关联到对象的生存期即可。这使你可以使用C++对象管理设施来管理资源。其最简单的形式是,创建一个对象,它在构造函数获得资源,并在其析构函数中释放资源。

class intHandle {
public:explicit intHandle(int* anInt) : i_(anInt) {} // 获取资源~intHandle() { delete i_; } // 释放资源intHandle& operator=(const int i){*i_ = i;return *this;}int* get() { return i_; } // 访问资源private:intHandle(const intHandle&) = delete;intHandle& operator=(const intHandle&) = delete;int* i_;
};// 资源获取初始化(Resource Acquisition Is Initialization, RAII)
void test_memory_arii()
{intHandle ih(new int);ih = 5;fprintf(stdout, "value: %d\n", *ih.get());// 使用std::unique_ptr能完成同样的事情,而且更简单std::unique_ptr<int> ip(new int);*ip = 5;fprintf(stdout, "value: %d\n", *ip.get());
}

如果发生下列任何情况,new表达式抛出std::bad_array_new_length异常,以报告无效的数组长度:(1).数组的长度为负;(2).新数组的总大小超过实现定义的最大值;(3).在一个大括号初始化列表中的初始值设定子句数量超过要初始化的元素数量(即声明的数组的大小)。只有数组的第一个维度可能会产生这个异常,除第一个维度外的维度都是常量表达式,它们在编译时检查。

// 抛出std::bad_array_new_length的三种情况
void test_memory_bad_array_new_length()
{try {int negative = -1;new int[negative]; // 大小为负} catch(const std::bad_array_new_length& e) {fprintf(stderr, "1: %s\n", e.what());}try {int small = 1;new int[small]{1, 2, 3}; // 过多的初始化值设定} catch(const std::bad_array_new_length& e) {fprintf(stderr, "2: %s\n", e.what());}try {int large = INT_MAX;new int[large][1000000]; // 过大} catch(const std::bad_alloc& e) {fprintf(stderr, "3: %s\n", e.what());}
}

释放函数:是类的成员函数或全局函数,在一个全局范围以外的命名空间范围声明释放函数或全局范围内声明静态的释放函数都是不正确的。每个释放函数都返回void,并且它的第一个参数是void*。对于这些函数的两个参数形式,第一个参数是一个指向需要释放的内存块的指针,第二个参数是要释放的字节数。这种形式可能被用于从基类中删除一个派生类对象。提供给一个释放函数的第一个参数的值可以是一个空指针值,如果是这样的话,并且如果释放函数是标准库提供的,那么该调用没有任何作用

如果提供给标准库中的一个释放函数的参数是一个指针,且它不是空指针值,那么释放函数释放该指针所引用的存储,引用指向已释放存储(deallocated storage)的任何部分的所有指针无效。使用无效的指针值(包括将它传递给一个释放函数)产生的影响是未定义的。

垃圾回收:在C++中,垃圾回收(自动回收不再被引用的内存区域)是可选的,也就是说,一个垃圾回收器(Garbage Collector, GC)不是必须的。一个垃圾回收器必须能够识别动态分配的对象的指针,以便它可以确定哪些对象可达(reachable),不应该被回收,哪些对象不可达(unreachable),并可以回收。

4.4 常见的C++内存管理错误:常见的与内存管理相关的编程缺陷,包括未能正确处理分配失败、解引用空指针、写入已经释放的内存、对相同的内存释放多次、不当配对的内存管理函数、未区分标量和数组,以及分配函数使用不当。

未能正确检查分配失败:new表达式要么成功要么抛出一个异常。new操作符的nothrow形式在失败时返回一个空指针,而不是抛出一个异常。

// 未能正确检查分配失败
void test_memory_new_wrong_usage()
{// new表达式,要么成功,要么抛出一个异常// 意味着,if条件永远为真,而else子句永远不会被执行int* ip = new int;if (ip) { // 条件总是为真} else {// 将永远不执行}delete ip;// new操作符的nothrow形式在失败时返回一个空指针,而不是抛出一个异常int* p2 = new(std::nothrow)int;if (p2) {delete p2;} else {fprintf(stderr, "fail to new\n");}
}

不正确配对的内存管理函数:使用new和delete而不是原始的内存分配和释放。C内存释放函数std::free不应该被用于由C++内存分配函数分配的资源,C++内存释放操作符和函数也不应该被用于由C内存分配函数分配的资源。C++的内存分配和释放函数分配和释放内存的方式可能不同于C内存分配和释放函数分配和释放内存的方式。因此,在同一资源上混合调用C++内存分配和释放函数及C内存分配和释放函数是未定义的行为,并可能会产生灾难性的后果。

class Widget {};// 不正确配对的内存管理函数
void test_memory_new_delete_unpaired()
{int* ip = new int(12);free(ip); // 错误,应使用delete ipint* ip2 = static_cast<int*>(malloc(sizeof(int)));*ip2 = 12;delete ip2; // 错误,应使用free(ip2)// new和delete操作符用于分配和释放单个对象Widget* w = new Widget();delete w;// new[]和delete[]操作符用于分配和释放数组Widget* w2 = new Widget[10];delete [] w2;// operator new()分配原始内存,但不调用构造函数std::string* sp = static_cast<std::string*>(operator new(sizeof(std::string)));//delete sp; // 错误operator delete (sp); // 正确
}

在C++的new表达式分配的对象上调用free,因为free不会调用对象的析构函数。这样的调用可能会导致内存泄漏、不释放锁或其它问题,因为析构函数负责释放对象所使用的资源。

new和delete操作符用于分配和释放单个对象;new[]和delete[]操作符用于分配和释放数组。

当分配单个对象时,先调用operator new()函数来分配对象的存储空间,然后调用其构造函数来初始化它。当一个对象被删除时,首先调用它的析构函数,然后调用相应的operator delete()函数来释放该对象所占用的内存。当分配一个对象数组时,先调用operator new[]()对整个数组分配存储空间,随后调用对象的构造函数来初始化数组中的每个元素。当删除一个对象数组时,首先调用数组中的每个对象的析构函数,然后调用operator delete[]()释放整个数组所占用的内存。要将operator delete()与operator new()一起使用,并将operator delete[]()与operator new[]()一起使用。如果试图使用operator delete()删除整个数组,只有数组的第一个元素所占用的内存将被释放,一个明显的内存泄漏可能会导致被利用。

new和operator new():可以直接调用operator new()分配原始内存,但不调用构造函数

函数operator new()、operator new[]()、operator delete()和operator delete[]()都可以被定义为成员函数。它们是隐藏继承的或命名空间范围中同名函数的静态成员函数。与其它内存管理函数一样,重要的是让它们正确配对。如果对象所使用的内存不是通过调用operator new()获得的,同时对其使用operator delete(),就可能发生内存损坏。

多次释放内存:智能指针是一个类类型(class type),它具有重载的”->”和”*”操作符以表现得像指针。比起原始指针,智能指针往往是一种更安全的选择,因为它们可以提供原始指针中不存在的增强行为,如垃圾回收、检查空,而且防止使用在特定情况下不合适或危险的原始指针操作(如指针算术和指针复制)。引用计数智能指针对它们所引用的对象的引用计数进行维护。当引用计数为零时,该对象就被销毁。

释放函数抛出一个异常:如果释放函数通过抛出一个异常终止,那么该行为是未定义的。释放函数,包括全局的operator delete()函数,它的数组形式与其用户定义的重载,经常在销毁类类型的对象时被调用,其中包括作为某个异常结果的栈解开。允许栈解开期间抛出异常导致逃避了调用std::terminate,而导致std::abort函数调用的默认效果。这种情况可能被利用为一种拒绝服务攻击的机会。因此,释放函数必须避免抛出异常

4.5 内存管理器:既管理已分配的内存,也管理已释放的内存。在大多数操作系统中,包括POSIX系统和Windows,内存管理器作为客户进程的一部分运行。分配给客户进程的内存,以及供内部使用而分配的内存,全部位于客户进程的可寻址内存空间内。操作系统通常提供内存管理器作为它的一部分(通常是libc的一部分)。在较不常见的情况下,编译器也可以提供替代的内存管理器。内存管理器可以被静态链接在可执行文件中,也可以在运行时确定。

4.6 Doug Lea的内存分配器:GNU C库和大多数Linux版本(例如Red Hat、Debian)都是将Doug Lea的malloc实现(dlmalloc)作为malloc的默认原生版本。Doug Lea独立地发布了dlmalloc,其他一些人对其作了修改并用作GNU libc的分配器。

动态分配的内存也可能遭遇缓冲区溢出。例如,缓冲区溢出可被用于破坏内存管理器所使用的数据结构从而能够执行任意的代码。

解链(unlink)技术:最早由Solar Designer提出。unlink技术被用于利用缓冲区溢出来操纵内存块的边界标志,以欺骗unlink()宏向任意位置写入4字节数据。

4.7 双重释放漏洞:Doug Lea的malloc还易于导致双重释放漏洞。这种类型的漏洞是由于对同一块内存释放两次造成的(在这两次释放之间没有对内存进行重新分配)。要成功地利用双重释放漏洞,有两个条件必须满足:被释放的内存块必须在内存中独立存在(也就是说,其相邻的内存块必须是已分配的,这样就不会发生合并操作了),并且该内存所被放入的筐(在dlmalloc中,空闲块被组织成环形双链表,或筐(bin))必须为空。

写入已释放的内存:一个常见的安全缺陷。

RtlHeap:并非只有使用dlmalloc开发的应用程序才可能存在基于堆的漏洞。使用微软RtlHeap开发的应用程序在内存管理API被误用时也有可能被利用。与大多数软件一样,RtlHeap也在不断地进化,不同的Windows版本通常都有不同的RtlHeap实现,它们的行为稍有不同。

4.8 缓解策略:有很多缓解措施可以用来消除或减少基于堆的漏洞。

空指针:一个明显的可以减少C和C++程序中漏洞数量的技术就是在指针所引用的内存被释放后,将此指针设置为NULL。空悬指针(执行已释放内存的指针)可能导致赋写已释放内存和双重释放漏洞。将指针置为NULL后,任何企图解引用该指针的操作都会导致致命的错误,这样就增加了在实现和测试过程中发现问题的几率。并且,如果指针被设置为NULL,内存可以被”释放”多次而不会导致不良后果

一致的内存管理约定:(1).使用同样的模式分配和释放内存;(2).在同一个模块中,在同一个抽象层次中分配和释放内存;(3).让分配和释放配对。

随机化:传统的malloc函数调用返回的内存分配地址在很大程度上是可预测的。通过让内存管理程序返回的内存块地址随机化,可以使对基于堆的漏洞利用变得更加困难。

运行时分析工具:Valgrind、Purify、Insure++、Application Verifier。

GitHub:https://github.com/fengbingchun/Messy_Test

相关文章:

作为一名程序员,数学到底对你有多重要?

最近在知乎上看到一个贴子&#xff0c;看完后我沉默了.....沉思后想想&#xff0c;其实每个行业都会分等级&#xff0c;程序员也不例外&#xff01;说好听一点的叫工程师&#xff0c;普通一点的叫程序员&#xff0c;差一点的叫码农&#xff0c;更差的还会叫码畜&#xff0c;码奴…

经典SQL(sqlServer)

一、基础 1、说明&#xff1a;创建数据库CREATE DATABASE database-name 2、说明&#xff1a;删除数据库drop database dbname3、说明&#xff1a;备份sql server--- 创建 备份数据的 deviceUSE masterEXEC sp_addumpdevice disk, testBack, c:\mssql7backup\MyNwind_1.dat--- …

iOS UITextField输入框随键盘弹出界面上移

//点击输入框界面跟随键盘上移 - (void)textFieldDidBeginEditing:(UITextField *)textField { CGRect frame textField.frame; int offSet frame.origin.y 70 - (self.view.frame.size.height - 216.0); //iphone键盘高度为216.iped键盘高度为352 [UIView beginAnimations:…

IEEE分享 | 机器学习在领英的规模化应用

人工智能和机器学习仍然是全球持续增长的领域之一&#xff0c;近年来涌现出越来越多本科生或者非人工智能专业出身的工程师&#xff0c;他们努力学习和使用技术来改进产品&#xff0c;几乎每天都有新的机器学习技术和框架发布。这篇文章将讨论领英如何规模化利用技术&#xff0…

GitHub/GitLab/Gitee中项目互拷贝后仍保留历史提交记录的方法

GitHub、GitLab、Gitee等在同一个网站中执行复制或拷贝一个已有项目到一个新项目比较简单&#xff0c;因为它们在每一个项目上都有一个Fork按钮&#xff0c;直接点击此Fork按钮即可&#xff0c;Fork后的新项目会保留原有项目的历史提交记录。但是如果不在同一个网站上进行此操作…

基于mimeTex的数学公式Webservice的部署和实现

通过Latex语法&#xff0c;实现生成数学公式的解决方案也很多。这里介绍一种方法&#xff0c;使用开源的mimeTex。该项目的官网地址如下&#xff1a;http://www.forkosh.com/mimetex.html网站主页有一个声明。如果你的服务器上已经安装了latex&#xff0c;那么推荐使用mathTex&…

对称加密算法AES之GCM模式简介及在OpenSSL中使用举例

AES(Advanced Encryption Standard)即高级加密标准&#xff0c;由美国国家标准和技术协会(NIST)于2000年公布&#xff0c;它是一种对称加密算法。关于AES的更多介绍可以参考&#xff1a;https://blog.csdn.net/fengbingchun/article/details/100139524 AES的GCM(Galois/Counte…

iOS UITextField清空按钮

extField.clearButtonModeUITextFieldViewModeWhileEditing; 就可以了&#xff0c;表明编辑输入框的时候启动一键清空按钮。另外&#xff0c;clearButtonMode还有三个属性&#xff1a; UITextFieldViewModeNever, 清空按钮永不出现 UITextFieldViewModeUnlessEditing, 不编…

腾讯“疯狂”开源

作者 | 马超责编 | 胡巍巍出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;近日&#xff0c;腾讯自研的万亿级分布式消息中间件TubeMQ正式开源&#xff0c;并捐赠给Apache基金会&#xff0c;成为基金会官方认可的Incubator项目。我们知道与TubeMQ功能类似的kafka是领…

[Android]开发摇一摇分歧表决器过程

心血来潮&#xff0c;走进Android&#xff0c;准备开发一个摇一摇分歧表决器&#xff08;PS&#xff1a;这个想法源自去年看的一个都市剧《约会专家》中主人公杭杭开发的分歧表决器APP&#xff09;。简述&#xff1a;摇一摇分歧表决器是一款Android App&#xff0c;通过将传统的…

C和C++安全编码笔记:整数安全

5.1 整数安全导论&#xff1a;整数由包括0的自然数(0, 1, 2, 3, …)和非零自然数的负数(-1, -2, -3, …)构成。 5.2 整数数据类型&#xff1a;整数类型提供了整数数学集合的一个有限子集的模型。一个具有整数类型的对象的值是附着在这个对象上的数学值。一个具有整数类型的对象…

8.3折特惠票仅剩3天!「2019 嵌入式智能国际大会」全日程大公开!

8.3折特惠票仅剩3天立即抢购&#xff1a;https://t.csdnimg.cn/otBk还有5天&#xff0c;大伙期待的「2019嵌入式智能国际大会」正式开幕了&#xff01;2019年12月6日-7日&#xff0c;我们在深圳市人才研修院见&#xff01;大会以“万物互联泛在智能”为主题&#xff0c;邀请30位…

iOS点击空白收回键盘

//点击空白收回键盘 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self.view endEditing:YES]; }

第一个net-mvc程序

结构 视图层 <% Page Language"C#" Inherits"System.Web.Mvc.ViewPage<dynamic>" %><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">…

iOS 储存用户信息设置封装 直接调用即可(部分是代码片段)

、、、、、、、、、、、、、、、、、、首先 定义 UserInfo #import <Foundation/Foundation.h> interface UserInfo : NSObject //用户id property (nonatomic, strong) NSString *userID; //用户名 property (nonatomic, strong) NSString *userName; //密码 propert…

ASN.1简介及OpenSSL中ASN.1接口使用举例

ASN.1(Abstract Syntax Notation One)是一套标准&#xff0c;是描述数据的表示、编码传输、解码的灵活的记法。它提供了一套正式、无歧义和精确的规则以描述独立于特定计算机硬件的对象结构。OpenSSL的编码方法就是基于该标准。ASN.1是一种结构化的数字对象描述语言&#xff0c…

谁是当今最顶级的技术?SQL、Java、Python、C++ 皆上榜!

【12月公开课预告】&#xff0c;入群直接获取报名地址12月11日晚8点直播主题&#xff1a;人工智能消化道病理辅助诊断平台——从方法到落地12月12日晚8点直播&#xff1a;利用容器技术打造AI公司技术中台12月17日晚8点直播主题&#xff1a;可重构计算&#xff1a;能效比、通用性…

将表里的数据批量生成INSERT语句的存储过程 增强版

原文:将表里的数据批量生成INSERT语句的存储过程 增强版将表里的数据批量生成INSERT语句的存储过程 增强版 有时候&#xff0c;我们需要将某个表里的数据全部或者根据查询条件导出来&#xff0c;迁移到另一个相同结构的库中 目前SQL Server里面是没有相关的工具根据查询条件来生…

通过OpenSSL的接口实现Base64编解码

对openssl genrsa产生的rsa私钥pem文件&#xff0c;使用普通的base64解码会有问题&#xff0c;如使用https://blog.csdn.net/fengbingchun/article/details/85218653 中介绍的方法&#xff0c;一是有可能不能从返回的结果中直接使用strlen来获得最终字符的大小&#xff0c;因为…

激辩:机器究竟能否理解常识?

【12月公开课预告】&#xff0c;入群直接获取报名地址12月11日晚8点直播主题&#xff1a;人工智能消化道病理辅助诊断平台——从方法到落地12月12日晚8点直播&#xff1a;利用容器技术打造AI公司技术中台12月17日晚8点直播主题&#xff1a;可重构计算&#xff1a;能效比、通用性…

Mac OS X 下Node.js开发环境的搭建

1.安装Xcode2.安装Homebrew 谷歌搜索Homebrew 复制命令行 打开终端 粘贴命令行 点击回车 安装 输入密码等2.安装Nodejs利用Homebrew安装nodejs打开终端 输入 &#xff1a;brew install nodejs 回车查询nodejs版本&#xff1a;node --version3.安装文档数据库 MongoDB打开终…

.NET 使用 MySql.Data.dll 动态库操作MySql的帮助类--MySqlHelper

.NET 使用 MySql.Data.dll 动态库操作MySql的帮助类--MySqlHelper 參考演示样例代码&#xff0c;例如以下所看到的&#xff1a; /// <summary>/// MySql 数据库操作类/// </summary>public class MySqlHelper{/// <summary>/// MysqlConnection/// </summ…

Instagram个性化推荐工程中三个关键技术是什么?

作者 | Ivan Medvedev&#xff0c;Haotian Wu&#xff0c;Taylor Gordon译者 | 陆离编辑 | Jane出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09; 【导语】近期&#xff0c;Facebook 在博客上分享了第一篇详细介绍 Explore 系统关键技术&#xff0c;以及 I…

iOS UIbutton 点击无反应的几种情况

1、UIButton不能点击情况的第一种是&#xff0c;你将button添加到一个不能响应点击事件的View里。如你将button添加到UIImageView中&#xff0c;解决办法只需将UIImageView的 userInteractionEnabled设为YES即可。 例如&#xff1a; self.headImgV [[UIImageView alloc] ini…

C和C++安全编码笔记:格式化输出

C标准中定义了一些可以接受可变数量参数的格式化输出参数&#xff0c;参数中包括一个格式字符串。printf()和sprintf()都是格式化输出函数的例子。格式化输出函数是由一个格式字符串和可变数目的参数构成的。在效果上&#xff0c;格式化字符串提供了一组可以由格式化输出函数解…

谈谈UI架构设计的演化

谈谈UI架构设计的演化 经典MVC 在1979年&#xff0c;经典MVC模式被提出。 在当时&#xff0c;人们一直试图将纯粹描述思维中的对象与跟计算机环境打交道的代码隔离开来&#xff0c;而Trygve Reenskaug在跟一些人的讨论中&#xff0c;逐渐剥离出一系列的概念&#xff0c;最初是T…

JWT(JSON Web Token)简介及实现

JWT(JSON Web Token)&#xff1a;是一个开放标准(RFC 7519)&#xff0c;它定义了一种紧凑且自包含的方式&#xff0c;用于在各方之间作为Json对象安全地传输信息。由于此信息是经过数字签名的&#xff0c;因此可以被验证和信任。可以使用HMAC SHA256或RSA等对JWT进行签名。 JW…

iOS UIImageView 加载含有汉字的url处理方法

NSString *url [model.pic stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; [self.headImgView sd_setImageWithURL:[NSURL URLWithString:url]];

《评人工智能如何走向新阶段》后记

由AI科技大本营下载自视觉中国自《评人工智能如何走向新阶段》一文发表&#xff08;在内部&#xff09;后&#xff0c;引来了中外专家、草根们的广泛议论&#xff0c;有深有浅&#xff0c;其中似有一些思考价值&#xff0c;故录入本文后记&#xff1a; 中外专家、草根们23条议…

用XCA(X Certificate and key management)可视化程序管理SSL 证书(3)--创建自己定义的凭证管理中心(Certificate Authority)...

在第“用XCA&#xff08;X Certificate and key management&#xff09;可视化程序管理SSL 证书&#xff08;2&#xff09;---创建证书请求”章节中&#xff0c;我们介绍了怎样用XCA创建SSL证书请求&#xff08;Certificate Request&#xff09;&#xff0c;在一章节中&#xf…