前端學習C語言 - 初級指針

来源:https://www.cnblogs.com/pengjiali/archive/2023/06/25/17502968.html
-Advertisement-
Play Games

## 初級指針 本篇主要介紹:`指針和變數的關係`、指針類型、`指針的運算符`、空指針和野指針、`指針和數組`、`指針和字元串`、const 和指針、以及`gdb 調試段錯誤`。 ### 基礎概念 > 指針是一種特殊的變數。存放地址的變數就是指針。 `int num = 1;` 會申請4個位元組的記憶體 ...


初級指針

本篇主要介紹:指針和變數的關係、指針類型、指針的運算符、空指針和野指針、指針和數組指針和字元串、const 和指針、以及gdb 調試段錯誤

基礎概念

指針是一種特殊的變數。存放地址的變數就是指針。

int num = 1; 會申請4個位元組的記憶體來存放數字1,每次訪問 num 就是訪問這4個位元組。

訪問記憶體中的這4個位元組,不僅可以通過名稱(例如 num),還可以通過地址

Tip& 不僅是位運算符,還是取地址操作符。例如 int* ptr = #,就是取變數 num 的地址並將其保存到指針變數 ptr 中

請看示例:

#include <stdio.h>

int main() {
    int num = 10;

    // num 的地址:0x7fff4dbf01d8
    printf("num 的地址:%p\n", &num);
    // num 的地址加1 :0x7fff4dbf01dc。
    printf("num 的地址加1 :%p\n", &num + 1);

    // j 存放連續記憶體的第一個位元組地址
    int *j = &num;

    // 10。通過地址訪問
    printf("%d", *j);

    return 0;
}

&num&num + 1 相差4個位元組,說明 &num 表示整數。

普通變數存放值,而指針用於存放地址。

通過 int *j = &num 將變數num的首地址給到指針 j(j的類型是 int *),最後通過地址(*j) 訪問整數1。

int *j 是一個int類型的指針,還有 char、float等指針類型。指針類型必須匹配,比如將 j 的指針類型換成 char,則會警告。就像這樣:

- int *j = &num;
+ char *j = &num;

運行:

/workspace/CProject-test/main.c:12:11: warning: incompatible pointer types initializing 'char *' with an expression of type 'int *' [-Wincompatible-pointer-types]
    char *j = &num;
          ^   ~~~~
1 warning generated.
num 的地址:0x7ffddcfe5328
num 的地址加1 :0x7ffddcfe532c
10

Tip: 指針 j 也有地址,也就是指針的指針。現在不研究

練習

題目:請問輸出什麼?

#include <stdio.h>

int main() {
    int num = 10;
    int *p = &num;
    printf("用指針訪問數據 num :%d\n", *p);

    *p = 11;
    printf("用過指針修改 num 數據:%d\n", num);

    return 0;
}

提示:數據可以通過變數訪問,也能使用地址(指針)訪問。就像通知同學去嵌入式實驗室上課,或者是 303 上課。其中*p = 11; 等價於 num = 11;

輸出:

用指針訪問數據 num :10
用過指針修改 num 數據:11

星號的作用

指針 * 有兩個主要作用(根據* 前面有無類型做區分):

  • 指針類型聲明
  • 取值(又稱解引用操作符)。例如,*ptr 表示獲取指針變數 ptr 所指向記憶體地址上的值。

請看示例:

#include <stdio.h>

int main() {
    int num = 10;
    // 指針類型聲明
    int *p = &num;

    // 取值
    printf("%d\n", *p); // 10

    // 取值
    *p = 11;
    printf("%d\n", num); // 11

    return 0;
}

指針類型

所占位元組

在32位系統上,指針通常占用4個位元組;而在64位系統上,指針通常占用8個位元組。請看示例:

#include <stdio.h>

int main() {
    printf("char類型指針所占位元組數為:%zu\n", sizeof(char*));
    printf("short類型指針所占位元組數為:%zu\n", sizeof(short*));
    printf("int類型指針所占位元組數為:%zu\n", sizeof(int*));
    printf("long類型指針所占位元組數為:%zu\n", sizeof(long*));
    printf("float類型指針所占位元組數為:%zu\n", sizeof(float*));
    printf("double類型指針所占位元組數為:%zu\n", sizeof(double*));
    printf("long long類型指針所占位元組數為:%zu\n", sizeof(long long*));
    return 0;
}

輸出:

char類型指針所占位元組數為:8
short類型指針所占位元組數為:8
int類型指針所占位元組數為:8
long類型指針所占位元組數為:8
float類型指針所占位元組數為:8
double類型指針所占位元組數為:8
long long類型指針所占位元組數為:8

練習

題目:請問整數類型的指針和字元類型的指針加1分別是幾個位元組?

#include <stdio.h>

int main() {
    int num = 10;

    printf("num 的地址:%p\n", &num);
    printf("num 的地址加1 :%p\n", &num + 1);

    char ch = 'a';

    printf("ch 的地址:%p\n", &ch);
    printf("ch 的地址加1 :%p\n", &ch + 1);
    return 0;
}

輸出:

num 的地址:0x7fffe8244288
num 的地址加1 :0x7fffe824428c
ch 的地址:0x7fffe8244287
ch 的地址加1 :0x7fffe8244288

答案int * 加1是4個位元組;char * 加1是1個位元組。&num 和 &ch 分別代表該變數的全部位元組。

指針交換數據

比如這段代碼是不能實現 a、b 兩數交換。請看示例:

#include <stdio.h>

void swap(x, y){
    int tmp = x;
    x = y;
    y = tmp;
}
int main() {
    int a = 1;
    int b = 2;
    swap(a, b);
    printf("a:%d\n", a);
    printf("b:%d\n", b);
    return 0;
}
a:1
b:2

分析:調用 swap(a, b) 這裡是一個值傳遞,找到函數入口地址,對參數 x、y 申請空間和賦值,通過 tmp 變數完成了 x和y的交換,最後回收局部變數 x、y和tmp,釋放空間。而 a,b數據沒有變化。

可以通過指針來實現兩數的交換。請看示例:

#include <stdio.h>

void swap(int* x, int* y){
    int tmp = *x;
    *x = *y;
    *y = tmp;
}
int main() {
    int a = 1;
    int b = 2;
    swap(&a, &b);
    printf("a:%d\n", a);
    printf("b:%d\n", b);
    return 0;
}
a:2
b:1

分析:通過 swap(&a, &b) 將 a b 的地址傳給 x 和 y,通過 x 和 y 指針對 a 和 b 進行交換,雖然最後會銷毀swap中的局部變數,但 a 和 b的值已經完成了交換。

指針的運算符

指針和變數的關係

練習1

題目:輸出什麼?

#include <stdio.h>

int main() {
    int a = 10, *pa = &a, *pb;
    printf("%d\n", *pa);
    pb = pa;
    printf("%d\n", *pb);
    return 0;
}

輸出:10 10

分析:


int a = 10, 
// pa 指向變數 a
*pa = &a, 
// 定義一個整數型的指針 pb
*pb;
printf("%d\n", *pa);

// pb 也指向變數 a
pb = pa;
printf("%d\n", *pb);
return 0;

練習2

題目:輸出什麼?

#include <stdio.h>

int main() {
    int x = 3, y = 0, *px = &x;

    y = *px + 5;
    printf("%d\n", y);

    y= ++*px;
    printf("%d\n", y);

    printf("%p\n", px);

    y = *px++; 
    printf("%p\n", px);
    printf("%d\n", y);

    return 0;
}

輸出:

8
4
0x7ffc48b9be38
0x7ffc48b9be3c
4

分析:

  • y= ++*px; 等效 ++(*px)。如果是 ++* 是不對的
類似 y = ++i,等於先執行 ++,在執行 y = i,
這裡先對 (*px) 執行 ++,在返回  *px 的值
  • y = *px++;
先執行 y = *px,然後是 px++。px是整數類型的地址,加1就是加4個位元組。

練習3

題目:輸出什麼?

#include <stdio.h>

int main() {
    int x = 3, y = 0, *px = &x;
    printf("%p\n", px);
    y = (*px)++; 
    printf("%p\n", px);
    printf("%d\n", x);

    return 0;
}

輸出:

0x7ffef1dc4d58
0x7ffef1dc4d58
4

分析:*px++ 表示指針加1,(*px)++ 表示值加1。

指針初始化

指針初始化有兩種方法:已經存在的空間和自己申請空間。

已經存在的空間,例如:

#include <stdio.h>
#include <stdlib.h>
int main() {
    int num;
    int* p = &num;
    *p = 10;

    char *str = "abc";
    printf("%s\n", str); // abc。把字元串的地址賦值給指針變數
    return 0;
}

自己申請空間可以使用 malloc 函數。申請的是 void 類型指針,也稱為通用類型指針。請看示例:

#include <stdio.h>
// malloc 需要引入 <stdlib.h>
#include <stdlib.h>
int main() {
    // 申請16個位元組
    int* q = malloc(sizeof(int) * 4); // 在堆里申請了16個位元組
    // int* q = (int *)malloc(sizeof(int) * 4); // 推薦
    
    *q = 10;
    // 釋放申請的16個位元組
    free(q);
    return 0;
}

申請空間,使用完需要使用 free() 釋放。

Tip:根據 C99 標準以及更高版本的標準,顯式的類型轉換是建議的做法,以確保類型的安全性和可讀性。

空指針和野指針

下麵這段代碼 p 就是一個野指針,運行報錯:段錯誤 (核心已轉儲)

#include <stdio.h>

int main() {
    int* p;
    *p = 1;
    return 0;
}

這裡聲明一個指針 p,裡面是一個隨機數,例如 0x7ffe71df3f40,接著往指向的記憶體放1,由於這塊記憶體不知道是否存在,即使存在也不能訪問,於是報段錯誤

直接手寫一個地址也不可以。就像這樣:

#include <stdio.h>

int main() {
    
    // warning: incompatible integer to pointer conversion initializing 'int *' with an expression of type 'long' [-Wint-conversion]
    // 這個警告是因為你正在將一個 long 類型的表達式賦值給一個 int* 類型的指針變數,導致類型不匹配。
    // int* p = 0x7ffe71df3f40;
    int* p = (int *)0x7ffe71df3f40;
    *p = 100;
    return 0;
}
// 分段錯誤 (核心已轉儲)"
Segmentation fault (core dumped)

空指針也不能使用:

int* p = NULL;
*p = 100;

// 輸出:`Segmentation fault (core dumped)`

但空指針會讓你可控。就像這樣:

int* p = NULL;

if (p != NULL) {
    printf("p is not NULL\n");
}else{
    printf("p is NULL\n");
}

// 輸出:p is NULL

指針和數組

指針當數組用

遍歷一個數組,可以這樣:

#include <stdio.h>

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int length = sizeof(arr) / sizeof(arr[0]);  // 計算數組的長度
    // 1 2 3 4 5 
    for (int i = 0; i < length; ++i) {
        printf("%d ", arr[i]);
    }

    return 0;
}

使用指針遍曆數組有兩種方式(效果相同)。請看示例:

#include <stdio.h>

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int length = sizeof(arr) / sizeof(arr[0]);  // 計算數組的長度

    // 指針遍歷方式1
    /*
    int* pArr = arr;
    for (int i = 0; i < length; ++i) {
        printf("%d ", *(pArr + i));
    }
    */

    // 指針遍歷方式2
    int* pArr = arr;
    for (int i = 0; i < length; ++i) {
        printf("%d ", pArr[i]);
    }

    return 0;
}

Tip:在數組一文中我們知道數組名錶示首元素地址,這裡*(pArr + i)會依次遍曆數組或許是因為指針是int類型吧!

總結pArr[i] 等於 *(pArr + i)。在這裡[]不再是取某個索引,而是表示取值。

指針和字元數組

題目:分析 char a[] = "Hello";char *b = "World";

  • 都可以用for遍歷元素。例如:
#include <stdio.h>

int main() {
    char a[] = "Hello";
    char *b = "World";

    // Iterating over 'a'
    printf("Characters in 'a':\n");
    for (int i = 0; a[i] != '\0'; i++) {
        printf("%c\n", a[i]);
    }

    // Iterating over 'b'
    printf("\nCharacters in 'b':\n");
    for (int i = 0; b[i] != '\0'; i++) {
        printf("%c\n", b[i]);
    }

    return 0;
}

輸出:

開始運行...

Characters in 'a':
H
e
l
l
o

Characters in 'b':
W
o
r
l
d

運行結束。
  • 為什麼指針也可以通過索引訪問特定字元?
    比如 char *b = "World";,可以將字元串視為字元數組,使用指針來指向該數組的首地址,指針可以通過偏移來訪問特定位置的元素,包括字元串中的字元。

練習

題目:下麵代碼中 p1[0]p2[0]p3[0]的值分別是多少?

// 申請4*4個位元組,每個位元組地址假如是:0x100(存放1) 0x104(存放2) 0x108 0x10c
int a[] = {1,2,3,4};

int *p1 = (int*)(&a + 1);

int *p2 = (int*)((int)a + 1);

int *p3 = (int*)(a + 1);

分析:

  • (int*)(&a + 1) - &a 表示整個數組,加1則到下一個數組,然後將數組指針強轉成整數指針,指向第5個元素,其實已經越界了。
  • (int*)((int)a + 1) - a 表示數組首元素地址,(int)a 將地址轉為整數,以前是加1個元素,現在就是加1,然後又將整數轉為整數指針,亂了(就好比訪問 0x101 0x102 0x103 0x104
  • (int*)(a + 1) - a 表示數組首元素地址,加1則是第二個元素地址 0x104,不強轉也可以。

結論:只有p3[0](等價於 *(p3 + 0))是一個正常的元素,也就是2.

指針和字元串

題目:用數組和指針定義字元串有什麼區別?

#include <stdio.h>

int main() {
    char str[] = "HelloWorld";
    // HelloWorld
    printf("%s\n", str);

    char* s = "HelloWorld";
    // HelloWorld
    printf("%s\n", s);

    return 0;
}

Tip: 字元串的輸出都是首地址,比如這裡的 str 是數組的首地址,s 指針指向的也是首地址。

分析:
char str[] = "HelloWorld"; 在棧中定義一個數組,用11個位元組存儲HelloWorld(還有一個 \0)。請看示例:

#include <stdio.h>

int main() {
    char str[] = "HelloWorld";
    str[0]++; 
    // IelloWorld
    printf("%s\n", str); 
    // error: cannot increment value of type 'char[11]'
    str++;
    // printf("%s\n", str);
    return 0;
}

數組名(str++)不可以修改,str 就是數組首元素地址,已經固定了,可認為它是常量。但數組內容可以修改。

char* s = "helloWorld";helloWorld 放在只讀數據區,s 是局部變數,放在棧中,占8個位元組。請看示例:

#include <stdio.h>

int main() {
    char* s = "helloWorld";
    
    s++;
    // elloWorld
    printf("%s\n", s);

    // 報錯:Segmentation fault (core dumped)
    s[0]++;

    return 0;
}

指針可以加加,但指針指向的內容不能修改。

str 只是個名字,不占空間,如果一定要說占多少,那就是它執行的數組占11個位元組。而 s 是8個位元組,指向一個只讀區,占 11 個位元組。

練習

題目:分析以下示例。

#include <stdio.h>

int main() {
    char str[20];
    str = "HelloWorld";

    char* s;
    s = "HelloWorld";
    // HelloWorld
    printf("%s\n", s);

    return 0;
}

分析:

// 分配20個位元組的記憶體,並把首地址給 str
char str[20];
// str 是只讀的,不能再賦值。報錯:`error: array type 'char[20]' is not assignable`
str = "HelloWorld";

// 定義一個指針 s
char* s;
// 將 HelloWorld 的首地址給 s
s = "HelloWorld";

擴展

自定義strcpy()函數

題目:實現原生字元串拷貝方法strcpy。strcpy 其用法如下:

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

int main() {
    char source[] = "Hello";
    char destination[10]; // 目標字元串需要足夠的空間來容納 source 字元串

    strcpy(destination, source);

    printf("Source string: %s\n", source);
    printf("Destination string: %s\n", destination);

    return 0;
}

實現:

#include <stdio.h>

char* strcpy_custom(char* destination, const char* source) {
    // 字元串數組末尾有一個特殊的空字元 '\0' 來表示字元串的結束。逐個複製字元,直到遇到源字元串的結束標誌 '\0'
    while (*source != '\0') {
        *destination = *source;
        destination++;
        source++;
    }

    *destination = '\0'; // 在目標字元串末尾添加結束標誌 '\0'

    return destination;
}

int main() {
    // 定義兩個字元數組
    char source[] = "Hello";
    char destination[10]; // 目標字元串需要足夠的空間來容納 source 字元串

    // 數組名。表示首元素的地址,加 1 是加一個元素(比如這裡1個位元組)
    strcpy_custom(destination, source);

    printf("Source string: %s\n", source);
    printf("Destination string: %s\n", destination);

    return 0;
}

Tipconst char* source 中 const 的作用請看const 和指針

輸出:

開始運行...

Source string: Hello
Destination string: Hello

運行結束。

將 while 替換成下麵一行代碼效果也相同:

char* strcpy_custom(char* destination, const char* source) {
    /*
    while (*source != '\0') {
        *destination = *source;
        destination++;
        source++;
    }

    *destination = '\0'; 
    */

    // 替換成
    while((*destination++ = *source++) != '\0');
    return destination;
}

分析:(*destination++ = *source++) != '\0':

之前的是首先判斷,在賦值。`*source != '\0'`、`*destination = '\0';`,這裡是先賦值

後置++會放在表達式最後,所以等於:

(*destination = *source) != '\0';
destination++;
source++;

const 和指針

首先補充下(int*)的作用。之前說到 const 定義的變數可以被修改,我們寫瞭如下代碼:

#include <stdio.h>

int main() {
    const int val =5;

    int *ptr= (int*)&val;
    *ptr=10;

    printf("val = %d\n",val);
    printf("*ptr = %d\n", *ptr);

    return 0;
}

其中 int *ptr= (int*)&val; 是將一個 const int 類型的變數 val 地址強制轉換為 int* 類型的指針,並將指針存儲在 ptr 中。這種類型轉換是不安全的,因為它丟失了 val 的常量性質。

const char* source 聲明一個常量指針,以下代碼僅做示意:

#include <stdio.h>

int main() {
    const char* source = "Hello";
    char* mutableSource = "World";

    printf("%c\n", source[0]);
    printf("%c\n", mutableSource[0]);

    // 以下操作是非法的,會導致編譯錯誤
    // source[0] = 'h'; // 不能修改字元數據

    // 合法
    // 儘管mutableSource是一個非常量指針,看起來可以進行修改,但修改字元串常量是不被允許的,並且這可能導致未定義行為。
    mutableSource[4] = 'w'; // 可以修改字元數據
    return 0;
}

運行:

開始運行...

H
W
Segmentation fault (core dumped)

運行結束。
就近原則

const 有個就近原則

  • 比如:const int* p1 = &num;,const 修飾的是 *,所以 *p1 不能修改, p1 可以修改
  • 比如:int* const p2 = &num;,const 修飾 p2,所以 p2 不能修改,*p2 可以修改

請看示例:

#include <stdio.h>

int main() {
    int num = 1;
    const int* p1 = &num; // const 修飾的是 *,所以 *p1 不能修改, p1 可以修改

    p1++;
    // (*p1)++;

    int* const p2 = &num; // const 修飾 p2,所以 p2 不能修改,*p2 可以修改

    // p2++;
    (*p2)++;

    const int* const p3 = &num; // 兩個都不能修改
    // p3++;
    // (*p3)++;

    return 0;
}

gdb 調試段錯誤

GDB(GNU Debugger)是一款強大的調試器,用於幫助開發者查找和解決程式中的錯誤。通過與源代碼交互,並提供諸如斷點設置、變數觀察、記憶體檢查等功能,GDB允許開發者逐行執行程式並分析其運行狀態。
除了上文使用的 run,還有如下操作

  • run:運行程式。
  • break <line_number>:在指定行設置斷點。
  • break <function_name>:在指定函數設置斷點。
  • continue:繼續執行程式直到下一個斷點或程式結束。
  • next:逐過程地執行程式。
  • step:逐語句地執行程式。
  • print <variable>:列印變數的值。
  • backtrace:顯示函數調用的堆棧跟蹤信息。
  • quit:退出GDB調試會話。

使用 gdb 調試段錯誤的過程如下:

編寫代碼:

pjl@pjl-pc:~/pjl$ cat demo-3.c
#include <stdio.h>

int main() {
    int* p;
    *p = 1;

    return 0;
}

編譯運行發現段錯誤:

pjl@pjl-pc:~/pjl$ gcc demo-3.c -o demo-3
pjl@pjl-pc:~/pjl$ ./demo-3
段錯誤 (核心已轉儲)

將代碼編譯為可調試的可執行文件。在gcc或g++編譯時,添加"-g"選項可以生成包含調試信息的可執行文件。

// 增加 -g
pjl@pjl-pc:~/pjl$ gcc demo-3.c -o demo-3 -g

// 啟動GDB並載入可執行文件
pjl@pjl-pc:~/pjl$ gdb demo-3
GNU gdb (Ubuntu 9.1-0kylin1) 9.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from demo-3...

(gdb) 

輸入 run(還有其他操作) 找到是第5行代碼報錯:

...
// run:運行程式。
(gdb) run
Starting program: /home/pjl/pjl/demo-3

Program received signal SIGSEGV, Segmentation fault.
0x0000555555555135 in main () at demo-3.c:5
5           *p = 1;
(gdb)

高級指針

提前透露:指針遇上數組

題目:以下代碼輸出什麼?

#include <stdio.h>

int main() {
    char * string[] = {"Hello", "World" };
    printf("%s\n", string);
    return 0;
}

分析:
我們知道定義字元串有以下兩種方法:

char str[] = "HelloWorld";
char* s = "HelloWorld";

Tip: string 在 C 中不是關鍵字,也不是保留字,就是一個普通變數名。

[] 的優先順序是非常高的,這裡首先是定義一個數組(string[]),其次就是指針,合起來就是一個指針數組。

首先在只讀區分配兩塊記憶體分別存放 Hello(地址比如是 0x100) 和 World(地址比如是 0x200),指針數組是16個位元組,本質就是數組,只不過裡面放的是指針,比如前8個位元組的地址是0x1000,那麼 string 就是 0x1000,因為數組名就是數組首元素地址。

所以要輸出這兩個字元串,可以這麼寫:

#include <stdio.h>

int main() {
    char * string[] = {"Hello", "World" };

    // Hello
    printf("%s\n", string[0]);
    // World
    printf("%s\n", string[1]);
    return 0;
}
作者:彭加李
出處:https://www.cnblogs.com/pengjiali/p/17502968.html
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接。

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

-Advertisement-
Play Games
更多相關文章
  • > 本文首發於公眾號:Hunter後端 > 原文鏈接:[celery筆記七之周期/定時任務及crontab定義](https://mp.weixin.qq.com/s/sNShaRbuM2gm2qn_codaTg) periodic task,即為周期,或者定時任務,比如說每天晚上零點零分需要運行一 ...
  • ## 前言 在C語言中,指針是一種非常強大和靈活的工具,但同時也容易引發一些問題,其中包括空指針和野指針。 本文將帶你瞭解這兩個概念的含義、產生原因以及如何避免它們所導致的問題。 ## 一、人物簡介 - 第一位閃亮登場,有請今後會一直教我們C語言的老師 —— 自在。 ![](https://img2 ...
  • 今晚來聊聊我在**技術成長**中的一些感悟,跟大家分享下。 ## BALABALA 在大學的時候,我一個電腦專業相關的證書都沒考,自認為這些證書對我以後找工作沒什麼大的幫助。於是我把時間更多地花在研究八股文上,因為八股文在面試的時候是要用到的。 (**利益化**) > **我會對我做的事情利益化* ...
  • ### Kubernetes 概述 當下,我們很多項目於都在`Cloud Native`(雲原生)的上面,這種方法旨在使組織能夠確保可用性並快速響應和適應變化,雲原生其實就是一組本質上支持在不同雲環境(公共雲、私有雲或混合雲)上大規模構建、運行和管理應用程式的實踐和技術。 雲原生離不開兩個概念:`容 ...
  • # 下載 這就不多說了,直接官網下載 https://tomcat.apache.org/ 直接解壓 配置 環境變數 (提前安裝好java,配置好java的環境變數) 配置Tomcat環境變數前一定要配置好java的環境變數,尤其是JAVA_HOME 新建 `CATALINA_HOME` 環境變數, ...
  • 狀態機,包括了狀態和動作,某個**狀態**下,只能執行某些**動作**,如果**動作**不匹配,狀態是不會進行變更了,這樣就保護了我們狀態欄位的準備性,不能隨意改變,必須按著我們**設計的規則**進行狀態的輪轉。 # Stateless實現的狀態機 1. **Stateless**:Stateles ...
  • 來源:blog.csdn.net/qq_35387940/article/details/129167329 ## **前言** 平時做一些統計數據,經常從資料庫或者是從介面獲取出來的數據,單位是跟業務需求不一致的。 - 比如, 我們拿出來的 分, 實際上要是元 - 又比如,我們拿到的數據需要 乘以 ...
  • # 1.文件路徑 我們發現不管是寫入還是寫出操作,我們提供的都是文件名,其實這裡準確說應該是文件路徑。當我們簡單把文件名傳遞給open函數時,Python將在當前執行程式的文件所在的目錄中查找文件名所代表的文件。 根據組織文件的方式,可能需要打開不在當前執行程式文件所屬目錄中的文件。如果此時我們把該 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...