嵌入式C語言自我修養 02:Linux 內核驅動中的指定初始化

来源:https://www.cnblogs.com/wanglitao2019/archive/2019/03/12/10518533.html
-Advertisement-
Play Games

2.1 什麼是指定初始化 在標準 C 中,當我們定義並初始化一個數組時,常用方法如下: 按照這種固定的順序,我們可以依次給 a[0] 和 a[8] 賦值。因為沒有對 a[9] 賦值,所以編譯器會將 a[9] 預設設置為0。當數組長度比較小時,使用這種方式初始化比較方便。當數組比較大,而且數組裡的非零 ...


2.1 什麼是指定初始化

在標準 C 中,當我們定義並初始化一個數組時,常用方法如下:

1 int a[10] = {0,1,2,3,4,5,6,7,8};


按照這種固定的順序,我們可以依次給 a[0] 和 a[8] 賦值。因為沒有對 a[9] 賦值,所以編譯器會將 a[9] 預設設置為0。當數組長度比較小時,使用這種方式初始化比較方便。當數組比較大,而且數組裡的非零元素並不連續時,這時候再按照固定順序初始化就比較麻煩了。

比如,我們定義一個數組 b[100],其中 b[10]、b[30] 需要初始化,如果還按照前面的固定順序初始化,{}中的初始化數據中間可能要填充大量的0,比較麻煩。

那怎麼辦呢?C99 標準改進了數組的初始化方式,支持指定任意元素初始化,不再按照固定的順序初始化。

int b[100] ={ [10] = 1, [30] = 2};


通過數組索引,我們可以直接給指定的數組元素賦值。除此之外,一個結構體變數的初始化,也可以通過指定某個結構體域直接賦值。

因為 GNU C 支持 C99 標準,所以 GCC 編譯器也支持這一特性。甚至早期不支持 C99,只支持 C89 的 GCC 編譯器版本,這一特性也被當作一個 GCC 編譯器的擴展特性來提供給程式員使用。

 

2.2 指定初始化數組元素

在 GNU C 中,通過數組元素索引,我們就可以給某個指定的元素直接賦值。

int b[100] = { [10] = 1, [30] = 2 };


在{ }中,我們通過 [10] 數組元素索引,就可以直接給 a[10] 賦值了。這裡有個細節註意一下,就是各個賦值之間用逗號 “,” 隔開,而不是使用分號“;”。

如果我們想給數組中某一個索引範圍的數組元素初始化,可以採用下麵的方式。

int main(void)
{
   int b[100] = { [10 ... 30] = 1, [50 ... 60] = 2 };
   for(int i = 0; i < 100; i++)
  {
       printf("%d ", a[i]);
       if( i % 10 == 0)
           printf("\n");
  }
   return 0;   
}


在這個程式中,我們使用 [10 ... 30] 表示一個索引範圍,相當於給 a[10] 到 a[30] 之間的20個數組元素賦值為1。

GNU C 支持使用 ... 表示範圍擴展,這個特性不僅可以使用在數組初始化中,也可以使用在 switch-case 語句中。比如下麵的程式:

#include<stdio.h>
int main(void)
{
   int i = 4;
   switch(i)
  {
       case 1:
           printf("1\n");
           break;
       case 2 ... 8:
           printf("%d\n",i);
           break;
       case 9:
           printf("9\n");
           break;
       default:
           printf("default!\n");
           break;
  }
   return 0;
}


在這個程式中,當 case 值為2到8時,都執行相同的 case 分支,可以通過 case 2 ... 8: 的形式來簡化代碼。這裡同樣也有一個細節需要註意,就是 ... 和其兩端的數據範圍2和8之間也要空格,不能寫成2...8的形式,否則編譯就會通不過。

 

2.3 指定初始化結構體成員變數

跟數組類似,在標準 C 中,結構體變數的初始化也要按照固定的順序。在 GNU C 中我們也可以通過結構域來初始化指定某個成員。

struct student{
   char name[20];
   int age;
};
​
int main(void)
{
   struct student stu1={ "wit",20 };
   printf("%s:%d\n",stu1.name,stu1.age);
​
   struct student stu2=
  {
      .name = "wanglitao",
      .age  = 28
  };
   printf("%s:%d\n",stu2.name,stu2.age);
​
   return 0;
}


在程式中,我們定義一個結構體類型 student,然後分別定義兩個結構體變數 stu1 和 stu2。初始化 stu1 時,我們採用標準 C 的初始化方式,即按照固定順序直接初始化。初始化 stu2 時,我們採用 GNU C 的初始化方式,通過結構功能變數名稱 .name 和 .age,我們就可以給結構體變數的某一個指定成員直接賦值。非常方便。

 

2.4 Linux 內核驅動註冊

在 Linux 內核驅動中,大量使用 GNU C 的這種指定初始化方式,通過結構體成員來初始化結構體變數。比如在字元驅動程式中,我們經常見到這樣的初始化:

static const struct file_operations ab3100_otp_operations = {
.open        = ab3100_otp_open,
.read        = seq_read,
.llseek        = seq_lseek,
.release    = single_release,
};


在驅動程式中,我們經常使用 file_operations 這個結構體變數來註冊我們開發的驅動,然後以回調的方式來執行我們驅動實現的相關功能。結構體 file_operations 在 Linux 內核中的定義如下:

struct file_operations {
       struct module *owner;
       loff_t (*llseek) (struct file *, loff_t, int);
       ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
       ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
       ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
       ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
       int (*iterate) (struct file *, struct dir_context *);
       unsigned int (*poll) (struct file *, struct poll_table_struct *);
       long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
       long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
       int (*mmap) (struct file *, struct vm_area_struct *);
       int (*open) (struct inode *, struct file *);
       int (*flush) (struct file *, fl_owner_t id);
       int (*release) (struct inode *, struct file *);
       int (*fsync) (struct file *, loff_t, loff_t, int datasync);
       int (*aio_fsync) (struct kiocb *, int datasync);
       int (*fasync) (int, struct file *, int);
       int (*lock) (struct file *, int, struct file_lock *);
       ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
       unsigned long (*get_unmapped_area)(struct file *,
              unsigned long, unsigned long, unsigned long, unsigned long);
       int (*check_flags)(int);
       int (*flock) (struct file *, int, struct file_lock *);
       ssize_t (*splice_write)(struct pipe_inode_info *, 
           struct file *, loff_t *, size_t, unsigned int);
       ssize_t (*splice_read)(struct file *, loff_t *, 
           struct pipe_inode_info *, size_t, unsigned int);
       int (*setlease)(struct file *, long, struct file_lock **, void **);
       long (*fallocate)(struct file *file, int mode, loff_t offset,
                 loff_t len);
       void (*show_fdinfo)(struct seq_file *m, struct file *f);
       #ifndef CONFIG_MMU
       unsigned (*mmap_capabilities)(struct file *);
       #endif
  };


結構體 file_operations 裡面定義了很多結構體成員,而在這個驅動中,我們只初始化了部分成員變數,通過訪問結構體的成員來指定初始化,非常方便。

 

2.5 指定初始化的好處

這種指定初始化方式,不僅使用靈活,而且還有一個好處就是:代碼易於維護。尤其是在 Linux 內核這種大型項目中,幾萬個文件,幾千萬的代碼量,當成百上千個文件都使用 file_operations 這個結構體類型來定義變數並初始化時,那麼一個很大的問題就來了:如果採用標準 C 那種按照固定順序賦值,當我們的 file_operations 結構體類型發生改變時,如添加成員、減少成員、調整成員順序,那麼使用該結構體類型定義變數的大量 C 文件都需要重新調整初始化順序,牽一發而動全身,想想這是多麼可怕!

我們通過指定初始化方式,就可以避免這個問題。無論file_operations 結構體類型如何變化,添加成員也好、減少成員也好、調整成員順序也好,都不會影響其它文件的使用。有了指定初始化,再也不用加班修改代碼了,媽媽再也不用擔心我們整日加班,不回家吃飯了,多好!


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

-Advertisement-
Play Games
更多相關文章
  • 一. MVC介紹 MVC架構模式有助於實現關註點分離。視圖和控制器均依賴於模型。 但是,模型既不依賴於視圖,也不依賴於控制器。 這是分離的一個關鍵優勢。 這種分離允許模型獨立於可視化展示進行構建和測試。ASP.NET Core MVC 包括以下功能: 路由、模型綁定、模型驗證、依賴關係註入、篩選器、 ...
  • 通過前面的教程學習,你可以實現一個簡單的書籍管理系統。 在本教程將向書籍索引頁中添加分頁功能。 ...
  • 0X00 前言 Newtonsoft.Json,這是一個開源的Json.Net庫,官方地址:https://www.newtonsoft.com/json ,一個讀寫Json效率非常高的.Net庫,在做開發的時候,很多數據交換都是以json格式傳輸的。而使用Json的時候,開發者很多時候會涉及到幾個 ...
  • 我基本上從0開始學習編程,運算符基本上跳過,因為知道了 “=”這個符號相當於賦值,然後“==”才是等於,其他和普通運算符號差不都,也就跳過了。 最基礎的賦值那種,我看了下代碼,似乎沒什麼難度,估計新手和我一樣,有一本書,大概看看就懂了,我從我遇到的問題開始。 我學習時候,發現C#接收用戶輸入的都是字 ...
  • 一 互聯網應用質量概述1.1 互聯網應用質量互聯網應用質量指標——QoE,其主要指標:服務成功率:指用戶所請求的服務成功完成的幾率。服務建立時間:指從服務請求到服務呈現所花費的時間,並且會因為用戶請求服務內容的不同而表現出微妙到秒級的區別。時延:指用戶從發出請求到獲得結果的時間。視聽播放卡頓:指播放... ...
  • 日常工作中我們往往有需要導出當前共用環境或磁碟文件目錄層級結構等的需求,最早在目錄少的情況下我們使用CMD下tree 命令可以很清晰的看到目錄、文件層級結構,那麼我們又如何通過powershell直觀顯示或導出某文件目錄或盤符目錄層級結構呢?DOS下查看目錄、文件結構:tree /?以圖形顯示驅動器... ...
  • 製作這塊51單片機的還是蠻艱辛的,應該是我水平太差,現在這塊51單片機已經穩定了,也把這塊板子製作過程中的一些問題及經驗總結記錄下來。 這塊板子製作出了很大問題很大原因是因為我對Altium Designer16這個軟體的操作不熟悉,畢竟畫這塊板子的很大一部分原因就是為了熟悉這個軟體。 首先是從原理 ...
  • 1.1 Linux 內核驅動中的奇怪語法 大家在看一些 GNU 開源軟體,或者閱讀 Linux 內核、驅動源碼時會發現,在 Linux 內核源碼中,有大量的 C 程式看起來“怪怪的”。說它是C語言吧,貌似又跟教材中的寫法不太一樣;說它不是 C 語言呢,但是這些程式確確實實是在一個 C 文件中。此時, ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...