caffe代码阅读4:LayerRegistry的介绍与实现_caffe layerregistry-程序员宅基地

技术标签: caffe  

一、LayerRegistry的作用简介

LayerResistry的功能很简单,就是将类和对应的字符串类型放入到一个map当中去,以便灵活调用。主要就是注册类的功能

二、LayerRegistry类的详细介绍


1)构造函数和析构函数

构造函数 
[cpp]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1. // 禁止实例化,因为该类都是静态函数,所以是私有的  
  2.   LayerRegistry() {}  

2)类型定义

[cpp]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1.  // 函数指针Creator,返回的是Layer<Dtype>类型的指针  
  2. typedef shared_ptr<Layer<Dtype> > (*Creator)(const LayerParameter&);  
  3. // CreatorRegistry是字符串与对应的Creator的映射  
  4. typedef std::map<string, Creator> CreatorRegistry;  

3)成员函数

3-1 加入一个Creator到注册表

[cpp]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1. // 给定类型,以及函数指针,加入到注册表  
  2. static void AddCreator(const string& type, Creator creator) {  
  3.   CreatorRegistry& registry = Registry();  
  4.   CHECK_EQ(registry.count(type), 0)  
  5.       << "Layer type " << type << " already registered.";  
  6.   registry[type] = creator;  
  7. }  

3-2 给定层的类型,创建层

这个创建层在net.cpp中会用到,在初始化整个网络的时候会根据参数文件中的层的类型去创建该层的实例
[cpp]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1. static shared_ptr<Layer<Dtype> > CreateLayer(const LayerParameter& param) {  
  2.   if (Caffe::root_solver()) {  
  3.     LOG(INFO) << "Creating layer " << param.name();  
  4.   }  
  5.   // 从参数中获得类型字符串  
  6.   const string& type = param.type();  
  7.   // 获得注册表指针  
  8.   CreatorRegistry& registry = Registry();  
  9.   // 测试是否查找到给定type的Creator  
  10.   CHECK_EQ(registry.count(type), 1) << "Unknown layer type: " << type  
  11.       << " (known types: " << LayerTypeListString() << ")";  
  12.   // 调用对应的层的Creator函数  
  13.   return registry[type](param);  
  14. }  

3-3 返回层的类型列表

[cpp]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1. static vector<string> LayerTypeList() {  
  2.   // 获得注册表  
  3.   CreatorRegistry& registry = Registry();  
  4.   vector<string> layer_types;  
  5.   // 遍历注册表压入layer_types字符串容器  
  6.   for (typename CreatorRegistry::iterator iter = registry.begin();  
  7.        iter != registry.end(); ++iter) {  
  8.     layer_types.push_back(iter->first);  
  9.   }  
  10.   return layer_types;  
  11. }  

3-4 返回一个string,就是把所有的类型都拼起来用逗号分隔形成一个字符串

[cpp]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1.   static string LayerTypeListString() {  
  2.     vector<string> layer_types = LayerTypeList();  
  3.     string layer_types_str;  
  4.     for (vector<string>::iterator iter = layer_types.begin();  
  5.          iter != layer_types.end(); ++iter) {  
  6.       if (iter != layer_types.begin()) {  
  7.         layer_types_str += ", ";  
  8.       }  
  9.       layer_types_str += *iter;  
  10.     }  
  11.     return layer_types_str;  
  12.   }  
  13. };  

3-5 获取注册表(静态的,第一次的时候才new,以后都是直接return的)

[cpp]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1. // 产生一个CreatorRegistry映射的的实例赋值给g_registry_  
  2.   // 表示内部的注册表  
  3.   // 静态函数,第一次的时候会new然后return,其余时间都是return  
  4.   static CreatorRegistry& Registry() {  
  5.     static CreatorRegistry* g_registry_ = new CreatorRegistry();  
  6.     return *g_registry_;  
  7.   }  

3-6此外还定义了一个层注册器

[cpp]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1. // LayerRegisterer  
  2. // 自己定义层的注册器  
  3. // 以供后面的宏进行使用  
  4. template <typename Dtype>  
  5. class LayerRegisterer {  
  6.  public:  
  7.   // 层的注册器的构造函数  
  8.   LayerRegisterer(const string& type,  
  9.                   shared_ptr<Layer<Dtype> > (*creator)(const LayerParameter&)) {  
  10.     // LOG(INFO) << "Registering layer type: " << type;  
  11.     // 还是调用的层注册表中的加入Creator函数加入注册表  
  12.     LayerRegistry<Dtype>::AddCreator(type, creator);  
  13.   }  
  14. };  

三、其他:

为了方便作者还弄了个宏便于注册自己写的层类

[cpp]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1. #define REGISTER_LAYER_CREATOR(type, creator)                                  \  
  2.   static LayerRegisterer<float> g_creator_f_##type(#type, creator<float>);     \  
  3.   static LayerRegisterer<double> g_creator_d_##type(#type, creator<double>)    \  
  4. #define REGISTER_LAYER_CLASS(type)                                             \  
  5.   template <typename Dtype>                                                    \  
  6.   shared_ptr<Layer<Dtype> > Creator_##type##Layer(const LayerParameter& param) \  
  7.   {                                                                            \  
  8.     return shared_ptr<Layer<Dtype> >(new type##Layer<Dtype>(param));           \  
  9.   }                                                                            \  
  10.   REGISTER_LAYER_CREATOR(type, Creator_##type##Layer)  
下面对该宏进行详细解释:
[cpp]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1. // 生成g_creator_f_type(type, creator<Dtype>)的两个函数 (double和float类型)  
  2. #define REGISTER_LAYER_CREATOR(type, creator)                                  \  
  3.   static LayerRegisterer<float> g_creator_f_##type(#type, creator<float>);     \  
  4.   static LayerRegisterer<double> g_creator_d_##type(#type, creator<double>)    \  
  5.   
  6. // 注册自己定义的类,类名为type,  
  7. // 假设比如type=bias,那么生成如下的代码  
  8. // 下面的函数直接调用你自己的类的构造函数生成一个类的实例并返回  
  9. // CreatorbiasLayer(const LayerParameter& param)  
  10. // 下面的语句是为你自己的类定义了LayerRegisterer<float>类型的静态变量g_creator_f_biasLayer(float类型,实际上就是把你自己的类的字符串类型和类的实例绑定到注册表)  
  11. // static LayerRegisterer<float> g_creator_f_biasLayer(bias, CreatorbiasLayer)  
  12. // 下面的语句为你自己的类定义了LayerRegisterer<double>类型的静态变量g_creator_d_biasLayer(double类型,实际上就是把你自己的类的字符串类型和类的实例绑定到注册表)  
  13. // static LayerRegisterer<double> g_creator_d_biasLayer(bias, CreatorbiasLayer)  
  14. #define REGISTER_LAYER_CLASS(type)                                             \  
  15.   template <typename Dtype>                                                    \  
  16.   shared_ptr<Layer<Dtype> > Creator_##type##Layer(const LayerParameter& param) \  
  17.   {                                                                            \  
  18.     return shared_ptr<Layer<Dtype> >(new type##Layer<Dtype>(param));           \  
  19.   }                                                                            \  
  20.   REGISTER_LAYER_CREATOR(type, Creator_##type##Layer)  

四、Layer_factory.cpp中的实现

首先给出卷积层的参数
[cpp]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1. message ConvolutionParameter {  
  2.   optional uint32 num_output = 1; // The number of outputs for the layer  
  3.   optional bool bias_term = 2 [default = true]; // whether to have bias terms  
  4.   
  5.   // Pad, kernel size, and stride are all given as a single value for equal  
  6.   // dimensions in all spatial dimensions, or once per spatial dimension.  
  7.   repeated uint32 pad = 3; // The padding size; defaults to 0  
  8.   repeated uint32 kernel_size = 4; // The kernel size  
  9.   repeated uint32 stride = 6; // The stride; defaults to 1  
  10.   
  11.   // For 2D convolution only, the *_h and *_w versions may also be used to  
  12.   // specify both spatial dimensions.  
  13.   optional uint32 pad_h = 9 [default = 0]; // The padding height (2D only)  
  14.   optional uint32 pad_w = 10 [default = 0]; // The padding width (2D only)  
  15.   optional uint32 kernel_h = 11; // The kernel height (2D only)  
  16.   optional uint32 kernel_w = 12; // The kernel width (2D only)  
  17.   optional uint32 stride_h = 13; // The stride height (2D only)  
  18.   optional uint32 stride_w = 14; // The stride width (2D only)  
  19.   
  20.   optional uint32 group = 5 [default = 1]; // The group size for group conv  
  21.   
  22.   optional FillerParameter weight_filler = 7; // The filler for the weight  
  23.   optional FillerParameter bias_filler = 8; // The filler for the bias  
  24.   enum Engine {  
  25.     DEFAULT = 0;  
  26.     CAFFE = 1;  
  27.     CUDNN = 2;  
  28.   }  
  29.   optional Engine engine = 15 [default = DEFAULT];  
  30.   
  31.   // The axis to interpret as "channels" when performing convolution.  
  32.   // Preceding dimensions are treated as independent inputs;  
  33.   // succeeding dimensions are treated as "spatial".  
  34.   // With (N, C, H, W) inputs, and axis == 1 (the default), we perform  
  35.   // N independent 2D convolutions, sliding C-channel (or (C/g)-channels, for  
  36.   // groups g>1) filters across the spatial axes (H, W) of the input.  
  37.   // With (N, C, D, H, W) inputs, and axis == 1, we perform  
  38.   // N independent 3D convolutions, sliding (C/g)-channels  
  39.   // filters across the spatial axes (D, H, W) of the input.  
  40.   optional int32 axis = 16 [default = 1];  
  41.   
  42.   // Whether to force use of the general ND convolution, even if a specific  
  43.   // implementation for blobs of the appropriate number of spatial dimensions  
  44.   // is available. (Currently, there is only a 2D-specific convolution  
  45.   // implementation; for input blobs with num_axes != 2, this option is  
  46.   // ignored and the ND implementation will be used.)  
  47.   optional bool force_nd_im2col = 17 [default = false];  
  48. }  
注册卷积层、注册池化层、注册ReLU层注册Tanh层,注册python层(如果开始python绑定的话)
代码如下:
[cpp]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1. // Make sure we include Python.h before any system header  
  2. // to avoid _POSIX_C_SOURCE redefinition  
  3. #ifdef WITH_PYTHON_LAYER  
  4. #include <boost/python.hpp>  
  5. #endif  
  6. #include <string>  
  7.   
  8. #include "caffe/layer.hpp"  
  9. #include "caffe/layer_factory.hpp"  
  10. #include "caffe/proto/caffe.pb.h"  
  11. #include "caffe/vision_layers.hpp"  
  12.   
  13. #ifdef WITH_PYTHON_LAYER  
  14. #include "caffe/python_layer.hpp"  
  15. #endif  
  16.   
  17. namespace caffe {  
  18.   
  19. // 写一个获取卷积层实例的函数  
  20. // Get convolution layer according to engine.  
  21. template <typename Dtype>  
  22. shared_ptr<Layer<Dtype> > GetConvolutionLayer(  
  23.     const LayerParameter& param) {  
  24.    // 从参数中获取是使用什么引擎进行计算CUDNN还是CAFFE还是DEFAULT  
  25.    // engine可从caffe.proto中看出是枚举类型的  
  26.   ConvolutionParameter_Engine engine = param.convolution_param().engine();  
  27.   if (engine == ConvolutionParameter_Engine_DEFAULT) {  
  28.     engine = ConvolutionParameter_Engine_CAFFE;  
  29. #ifdef USE_CUDNN  
  30.     engine = ConvolutionParameter_Engine_CUDNN;  
  31. #endif  
  32.   }  
  33.   if (engine == ConvolutionParameter_Engine_CAFFE) {  
  34.     //  直接初始化Caffe的卷积层  
  35.     return shared_ptr<Layer<Dtype> >(new ConvolutionLayer<Dtype>(param));  
  36. #ifdef USE_CUDNN  
  37.   } else if (engine == ConvolutionParameter_Engine_CUDNN) {  
  38.     // 初始化CUDNN的卷积层  
  39.     return shared_ptr<Layer<Dtype> >(new CuDNNConvolutionLayer<Dtype>(param));  
  40. #endif  
  41.   } else { // 否则就是出错了  
  42.     LOG(FATAL) << "Layer " << param.name() << " has unknown engine.";  
  43.   }  
  44. }  
  45. // 注册该卷积层,类型名为Convolution,获取卷积层的实例为GetConvolutionLayer函数  
  46. REGISTER_LAYER_CREATOR(Convolution, GetConvolutionLayer);  
  47.   
  48. // 获取池化层的实例,同卷积层的逻辑  
  49. // Get pooling layer according to engine.  
  50. template <typename Dtype>  
  51. shared_ptr<Layer<Dtype> > GetPoolingLayer(const LayerParameter& param) {  
  52.   PoolingParameter_Engine engine = param.pooling_param().engine();  
  53.   if (engine == PoolingParameter_Engine_DEFAULT) {  
  54.     engine = PoolingParameter_Engine_CAFFE;  
  55. #ifdef USE_CUDNN  
  56.     engine = PoolingParameter_Engine_CUDNN;  
  57. #endif  
  58.   }  
  59.   if (engine == PoolingParameter_Engine_CAFFE) {  
  60.     return shared_ptr<Layer<Dtype> >(new PoolingLayer<Dtype>(param));  
  61. #ifdef USE_CUDNN  
  62.   } else if (engine == PoolingParameter_Engine_CUDNN) {  
  63.     PoolingParameter p_param = param.pooling_param();  
  64.     if (p_param.pad() || p_param.pad_h() || p_param.pad_w() ||  
  65.         param.top_size() > 1) {  
  66.       LOG(INFO) << "CUDNN does not support padding or multiple tops. "  
  67.                 << "Using Caffe's own pooling layer.";  
  68.       return shared_ptr<Layer<Dtype> >(new PoolingLayer<Dtype>(param));  
  69.     }  
  70.     return shared_ptr<Layer<Dtype> >(new CuDNNPoolingLayer<Dtype>(param));  
  71. #endif  
  72.   } else {  
  73.     LOG(FATAL) << "Layer " << param.name() << " has unknown engine.";  
  74.   }  
  75. }  
  76.   
  77. // 注册池化层  
  78. REGISTER_LAYER_CREATOR(Pooling, GetPoolingLayer);  
  79.   
  80. // 注册ReLU层  
  81. // Get relu layer according to engine.  
  82. template <typename Dtype>  
  83. shared_ptr<Layer<Dtype> > GetReLULayer(const LayerParameter& param) {  
  84.   ReLUParameter_Engine engine = param.relu_param().engine();  
  85.   if (engine == ReLUParameter_Engine_DEFAULT) {  
  86.     engine = ReLUParameter_Engine_CAFFE;  
  87. #ifdef USE_CUDNN  
  88.     engine = ReLUParameter_Engine_CUDNN;  
  89. #endif  
  90.   }  
  91.   if (engine == ReLUParameter_Engine_CAFFE) {  
  92.     return shared_ptr<Layer<Dtype> >(new ReLULayer<Dtype>(param));  
  93. #ifdef USE_CUDNN  
  94.   } else if (engine == ReLUParameter_Engine_CUDNN) {  
  95.     return shared_ptr<Layer<Dtype> >(new CuDNNReLULayer<Dtype>(param));  
  96. #endif  
  97.   } else {  
  98.     LOG(FATAL) << "Layer " << param.name() << " has unknown engine.";  
  99.   }  
  100. }  
  101.   
  102. REGISTER_LAYER_CREATOR(ReLU, GetReLULayer);  
  103.   
  104. // 注册sigmoid层  
  105. // Get sigmoid layer according to engine.  
  106. template <typename Dtype>  
  107. shared_ptr<Layer<Dtype> > GetSigmoidLayer(const LayerParameter& param) {  
  108.   SigmoidParameter_Engine engine = param.sigmoid_param().engine();  
  109.   if (engine == SigmoidParameter_Engine_DEFAULT) {  
  110.     engine = SigmoidParameter_Engine_CAFFE;  
  111. #ifdef USE_CUDNN  
  112.     engine = SigmoidParameter_Engine_CUDNN;  
  113. #endif  
  114.   }  
  115.   if (engine == SigmoidParameter_Engine_CAFFE) {  
  116.     return shared_ptr<Layer<Dtype> >(new SigmoidLayer<Dtype>(param));  
  117. #ifdef USE_CUDNN  
  118.   } else if (engine == SigmoidParameter_Engine_CUDNN) {  
  119.     return shared_ptr<Layer<Dtype> >(new CuDNNSigmoidLayer<Dtype>(param));  
  120. #endif  
  121.   } else {  
  122.     LOG(FATAL) << "Layer " << param.name() << " has unknown engine.";  
  123.   }  
  124. }  
  125.   
  126. REGISTER_LAYER_CREATOR(Sigmoid, GetSigmoidLayer);  
  127.   
  128. // 注册softmax层  
  129. // Get softmax layer according to engine.  
  130. template <typename Dtype>  
  131. shared_ptr<Layer<Dtype> > GetSoftmaxLayer(const LayerParameter& param) {  
  132.   SoftmaxParameter_Engine engine = param.softmax_param().engine();  
  133.   if (engine == SoftmaxParameter_Engine_DEFAULT) {  
  134.     engine = SoftmaxParameter_Engine_CAFFE;  
  135. #ifdef USE_CUDNN  
  136.     engine = SoftmaxParameter_Engine_CUDNN;  
  137. #endif  
  138.   }  
  139.   if (engine == SoftmaxParameter_Engine_CAFFE) {  
  140.     return shared_ptr<Layer<Dtype> >(new SoftmaxLayer<Dtype>(param));  
  141. #ifdef USE_CUDNN  
  142.   } else if (engine == SoftmaxParameter_Engine_CUDNN) {  
  143.     return shared_ptr<Layer<Dtype> >(new CuDNNSoftmaxLayer<Dtype>(param));  
  144. #endif  
  145.   } else {  
  146.     LOG(FATAL) << "Layer " << param.name() << " has unknown engine.";  
  147.   }  
  148. }  
  149.   
  150. REGISTER_LAYER_CREATOR(Softmax, GetSoftmaxLayer);  
  151.   
  152. // 注册tanh层  
  153. // Get tanh layer according to engine.  
  154. template <typename Dtype>  
  155. shared_ptr<Layer<Dtype> > GetTanHLayer(const LayerParameter& param) {  
  156.   TanHParameter_Engine engine = param.tanh_param().engine();  
  157.   if (engine == TanHParameter_Engine_DEFAULT) {  
  158.     engine = TanHParameter_Engine_CAFFE;  
  159. #ifdef USE_CUDNN  
  160.     engine = TanHParameter_Engine_CUDNN;  
  161. #endif  
  162.   }  
  163.   if (engine == TanHParameter_Engine_CAFFE) {  
  164.     return shared_ptr<Layer<Dtype> >(new TanHLayer<Dtype>(param));  
  165. #ifdef USE_CUDNN  
  166.   } else if (engine == TanHParameter_Engine_CUDNN) {  
  167.     return shared_ptr<Layer<Dtype> >(new CuDNNTanHLayer<Dtype>(param));  
  168. #endif  
  169.   } else {  
  170.     LOG(FATAL) << "Layer " << param.name() << " has unknown engine.";  
  171.   }  
  172. }  
  173.   
  174. REGISTER_LAYER_CREATOR(TanH, GetTanHLayer);  
  175.   
  176. // 注册PYTHON层  
  177. #ifdef WITH_PYTHON_LAYER  
  178. template <typename Dtype>  
  179. shared_ptr<Layer<Dtype> > GetPythonLayer(const LayerParameter& param) {  
  180.   Py_Initialize();  
  181.   try {  
  182.     bp::object module = bp::import(param.python_param().module().c_str());  
  183.     bp::object layer = module.attr(param.python_param().layer().c_str())(param);  
  184.     return bp::extract<shared_ptr<PythonLayer<Dtype> > >(layer)();  
  185.   } catch (bp::error_already_set) {  
  186.     PyErr_Print();  
  187.     throw;  
  188.   }  
  189. }  
  190.   
  191. REGISTER_LAYER_CREATOR(Python, GetPythonLayer);  
  192. #endif  
  193.   
  194. // Layers that use their constructor as their default creator should be  
  195. // registered in their corresponding cpp files. Do not register them here.  
  196. }  // namespace caffe  

五、总结

作者还真是煞费苦心,弄了个宏,一下子就注册了类。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/boon_228/article/details/54909561

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法