C基礎 萬能動態數組

来源:http://www.cnblogs.com/life2refuel/archive/2016/08/14/5768987.html
-Advertisement-
Play Games

開發中動態類型無外乎list 或者 vector, 這裡就是在C中實現vecotr結構容器部分. ...


引言 - 動態數組切入

  開發中動態類型無外乎list 或者 vector, 這裡就是在C中實現vector結構容器部分.

對於C中使用的數據結構, 可以參照下麵感覺很不錯框架源碼學習 , 感覺是<<C介面與實現>>的標準Demo

    twemproxy  https://github.com/twitter/twemproxy/tree/master/src 

寫的很清楚易懂, 給人一種鋪面而來的美感.

關於動態數組設計的總思路, 主要體現在下麵的數組結構 struct array {}; 中

struct array {
    void *        as;        /* 存儲數組具體內容首地址 */
    unsigned    len;    /* 當前數組的長度 */
    unsigned    size;   /* 當前數組容量大小 */
    size_t        alloc;    /* 每個元素位元組大小 */
};

 as 保存動態數組的首地址, len保存當前動態數組中元素個數, size表示動態數組容量(記憶體總大小), alloc記錄每次分配多大記憶體.

是不是很簡單, 數據結構一但確定什麼都OK了. 在具體分析之前先擴展講解一下 C11的內聯函數. 內聯函數是應用場景很多, 巨集一般的性能, 並且還可以當函數調試!

// 內聯函數聲明部分, 不需要加inline
extern void heoo(void);

// 內聯函數定義部分
inline void
heoo(void) {
  ... 
}

上面就是內聯函數的套路, 在定義的時候加上內聯屬性inline. 這種做法是為了VS2015 和 GCC5.3 對於inline 語法解析的統一. 

具體看下麵搜索到的老外給的資料 

    內聯解析warning資料 http://www.avrfreaks.net/forum/declaring-function-extern-inline-header-file

Computer science 還是老外厲害些, 國內最屌, 也只是國外一線的水準. 目前猜測的原因是, 祖國就一個, 國外太多了. O(∩_∩)O哈哈~

 

前言  - 介面分析

  具體設計了下麵幾種介面, 基本夠用了.也很好用.

/*
 * 返回創建數組對象
 * size        : 創建數組的總大小個數
 * alloc    : 數組中每個元素的位元組數
 *            : 返回創建的數組對象
 */
extern array_t array_new(unsigned size, size_t alloc);

/*
 * 銷毀這個創建的數組對象
 * a        : 創建的數組對象
 */
extern void array_delete(array_t a);

/*
 * 重新構建一個數組對象
 * a        : 可變數組對象
 * size        : 新可變數組總長度
 */
extern void array_newinit(array_t a, unsigned size);

/*
 * 得到節點elem在數組中索引
 * a        : 可變數組對象
 * elem        : 查詢元素
 *            : 返回查詢到位置
 */
extern unsigned array_idx(array_t a, void * elem);

/*
 * 為可變數組插入一個元素, 並返回這個元素的首地址
 * a        : 可變數組對象
 *            : 返回創建對象位置
 */
extern void * array_push(array_t a);

/*
 * 彈出一個數組元素
 * a        : 可變數組對象
 *            : 返回彈出數組元素節點
 */
extern void * array_pop(array_t a);

/*
 * 按照索引得到數組元素
 * a        : 可變數組對象
 * idx        : 索引位置
 *            : 返回查詢到數據
 */
extern void * array_get(array_t a, unsigned idx);

/*
 * 得到數組頂的元素
 * a        : 可變數組對象
 *            : 返回得到元素
 */
extern void * array_top(array_t a);

/*
 * 兩個數組進行交換
 * a        : 數組a
 * b        : 數組b
 */
extern void array_swap(array_t a, array_t b);

/*
 * 數組進行排序
 * a        : 數組對象
 * compare    : 比對規則
 */
extern void array_sort(array_t a, icmp_f compare);

/*
 * 數組進行遍歷
 * a        : 可變數組對象
 * func        : 執行每個結點函數, typedef flag_e    (* each_f)(void * node, void * arg);
 * arg        : 附加參數
 *            : 返回操作結果狀態碼
 */
flag_e array_each(array_t a, each_f func, void * arg);

 圍繞創建, 銷毀, 添加元素, 刪除元素, 交換, 遍歷, 排序等操作. 具體參看 array.h 文件 

#ifndef _H_SIMPLEC_ARRAY
#define _H_SIMPLEC_ARRAY

#include <stdlib.h>

#define sm_free free

typedef enum {
    RT_SuccessBase    = 00,                //結果正確的返回巨集
    RT_ErrorBase    = -1,                //錯誤基類型, 所有錯誤都可用它, 在不清楚的情況下
    RT_ErrorParam    = -2,                //調用的參數錯誤
    RT_ErrorMalloc    = -3,                //記憶體分配錯誤
    RT_ErrorFopen    = -4,                //文件打開失敗    
    RT_ErrorClose    = -5,                //文件描述符讀取關閉, 讀取完畢也會返回這個
} flag_e;

// icmp_f 最好 是 int cmp(const void * ln, const void * rn); 標準結構
typedef int        (* icmp_f)();
// 迴圈操作函數, arg 外部參數, node 內部節點
typedef flag_e    (* each_f)(void * node, void * arg);

struct array {
    void *        as;        /* 存儲數組具體內容首地址 */
    unsigned    len;    /* 當前數組的長度 */
    unsigned    size;   /* 當前數組容量大小 */
    size_t        alloc;    /* 每個元素位元組大小 */
};

// 定義可變數組類型 對象
typedef struct array * array_t;

/*
 * 在棧上創建對象 ##var
 * var        : 創建對象名稱
 * size        : 創建對象總長度
 * alloc    : 每個元素分配空間大小
 */
#define ARRAY_NEW(var, size, alloc) \
    struct array $__##var = { NULL, 0, 0, alloc }, * var = &$__##var;\
    array_newinit(var, size)
#define ARRAY_DELETE(var) \
    sm_free(var->as)

/*
 * 返回創建數組對象
 * size        : 創建數組的總大小個數
 * alloc    : 數組中每個元素的位元組數
 *            : 返回創建的數組對象
 */
extern array_t array_new(unsigned size, size_t alloc);

/*
 * 銷毀這個創建的數組對象
 * a        : 創建的數組對象
 */
extern void array_delete(array_t a);

/*
 * 重新構建一個數組對象
 * a        : 可變數組對象
 * size        : 新可變數組總長度
 */
extern void array_newinit(array_t a, unsigned size);

/*
 * 得到節點elem在數組中索引
 * a        : 可變數組對象
 * elem        : 查詢元素
 *            : 返回查詢到位置
 */
extern unsigned array_idx(array_t a, void * elem);

/*
 * 為可變數組插入一個元素, 並返回這個元素的首地址
 * a        : 可變數組對象
 *            : 返回創建對象位置
 */
extern void * array_push(array_t a);

/*
 * 彈出一個數組元素
 * a        : 可變數組對象
 *            : 返回彈出數組元素節點
 */
extern void * array_pop(array_t a);

/*
 * 按照索引得到數組元素
 * a        : 可變數組對象
 * idx        : 索引位置
 *            : 返回查詢到數據
 */
extern void * array_get(array_t a, unsigned idx);

/*
 * 得到數組頂的元素
 * a        : 可變數組對象
 *            : 返回得到元素
 */
extern void * array_top(array_t a);

/*
 * 兩個數組進行交換
 * a        : 數組a
 * b        : 數組b
 */
extern void array_swap(array_t a, array_t b);

/*
 * 數組進行排序
 * a        : 數組對象
 * compare    : 比對規則
 */
extern void array_sort(array_t a, icmp_f compare);

/*
 * 數組進行遍歷
 * a        : 可變數組對象
 * func        : 執行每個結點函數, typedef flag_e    (* each_f)(void * node, void * arg);
 * arg        : 附加參數
 *            : 返回操作結果狀態碼
 */
flag_e array_each(array_t a, each_f func, void * arg);

#endif // !_H_SIMPLEC_ARRAY
View Code

 

下麵添加棧上聲明部分, 採用巨集設計

// 定義可變數組類型 對象
typedef struct array * array_t;

/*
 * 在棧上創建對象 ##var
 * var        : 創建對象名稱
 * size        : 創建對象總長度
 * alloc    : 每個元素分配空間大小
 */
#define ARRAY_NEW(var, size, alloc) \
    struct array $__##var = { NULL, 0, 0, alloc }, * var = &$__##var;\
    array_newinit(var, size)
#define ARRAY_DELETE(var) \
    sm_free(var->as)

是不是很有意思, 這些都是具體開發中的解決方案.

此事我們的設計思路已經轉成介面設計代碼. 現在先寫一些前測試代碼 test_array.c

下麵主要測試棧上動態數組分配和使用

#define LEN(arr) \
    (sizeof(arr)/sizeof(*(arr)))

//簡單結構
struct dict {
    char * key;
    char * value;
};

// 單獨遍歷函數
static flag_e _dict_echo(struct dict * node, void * arg)
{
    printf("[%s]    => [%s]\n", node->key, node->value);
    return RT_SuccessBase;
}

// 比較函數
static int _dict_cmp(struct dict * ln, struct dict * rn) {
    return strcmp(ln->key, rn->key);
}

/*
 * 主要測試 array 動態數組模塊代碼
 */
void test_array(void) {
    struct dict * elem;

    struct dict dicts[] = {
        { "zero"    , "" },
        { "one"        , "" },
        { "two"        , "" },
        { "three"    , "" },
        { "four"    , "" },
        { "five"    , "" },
        { "six"        , "" },
        { "seven"    , "" },
        { "eight"    , "" },
        { "night"    , "" },
        { "night"    , "" },
    };

    /* Step 1 : 測試堆上array對象 */
    int i = -1, len = LEN(dicts);
    array_t a = array_new(len, sizeof(struct dict));

    // 插入數據
    while (++i < len) {
        elem = array_push(a);
        *elem = dicts[i];
    }

    // 列印數據測試
    puts("----------- start data look at the following:");
    array_each(a, (each_f)_dict_echo, NULL);

    // 排序一下
    array_sort(a, _dict_cmp);

    // 列印數據測試
    puts("----------- sort data look at the following:");
    array_each(a, (each_f)_dict_echo, NULL);

    array_delete(a);

    exit(EXIT_SUCCESS);
}

為什麼採用exit 結束操作, 先賣一個關子.  上面是圍繞創建和遍歷最後排序的測試. 從上面感受這些動態數組的api使用方式.

軟體設計套路很多但是對於C. 請參照下麵套路.

  業務理解  -> 數據結構 -> 介面定義 -> | 介面實現

                   ->  |      -> 業務實現 -> 業務測試 -> 提交功能

                    -> | 介面測試

標黑的特別重要. 當然了, 對於高級語言, 數據結構可以省略了, 主要是業務理解 -> 介面實現. 這也是軟體設計界一種解放吧. 正文部分會具體分析實現部分.

 

正文  - 介面實現

  好的介面定義, 是一切好的開始. 介面實現還是很容易的.  例如下麵關於動態數組的 array.c 實現

#include "array.h"
#include <stdio.h>
#include <assert.h>
#include <string.h>

// 導入需要的輔助函數
static void * sm_malloc(size_t sz) {
    void * ptr = malloc(sz);
    if(NULL == ptr) {
        fprintf(stderr, "sm_malloc malloc sz = %ld is error!\n", sz);
        exit(EXIT_FAILURE);
    }
    memset(ptr, 0, sz);
    return ptr;
}

static void * sm_realloc(void * ptr, size_t sz) {
    void * nptr = realloc(ptr, sz);
    if(NULL == nptr) {
        free(ptr);
        fprintf(stderr, "sm_realloc realloc ptr, sz = %p, %ld is error!\n", ptr, sz);
        exit(EXIT_FAILURE);
    }
    if(NULL != ptr)
        memset(nptr, 0, sz);
    return nptr;
}

/*
 * 返回創建數組對象
 * size        : 創建數組的總大小個數
 * alloc    : 數組中每個元素的位元組數
 *            : 返回創建的數組對象
 */
array_t 
array_new(unsigned size, size_t alloc) {
    struct array * a = sm_malloc(sizeof(struct array));
    if (size * alloc > 0)
        a->as = sm_malloc(size * alloc);

    a->size = size;
    a->alloc = alloc;

    return a;
}

/*
 * 銷毀這個創建的數組對象
 * a        : 創建的數組對象
 */
inline 
void array_delete(array_t a) {
    sm_free(a->as);
    sm_free(a);
}

/*
 * 重新構建一個數組對象
 * a        : 可變數組對象
 * size        : 新可變數組總長度
 */
inline void
array_newinit(array_t a, unsigned size) {
    assert(NULL != a);
    a->as = sm_realloc(a->as, size * a->alloc);
    if (a->len > size)
        a->len = size;
    a->size = size;
}

/*
 * 得到節點elem在數組中索引
 * a        : 可變數組對象
 * elem        : 查詢元素
 *            : 返回查詢到位置
 */
inline unsigned 
array_idx(array_t a, void * elem) {
    unsigned char * p, * q;
    unsigned off;

    assert(NULL != a && elem >= a->as);

    p = a->as;
    q = elem;
    off = (unsigned)(q - p);

    assert(off % (unsigned)a->alloc == 0);

    return off / (unsigned)a->alloc;
}

/*
 * 為可變數組插入一個元素, 並返回這個元素的首地址
 * a        : 可變數組對象
 *            : 返回創建對象位置
 */
void * 
array_push(array_t a) {
    assert(NULL != a);

    if (a->len == a->size) {
        /* the array is full; allocate new array */
        a->size <<= 1;
        a->as = sm_realloc(a->as, a->size * a->alloc);
    }

    return (unsigned char *)a->as + a->alloc * a->len++;
}

/*
 * 彈出一個數組元素
 * a        : 可變數組對象
 *            : 返回彈出數組元素節點
 */
inline void * 
array_pop(array_t a) {
    assert(NULL != a && 0 != a->len);
    --a->len;
    return (unsigned char *)a->as + a->alloc * a->len;
}

/*
 * 按照索引得到數組元素
 * a        : 可變數組對象
 * idx        : 索引位置
 *            : 返回查詢到數據
 */
inline void * 
array_get(array_t a, unsigned idx) {
    assert(NULL != a && idx < a->len);
    return (unsigned char *)a->as + a->alloc * idx;
}

/*
* 得到數組頂的元素
* a        : 可變數組對象
*            : 返回得到元素
*/
inline void * 
array_top(array_t a) {
    assert(NULL != a && 0 != a->len);
    return (unsigned char *)a->as + a->alloc * (a->len - 1);
}

/*
 * 兩個數組進行交換
 * a        : 數組a
 * b        : 數組b
 */
inline 
void array_swap(array_t a, array_t b) {
    struct array t = *a;
    *a = *b;
    *b = t;
}

/*
* 數組進行排序
* a        : 數組對象
* compare    : 比對規則
*/
inline void 
array_sort(array_t a, icmp_f compare) {
    assert(NULL != a && 0 != a->len && NULL != compare);
    qsort(a->as, a->len, a->alloc, (int ( *)(const void *, const void *))compare);
}

/*
* 數組進行遍歷
* a        : 可變數組對象
* func        : 執行每個結點函數, typedef flag_e    (* each_f)(void * node, void * arg);
* arg        : 附加參數
*            : 返回操作結果狀態碼
*/
flag_e 
array_each(array_t a, each_f func, void * arg) {
    flag_e rt;
    unsigned char * s, * e;

    assert(NULL != a && NULL != func);

    s = a->as;
    e = s + a->alloc * a->len;
    while (s < e) {
        rt = func(s, arg);
        if (RT_SuccessBase != rt)
            return rt;
        s += a->alloc;
    }

    return RT_SuccessBase;
}
View Code

 

觀看array.c 中具體部分 下麵三個函數是控制整個動態數組生命周期的

/*
 * 返回創建數組對象
 * size        : 創建數組的總大小個數
 * alloc    : 數組中每個元素的位元組數
 *            : 返回創建的數組對象
 */
array_t 
array_new(unsigned size, size_t alloc) {
    struct array * a = sm_malloc(sizeof(struct array));
    if (size * alloc > 0)
        a->as = sm_malloc(size * alloc);

    a->size = size;
    a->alloc = alloc;

    return a;
}

/*
 * 銷毀這個創建的數組對象
 * a        : 創建的數組對象
 */
inline 
void array_delete(array_t a) {
    sm_free(a->as);
    sm_free(a);
}

/*
 * 重新構建一個數組對象
 * a        : 可變數組對象
 * size        : 新可變數組總長度
 */
inline void
array_newinit(array_t a, unsigned size) {
    assert(NULL != a);
    a->as = sm_realloc(a->as, size * a->alloc);
    if (a->len > size)
        a->len = size;
    a->size = size;
}

new 創建, delete銷毀, newinit重新創建.  其中還有一個 api, 向動態數組加入元素也特別有意思

/*
 * 為可變數組插入一個元素, 並返回這個元素的首地址
 * a        : 可變數組對象
 *            : 返回創建對象位置
 */
void * 
array_push(array_t a) {
    assert(NULL != a);

    if (a->len == a->size) {
        /* the array is full; allocate new array */
        a->size <<= 1;
        a->as = sm_realloc(a->as, a->size * a->alloc);
    }

    return (unsigned char *)a->as + a->alloc * a->len++;
}

返回插入元素的首地址, 需要自己初始化. 特別新潮的做法. (當然了對於C這種老古董,  這些也都是老掉牙的東西, 只是自娛自樂, 開心就好)

實現部分到這裡基本完畢了, 最好能看一遍其中具體文件, 設計思路非常好, 實現也是力求最快最簡.

對於 exit這個坑 , 主要 看 linux上代碼, 例如 man select 會出現

exit(EXIT_SUCCESS) ; 這個做法是為了, 解決在函數調用結束, 釋放啟動這個 main函數之前聲明的資源.

當然系統都會對 mian 函數特殊處理, 哪怕return 也能釋放乾凈. 但是假如一個文件沒有main函數, 啟動了 新的入口函數,

這是否return 是不會幫我們額外清理一些資源的. 這時候運行結束會崩潰. exit 就是為瞭解決退出時候資源釋放問題.

到這裡我們給出測試部分了. 先看 Makefile 文件

CC = gcc
DEBUG = -ggdb3 -Wall
RUN = $(CC) $(DEBUG) -o $@ $^
RUNO = $(CC) -c -o $@ $<
RUNMAIN = $(RUN) -nostartfiles -e $(*F)

all:test_array.out test_array_stack.out

%.o:%c
    $(RUNO)
    
test_array.out:test_array.o array.o
    $(RUNMAIN)

test_array_stack.out:test_array.o array.o
    $(RUNMAIN)

# 清除命令
.PHONY:clean
clean:
    rm -rf *.i *.s *.o *.out *~ ; ls

具體的測試文件 test_array.c

#include "array.h"
#include <stdio.h>
#include <string.h>

#define LEN(arr) \
    (sizeof(arr)/sizeof(*(arr)))

//簡單結構
struct dict {
    char * key;
    char * value;
};

// 單獨遍歷函數
static flag_e _dict_echo(struct dict * node, void * arg)
{
    printf("[%s]    => [%s]\n", node->key, node->value);
    return RT_SuccessBase;
}

// 比較函數
static int _dict_cmp(struct dict * ln, struct dict * rn) {
    return strcmp(ln->key, rn->key);
}

/*
 * 主要測試 array 動態數組模塊代碼
 */
void test_array(void) {
    struct dict * elem;

    struct dict dicts[] = {
        { "zero"    , "" },
        { "one"        , "" },
        { "two"        , "" },
        { "three"    , "" },
        { "four"    , "" },
        { "five"    , "" },
        { "six"        , "" },
        { "seven"    , "" },
        { "eight"    , "" },
        { "night"    , "" },
        { "night"    , "" },
    };

    /* Step 1 : 測試堆上array對象 */
    int i = -1, len = LEN(dicts);
    array_t a = array_new(len, sizeof(struct dict));

    // 插入數據
    while (++i < len) {
        elem = array_push(a);
        *elem = dicts[i];
    }

    // 列印數據測試
    puts("----------- start data look at the following:");
    array_each(a, (each_f)_dict_echo, NULL);

    // 排序一下
    array_sort(a, _dict_cmp);

    // 列印數據測試
    puts("----------- sort data look at the following:");
    array_each(a, (each_f)_dict_echo, NULL);

    array_delete(a);

    exit(EXIT_SUCCESS);
}

void test_array_stack(void) {
    struct dict * elem;

    struct dict dicts[] = {
        { "zero"    , "" },
        { "one"        , "" },
        { "two"        , "" },
        { "three"    , "" },
        { "four"    , "" },
        { "five"    , "" },
        { "six"        , "" },
        { "seven"    , "" },
        { "eight"    , "" },
        { "night"    , "" },
        { "night"    , "" },
    };

    /* Step 1 : 測試堆上array對象 */
    int i = -1, len = LEN(dicts);
    ARRAY_NEW(a, len, sizeof(struct dict));

    // 插入數據
    while (++i < len) {
        elem = array_push(a);
        *elem = dicts[i];
    }

    // 列印數據測試
    puts("----------- start data look at the following:");
    array_each(a, (each_f)_dict_echo, NULL);

    // 排序一下
    array_sort(a, _dict_cmp);

    // 列印數據測試
    puts("----------- sort data look at the following:");
    for(i=0; i<len; ++i) {
        elem = array_get(a, i);
        _dict_echo(elem, NULL);
    }

    ARRAY_DELETE(a);
    
    exit(EXIT_SUCCESS);
}
View Code

最終效果 , 先看編譯結果圖

在展示部分棧上測試結果圖

一切正常. 以上就是具體設計思路. 在具體工程中會一些細節不同, 例如對於記憶體申請和銷毀走統一介面. 但是總的關於array設計是一樣的.

有興趣可以將上面4個文件看看, 自己coding test . 很實用!

 

後記  - 每天都是重新開始

  錯誤是難免, 歡迎更正. 哼哈 O(∩_∩)O~~

耶利亞女郎   http://music.163.com/#/song?id=119018

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 昨天有朋友問到我關於Linux的東西,今天就先把Linux的命令彙總一下。 文件操作命令: cd: 切換目錄 ls:顯示文件或者文件名稱 ls-a:顯示隱藏文件 ls-l、ll+文件夾:能展示出文件夾中的內容 ls -al:顯示隱藏文件 ls -alt:按時間排序 cp:拷貝命令 cp a.txt ...
  • 設置用戶ID(set-user-ID),設置組ID(set-group-ID),sticky set-user-ID: SUID 當文件的該位有設置時,表示當該文件被執行時,程式具有文件所有者的許可權而不是執行者的許可權。 這樣說有點繞,舉個例子就是說passwd這個命令,它的所有者是root,並且它的 ...
  • shell Unix系統中常見的shell有:sh、csh、ksh、tcsh、bash 使用命令: vim /etc/shells 可以查看當前系統支持的bash shell執行腳本 shell執行腳本是一門解釋性語言、批量化處理語言,大大的節省了工作成本 shell腳本第一行必須以 #!開頭,它表 ...
  • glob 模式(globbing)也被稱之為 shell 通配符,名字的起源來自於 Unix V6 中的 /etc/glob (詳見 man 文檔)。glob 是一種特殊的模式匹配,最常見的是通配符拓展,也可以將 glob 模式設為精簡了的正則表達式,在最新的 CentOS 7 中已經刪除了 glo... ...
  • 當UNIX函數發生錯誤時,通常會返回一個負值,而且整形變數errno通常被設置為具有特定信息的值。 errno是全局變數,僅當函數出錯才有被改變。對待errno,應註意兩條規則 1:如果沒有出錯,其值不會被常式清除,也就是說,當且僅當函數出錯時,馬上檢查errno的值才有意義。 2:任何函數都不會將 ...
  • 在Linux系統中/tmp文件夾下的文件是會被清理、刪除的,文件清理的規則是如何設定的呢? 以Redhat為例,這個主要是因為作業裡面會調用tmpwatch命令刪除那些一段時間沒有訪問的文件。 那麼什麼是tmpwatch呢?其實tmpwatch是一個命令或者說是一個包。如下所示 tmpwatch -... ...
  • 1.安裝samba伺服器 2.配置 3.新建用戶名,新建文件夾並且設定ACL許可權 ACL: 1.查看是否具有ACL許可權 2.如果1什麼也沒輸出 那麼就需要添加許可權 在/ 那一欄中 磁碟格式後 配置前添加上acl即可 完成之後需要重啟(請設置好,不要不能開機哦!!!) 或者 3.配置acl 4.查看a ...
  • 一,項目背景 記得大學畢業課題,我就是選擇做個CMS,不過當時雖然做了個,不過感覺不是很好,所以現在又重做了,順便發上來供大家討論的。雖然CMS不是什麼特別的項目,但是還是想從一個普通項目學到更多的東西。 二,核心技術 使用asp.net mvc5 模板引擎使用razor。 還有簡單數據操作工具Wa ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...