簡體   English   中英

通過 strlen 的 function 計算字符串的長度

[英]calculate the length of string by the function of strlen

  char arr[]={'a','b','c'};
  int len=strlen(arr);

我知道當 char 的指針遇到'0'的地址時,這個 function 將停止運行並返回數組的第一個地址和'0'的地址之間的長度。 但是當我用這種方式創建一個字符串時,我沒有放'0' 所以 char 的指針可能會繼續移動以找到'0'的地址。 在這個過程中,指針可能會出現越界訪問錯誤。 那么為什么這段代碼沒有向我發出警告或者為什么這段代碼沒有出錯呢?

strlen()僅適用於零終止字符 arrays,而您所擁有的不是一個。

len為您的程序返回的內容完全取決於地址arr + 3之后發生在 memory 中的內容。

如果那里有一個零,那么你會得到 3。如果在零之前還有其他數據,那么你會得到另一個數字。 如果您不走運並且沒有零(在您的進程的 memory 空間中),您的程序將因越界讀取而崩潰。

例如,程序

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

int main(void) {
  char blarr[] = {'d', 'e', 'f'};
  char arr[] = {'a', 'b', 'c'};
  int len = strlen(arr);
  printf("%d\n", len);
  return 0;
}

可能會打印 6,具體取決於編譯器如何在堆棧上分配arrblarr

你的編譯器不會發出任何警告,因為你的程序在技術上是正確的——你將char*傳遞給strlen ,這很好——但它不夠聰明,無法檢測到char*不是以零結尾的字符串。

所以 char 的指針可能會一直移動以找到 '0' 的地址。在這個過程中,指針可能會出現越界訪問錯誤。

是的,這正是發生的事情。

那么為什么這段代碼沒有向我發出警告或者為什么這段代碼沒有出錯呢?

因為聲明

char arr[] = {'a','b','c'};

是完全有效的。 您沒有向編譯器表明您打算將arr用作字符串。

一個更有趣的例子是,如果你要寫

char arr[3] = "abc";

由於歷史上的怪癖,這是完全合法的 C,盡管它創建了完全相同的數組arr並且如果將它傳遞給strlen也會遇到完全相同的問題。 不過,在這里,我相信一些編譯器發出警告,而且這肯定是一個適當的警告,因為該功能是值得商榷的,而且很少有人故意使用。

很多時候,它是關於管理期望。

讓我們從一個小的思想實驗開始(或者時光倒流回到計算的早期),沒有編程語言——只有機器代碼。 在那里,您將(使用特定於 CPU 的指令)編寫如下內容來表示字符串:

arr: db 'a','b','c'
strlen:                         ; RDI (pointer to string) -> RAX (length of string)
                                ; RAX length counter and return value
                                ; CL used for null character test
        xor RAX, RAX            ; set RAX to 0
strlen_loop:
        mov cl, [rdi]           ; load CL with the byte pointed to by argument
        test cl,cl
        jz strlen_loop_done
        inc rdi                 ; look at next byte in argument
        inc rax                 ; increment the length counter
        jmp strlen_loop
strlen_loop_done:
        ret                     ; rax contains a zero terminated strings length

相比之下,在 C 中寫同樣的 function 要簡單得多。

  • 我們不必關心寄存器分配(哪個寄存器做什么)。
  • 我們不依賴特定 CPU 的指令集
  • 我們不必查找目標系統的“調用約定”或 ABI(參數傳遞約定等)
size_t strlen(const char* s) {
  size_t l = 0;
  while (*s) {
    l++;
    s++;
  }
  return l;
}

約定,“字符串”只是指向帶有 null 值終止符的字符(字節)的指針,這無疑是相當隨意的,但 C 編程語言“附帶”。 這只是一個約定。 編譯器本身對此一無所知(哦,它確實知道在字符串文字上添加終止 null)。 但是在調用strlen()時,它無法區分字符串大小寫和字節數組大小寫。 為什么? 因為沒有特定的字符串類型。

因此,它和我上面給出的匯編代碼版本一樣聰明。 它依賴於“c-string-convention”。 匯編器不檢查,C 編譯器也不檢查,因為 - 老實說,C 的主要成就是我上面給出的項目符號。

因此,如果您管理您的期望,關於語言 C,請將其想象為:一種經過美化的匯編語言的稍微抽象的版本。

如果您對 c-string 約定感到惱火(畢竟strlen在時間復雜度上是O(n) ),您仍然可以想出自己的字符串類型,也許是這樣:

typedef struct String_tag {
  size_t length;
  char data[];
} String_t;

並編寫自己的助手(在堆上創建一個字符串)和宏(用alloca或其他東西在堆棧上創建一個字符串)。 並圍繞該類型編寫您自己的字符串特征庫。

如果您剛剛開始使用 C,而不是處理更大的事情,我認為這將是學習語言的一個很好的練習。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM