主頁 > 作業系統 > 是否可以將字串轉換為C中的整數/長表示形式?

是否可以將字串轉換為C中的整數/長表示形式?

2022-04-26 01:28:01 作業系統

在反編譯各種程式(我沒有源代碼)后,我發現了一些有趣的代碼序列。程式str在 DATA 部分中定義了一個 c 字串 ( )。在 TEXT 部分的某些函式中,該字串的一部分是通過將十六進制數移動到字串中的某個位置來設定的(簡化的 Intel 匯編MOV str,0x006f6c6c6568)。這是C中的一個片段:

#include <stdio.h>

static char str[16];

int main(void)
{
    *(long *)str = 0x006f6c6c6568;
    printf("%s\n", str);
    return 0;
}

我正在運行 macOS,它使用 little endian,因此0x006f6c6c6568轉換為hello. hello該程式編譯時沒有錯誤或警告,并且在運行時按預期列印出來。0x006f6c6c6568手工計算,但我想知道 C 是否可以為我計算。我的意思是這樣的:

#include <stdio.h>

static char str[16];

int main(void)
{
    // *(long *)str = 0x006f6c6c6568;
    *(str 0) = "hello";
    printf("%s\n", str);
    return 0;
}

現在,我不想將其"hello"視為字串文字,對于 little-endian 可能會像這樣對待:

    *(long *)str = (long)(((long)'h') |
                          ((long)'e' << 8) |
                          ((long)'l' << 16) |
                          ((long)'l' << 24) |
                          ((long)'o' << 32) |
                          ((long)0 << 40));

或者,如果為大端目標編譯,則:

    *(long *)str = (long)(((long) 0  << 16) |
                          ((long)'o' << 24) |
                          ((long)'l' << 32) |
                          ((long)'l' << 40) |
                          ((long)'e' << 48) |
                          ((long)'h' << 56));

想法?

uj5u.com熱心網友回復:

是否有一些內置的 C 函式/方法/前處理器函式/運算子/等。可以將 8 個字符的字串轉換為其原始十六進制表示的 long 型別

我看到你已經接受了一個答案,但我認為這個解決方案更容易理解并且可能是你想要的。

只需將字串位元組復制為 64 位整數型別即可。我將使用uint64_t而不是,long因為它保證在所有平臺上都是 8 個位元組。long通常只有 4 個位元組。

#include <string.h>
#include <stdint.h>
#include <inttypes.h>

uint64_t packString(const char* str) {
    uint64_t value = 0;
    size_t copy = str ? strnlen(str, sizeof(value)) : 0; // copy over at most 8 bytes

    memcpy(&value, str, copy);
    return value;
}

例子:

int main() {
    printf("0x%" PRIx64 "\n", packString("hello"));
    return 0;
}

然后構建并運行:

$:~/code/packString$ g main.cpp -o main

$:~/code/packString$ ./main

0x6f6c6c6568

uj5u.com熱心網友回復:

TL:DR: 你想strncpy進入uint64_t. 這個答案很長,試圖解釋概念以及如何從 C 與 asm 的角度以及整個整數與單個chars / 位元組的角度來考慮記憶體。(即如果很明顯 strlen/memcpy 或 strncpy 會做你想做的事,直接跳到代碼。)


如果您想將 8 個位元組的字串資料準確地復制到一個整數中,請使用memcpy. 整數的物件表示將是那些字串位元組。

字串總是在最低地址處具有第一個char,即一系列char元素,因此位元組序不是一個因素,因為在 a 中沒有尋址char與整數不同,它取決于位元組序,哪一端是最低有效位元組。

將此整數存盤到記憶體中將具有與原始字串相同的位元組順序,就像您memcpychar tmp[8]陣列而不是uint64_t tmp. (C 本身沒有任何記憶體與暫存器的概念;每個物件都有一個地址,除非通過 as-if 規則進行優化允許,但是分配給某些陣列元素可以讓真正的編譯器使用存盤指令,而不是僅僅放置暫存器中的常量。因此您可以使用除錯器查看這些位元組,并查看它們的順序是否正確。或者傳遞一個指向fwriteorputs或其他的指標。)

memcpy避免來自對齊和嚴格混疊違規的可能未定義行為*(uint64_t*)str = val;iememcpy(str, &val, sizeof(val))是一種在 C 中表達未對齊的嚴格別名安全的 8 位元組加載或存盤的安全方式,就像您可以mov在 x86-64 asm 中輕松完成一樣。
GNU C 也允許你 typedef uint64_t aliasing_u64 __attribute__((aligned(1), may_alias));——你可以指向任何東西并安全地讀/寫它,就像使用 8 位元組的 memcpy 一樣。)

char*并且unsigned char*可以在 ISO C 中為任何其他型別設定別名,因此使用 memcpy 甚至strncpy撰寫其他型別的物件表示是安全的,尤其是那些具有保證格式/布局的物件表示,例如uint64_t(固定寬度,無填充,如果它存在的話) )。


如果您想要較短的字串以零填充到整數的完整大小,請使用strncpy. 在 little-endian 機器上,它就像一個寬度整數CHAR_BIT * strlen()被零擴展為 64 位,因為字串后面的額外零位元組進入表示整數最高有效位的位元組。

在大端機器上,該值的低位將為零,就好像您將“窄整數”左移到更寬整數的頂部一樣。(并且非零位元組的順序不同。彼此)。
在混合端機器(例如 PDP-11)上,描述起來沒那么簡單。

strncpy對實際的字串不好,但正是我們想要的。對于普通的字串復制來說效率很低,因為它總是寫出指定的長度(浪費時間并觸摸長緩沖區的其他未使用部分以進行短副本)。而且它對于字串的安全性不是很有用,因為它不會為帶有大源字串的終止零留出空間。
但是這兩件事正是我們在這里想要/需要的:它的行為類似于memcpy(val, str, 8)長度為 8 或更高的字串,但對于較短的字串,它不會在整數的高位元組中留下垃圾。

示例:字串的前 8 個位元組

#include <string.h>
#include <stdint.h>

uint64_t load8(const char* str)
{
    uint64_t value;
    memcpy(&value, str, sizeof(value));     // load exactly 8 bytes
    return value;
}

uint64_t test2(){
    return load8("hello world!");  // constant-propagation through it
}

這編譯非常簡單,使用 GCC 或Godbolt 編譯器資源管理器上的一個 x86-64 8 位元組 mov 指令

load8:
        mov     rax, QWORD PTR [rdi]
        ret

test2:
        movabs  rax, 8031924123371070824  # 0x6F77206F6C6C6568 
          # little-endian "hello wo", note the 0x20 ' ' byte near the top of the value
        ret

在 ISA 上,未對齊的負載只能以最壞的速度損失作業,例如 x86-64 和 PowerPC64,memcpy可靠地行內。但是在 MIPS64 上你會得到一個函式呼叫。

# PowerPC64 clang(trunk) -O3
load8:
        ld 3, 0(3)            # r3 = *r3   first arg and return-value register
        blr

順便說一句,我使用sizeof(value)而不是8出于兩個原因:首先,您可以更改型別而無需手動更改硬編碼的大小。

其次,因為一些不起眼的 C 實作(例如具有字可尋址存盤器的現代 DSP)沒有CHAR_BIT == 8. 通常是 16 或 24,與sizeof(int) == 1ie 相同char我不確定位元組在字串文字中的確切排列方式,例如每個char單詞是否有一個字符,或者您是否只有一個少于 8 的 8 個字母的字串chars,但至少您不會t 在區域變數外部寫入時具有未定義的行為。

示例:短字串strncpy

// Take the first 8 bytes of the string, zero-padding if shorter
// (on a big-endian machine, that left-shifts the value, rather than zero-extending)
uint64_t stringbytes(const char* str)
{
    // if (!str)  return 0;   // optional NULL-pointer check
    uint64_t value;           // strncpy always writes the full size (with zero padding if needed)
    strncpy((char*)&value, str, sizeof(value)); // load up to 8 bytes, zero-extending for short strings
    return value;
}

uint64_t tests1(){
    return stringbytes("hello world!");
}
uint64_t tests2(){
    return stringbytes("hi");
}
tests1():
        movabs  rax, 8031924123371070824     # same as with memcpy
        ret
tests2():
        mov     eax, 26984        # 0x6968 = little-endian "hi"
        ret

strncpy錯誤功能(使其不適合人們希望它的設計用途,截斷strcpy到極限)是為什么像 GCC 這樣的編譯器警告這些有效用例的-Wall. 那和我們的非標準用例,我們想要截斷更長的字串文字只是為了演示它是如何作業的。這不是strncpy' 的錯,但有關通過與目標實際大小相同的長度限制的警告是。

n function 'constexpr uint64_t stringbytes2(const char*)',
    inlined from 'constexpr uint64_t tests1()' at <source>:26:24:
<source>:20:12: warning: 'char* strncpy(char*, const char*, size_t)' output truncated copying 8 bytes from a string of length 12 [-Wstringop-truncation]
   20 |     strncpy(u.c, str, 8);
      |     ~~~~~~~^~~~~~~~~~~~~
<source>: In function 'uint64_t stringbytes(const char*)':
<source>:10:12: warning: 'char* strncpy(char*, const char*, size_t)' specified bound 8 equals destination size [-Wstringop-truncation]
   10 |     strncpy((char*)&value, str, sizeof(value)); // load up to 8 bytes, zero-extending for short strings
      |     ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

大端示例:PowerPC64

奇怪的是,MIPS64 的 GCC 并不想 inline strnlen,而且 PowerPC 無論如何都可以更有效地構造大于 32 位的常量。(更少的移位指令,oris可以 OR 到位 [31:16],即 OR 移位立即數。)

uint64_t foo = tests1();
uint64_t bar = tests2();

編譯為 C 以允許函式回傳值作為全域變數的初始值設定項,PowerPC64 的 clang (trunk) 將上面的常量傳播編譯到.data這些全域變數的初始化靜態存盤中,而不是在啟動時呼叫“建構式”來存盤到不幸的是,像 GCC 這樣的 BSS。(這很奇怪,因為 GCC 的初始化函式只是從立即數本身構造值并存盤。)

foo:
        .quad   7522537965568948079             # 0x68656c6c6f20776f
                                      # big-endian "h e l l o   w o"

bar:
        .quad   7523544652499124224             # 0x6869000000000000
                                      # big-endian "h i \0\0\0\0\0\0"

asm fortests1()一次只能從立即數 16 位構造一個常量(因為一條指令只有 32 位寬,并且操作碼和暫存器號需要一些空間)。 神螺栓

# GCC11 for PowerPC64 (big-endian mode, not power64le)  -O3 -mregnames 
tests2:
        lis %r3,0x6869    # Load-Immediate Shifted, i.e. big-endian "hi"<<16
        sldi %r3,%r3,32   # Shift Left Doubleword Immediate  r3<<=32 to put it all the way to the top of the 64-bit register
          # the return-value register holds 0x6869000000000000
        blr               # return

tests1():
        lis %r3,0x6865        # big-endian "he"<<16
        ori %r3,%r3,0x6c6c    # OR Immediate producing "hell"
        sldi %r3,%r3,32       # r3 <<= 32
        oris %r3,%r3,0x6f20   # r3 |=  "o " << 16
        ori %r3,%r3,0x776f    # r3 |=  "wo"
          # the return-value register holds 0x68656c6c6f20776f
        blr

我玩了一下,讓常量傳播為uint64_t foo = tests1()C 中全域范圍的初始化程式作業(C 首先不允許非常量初始化程式),看看我是否可以讓 GCC 做 clang 所做的事情。至今沒有成功。即使使用constexprC 20 std::bit_cast<uint64_t>(struct_of_char_array),我也無法讓 g 或 clang 接受在語言實際需要 auint64_t foo[stringbytes2("h")]的背景關系中使用整數值,而不僅僅是一種優化。 神箭constexpr

IIRC std::bit_cast 應該能夠從字串文字中制造一個 constexpr 整數,但我可能忘記了一些技巧;我還沒有搜索現有的 SO 答案。我似乎記得看到一個bit_cast與某種 constexpr 型別雙關語相關的地方。


感謝@selbie 的strncpy想法和代碼的起點;出于某種原因,他們將答案更改為更復雜并避免strncpy,因此如果沒有發生常量傳播,假設strncpy使用手寫 asm 的良好庫實作可能會更慢。但無論哪種方式仍然使用字串文字進行行內和優化。

就正確性而言,他們當前對零初始化的答案與strnlen完全等效,但對于運行時變數字串的編譯效率較低。memcpyvalue

uj5u.com熱心網友回復:

添加#if __BYTE_ORDER__判斷,像這樣:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
    *(long *)str = (long)(((long)'h') |
                          ((long)'e' << 8) |
                          ((long)'l' << 16) |
                          ((long)'l' << 24) |
                          ((long)'o' << 32) |
                          ((long)0 << 40));
#else
    *(long *)str = (long)((0 |
                           ((long)'o' << 8) |
                           ((long)'l' << 16) |
                           ((long)'l' << 24) |
                           ((long)'e' << 32) |
                           ((long)'h' << 40));
#endif

轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/463856.html

標籤:C 细绳 部件 字节序 类型双关语

上一篇:我可以在Linux行程和分叉子行程之間創建共享資料部分嗎?

下一篇:為什么printf仍然可以使用低于XMM暫存器中FPargs數量的RAX?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • CA和證書

    1、在 CentOS7 中使用 gpg 創建 RSA 非對稱密鑰對 gpg --gen-key #Centos上生成公鑰/密鑰對(存放在家目錄.gnupg/) 2、將 CentOS7 匯出的公鑰,拷貝到 CentOS8 中,在 CentOS8 中使用 CentOS7 的公鑰加密一個檔案 gpg -a ......

    uj5u.com 2020-09-10 00:09:53 more
  • Kubernetes K8S之資源控制器Job和CronJob詳解

    Kubernetes的資源控制器Job和CronJob詳解與示例 ......

    uj5u.com 2020-09-10 00:10:45 more
  • VMware下安裝CentOS

    VMware下安裝CentOS 一、軟硬體準備 1 Centos鏡像準備 1.1 CentOS鏡像下載地址 下載地址 1.2 CentOS鏡像下載程序 點擊下載地址進入如下圖的網站,選擇需要下載的版本,這里選擇的是Centos8,點擊如圖所示。 決定選擇Centos8后,選擇想要的鏡像源進行下載,此 ......

    uj5u.com 2020-09-10 00:12:10 more
  • 如何使用Grep命令查找多個字串

    如何使用Grep 命令查找多個字串 大家好,我是良許! 今天向大家介紹一個非常有用的技巧,那就是使用 grep 命令查找多個字串。 簡單介紹一下,grep 命令可以理解為是一個功能強大的命令列工具,可以用它在一個或多個輸入檔案中搜索與正則運算式相匹配的文本,然后再將每個匹配的文本用標準輸出的格式 ......

    uj5u.com 2020-09-10 00:12:28 more
  • git配置http代理

    git配置http代理 經常遇到克隆 github 慢的問題,這里記錄一下幾種配置 git 代理的方法,解決 clone github 過慢。 目錄 git配置代理 git單獨配置github代理 git配置全域代理 配置終端環境變數 git配置代理 主要使用 git config 命令 git單獨 ......

    uj5u.com 2020-09-10 00:12:33 more
  • Linux npm install 裝包時提示Error EACCES permission denied解

    npm install 裝包時提示Error EACCES permission denied解決辦法 ......

    uj5u.com 2020-09-10 00:12:53 more
  • Centos 7下安裝nginx,使用yum install nginx,提示沒有可用的軟體包

    Centos 7下安裝nginx,使用yum install nginx,提示沒有可用的軟體包。 18 (flaskApi) [root@67 flaskDemo]# yum -y install nginx 19 已加載插件:fastestmirror, langpacks 20 Loading ......

    uj5u.com 2020-09-10 00:13:13 more
  • Linux查看服務器暴力破解ssh IP

    在公網的服務器上經常遇到別人爆破你服務器的22埠,用來挖礦或者干其他嘿嘿嘿的事情~ 這種情況下正確的做法是: 修改默認ssh的22埠 使用設定密鑰登錄或者白名單ip登錄 建議服務器密碼為復雜密碼 創建普通用戶登錄服務器(root權限過大) 建立堡壘機,實作統一管理服務器 統計爆破IP [root ......

    uj5u.com 2020-09-10 00:13:17 more
  • CentOS 7系統常見快捷鍵操作方式

    Linux系統中一些常見的快捷方式,可有效提高操作效率,在某些時刻也能避免操作失誤帶來的問題。 ......

    uj5u.com 2020-09-10 00:13:31 more
  • CentOS 7作業系統目錄結構介紹

    作業系統存在著大量的資料檔案資訊,相應檔案資訊會存在于系統相應目錄中,為了更好的管理資料資訊,會將系統進行一些目錄規劃,不同目錄存放不同的資源。 ......

    uj5u.com 2020-09-10 00:13:35 more
最新发布
  • vim的常用命令

    Vim的6種基本模式 1. 普通模式在普通模式中,用的編輯器命令,比如移動游標,洗掉文本等等。這也是Vim啟動后的默認模式。這正好和許多新用戶期待的操作方式相反(大多數編輯器默認模式為插入模式)。 2. 插入模式在這個模式中,大多數按鍵都會向文本緩沖中插入文本。大多數新用戶希望文本編輯器編輯程序中一 ......

    uj5u.com 2023-04-20 08:43:21 more
  • vim的常用命令

    Vim的6種基本模式 1. 普通模式在普通模式中,用的編輯器命令,比如移動游標,洗掉文本等等。這也是Vim啟動后的默認模式。這正好和許多新用戶期待的操作方式相反(大多數編輯器默認模式為插入模式)。 2. 插入模式在這個模式中,大多數按鍵都會向文本緩沖中插入文本。大多數新用戶希望文本編輯器編輯程序中一 ......

    uj5u.com 2023-04-20 08:42:36 more
  • docker學習

    ###Docker概述 真實專案部署環境可能非常復雜,傳統發布專案一個只需要一個jar包,運行環境需要單獨部署。而通過Docker可將jar包和相關環境(如jdk,redis,Hadoop...)等打包到docker鏡像里,將鏡像發布到Docker倉庫,部署時下載發布的鏡像,直接運行發布的鏡像即可。 ......

    uj5u.com 2023-04-19 09:26:53 more
  • 設定Windows主機的瀏覽器為wls2的默認瀏覽器

    這里以Chrome為例。 1. 準備作業 wsl是可以使用Windows主機上安裝的exe程式,出于安全考慮,默認情況下改功能是無法使用。要使用的話,終端需要以管理員權限啟動。 我這里以Windows Terminal為例,介紹如何默認使用管理員權限打開終端,具體操作如下圖所示: 2. 操作 wsl ......

    uj5u.com 2023-04-19 09:25:49 more
  • docker學習

    ###Docker概述 真實專案部署環境可能非常復雜,傳統發布專案一個只需要一個jar包,運行環境需要單獨部署。而通過Docker可將jar包和相關環境(如jdk,redis,Hadoop...)等打包到docker鏡像里,將鏡像發布到Docker倉庫,部署時下載發布的鏡像,直接運行發布的鏡像即可。 ......

    uj5u.com 2023-04-19 09:19:04 more
  • Linux學習筆記

    IP地址和主機名 IP地址 ifconfig可以用來查詢本機的IP地址,如果不能使用,可以通過install net-tools安裝。 Centos系統下ens33表示主網卡;inet后表示IP地址;lo表示本地回環網卡; 127.0.0.1表示代指本機;0.0.0.0可以用于代指本機,同時在放行設 ......

    uj5u.com 2023-04-18 06:52:01 more
  • 解決linux系統的kdump服務無法啟動的問題

    問題:專案麒麟系統服務器的kdump服務無法啟動,沒有相關日志無法定位問題。 1、查看服務狀態是關閉的,重啟系統也無法啟動 systemctl status kdump 2、修改grub引數,修改“crashkernel”為“512M(有的機器數值太大太小都會導致報錯,建議從128M開始試,或者加個 ......

    uj5u.com 2023-04-12 09:59:50 more
  • 解決linux系統的kdump服務無法啟動的問題

    問題:專案麒麟系統服務器的kdump服務無法啟動,沒有相關日志無法定位問題。 1、查看服務狀態是關閉的,重啟系統也無法啟動 systemctl status kdump 2、修改grub引數,修改“crashkernel”為“512M(有的機器數值太大太小都會導致報錯,建議從128M開始試,或者加個 ......

    uj5u.com 2023-04-12 09:59:01 more
  • 你是不是暴露了?

    作者:袁首京 原創文章,轉載時請保留此宣告,并給出原文連接。 如果您是計算機相關從業人員,那么應該經歷不止一次網路安全專項檢查了,你肯定是收到過資訊系統技術檢測報告,要求你加強風險監測,確保你提供的系統服務堅實可靠了。 沒檢測到問題還好,檢測到問題的話,有些處理起來還是挺麻煩的,尤其是線上正在運行的 ......

    uj5u.com 2023-04-05 16:52:56 more
  • 細節拉滿,80 張圖帶你一步一步推演 slab 記憶體池的設計與實作

    1. 前文回顧 在之前的幾篇記憶體管理系列文章中,筆者帶大家從宏觀角度完整地梳理了一遍 Linux 記憶體分配的整個鏈路,本文的主題依然是記憶體分配,這一次我們會從微觀的角度來探秘一下 Linux 內核中用于零散小記憶體塊分配的記憶體池 —— slab 分配器。 在本小節中,筆者還是按照以往的風格先帶大家簡單 ......

    uj5u.com 2023-04-05 16:44:11 more