顯示具有 C 標籤的文章。 顯示所有文章
顯示具有 C 標籤的文章。 顯示所有文章

2021年12月20日 星期一

程式優化寫法

 

  1. unsigned/signed
    一般情況下,在使用signed類型時compiler會考慮其為正數和負數的情況,因此在不會出現負數的環境中,使用unsigned會避免負數的開銷,從而節省ROM。


  2. 資料形態選擇適當範圍的資料類型,助於產生更精簡的指令。


  3. 浮點常數浮點常數默認為double類型,如果計算所需要的精度不需太高,可以將它強制轉為float,如:(float)3.14


    編譯器不會給浮點運算常量折叠,所以,如果有兩個浮點常數需要計算,可以把結果先算出來:


  4. const陣列把const陣列定義成全域會比局域的節省RAM:


  5. 算術運算
    位與運算(&)代替取模運算(%)一般情況下,“&”操作比“%”操作簡單,在某些特殊情況下,如果可以使用“&”代替“%”,則會降低指令的開銷。


  6. 程式結構
    switch語句代替if else語句當程式中存在多層嵌套的if  else語句,如果多重判斷可轉為switch語句,可以試著轉為switch語句,以便節省ROM
  7. 重複多次的運算可以用迴圈代替當程式中存在多次重複的運算,而且都有規律性,可以用迴圈代替。



  8. 使用預處理命令維護原始程式碼有時候為了維護的方便,希望同一份源碼滿足不同環境的要求,如果用硬編碼的方式,可能會消耗很多的ROM,此時若利用條件編譯的特性,就能輕鬆的實現:



  9. 函式呼叫
    避免不必要的函式呼叫若某函式被多次調用,但其返回值並無差異,應考慮用變數接收函式返回值,使用時做為替代。



  10. 封裝頻繁使用的代碼為函式如果在程式中存在某段代碼被多次使用,在可能的情況下,應將這段代碼封裝起來獨立成為函式,以便節省指令



  11. 如果函式只在本文檔調用,可以定義成static



  12. 全局變數的分配對無擴展指令架構的MCU,RAM  BANK0以外的地址只能用間接定址的方式訪問,下例可以看出,間接定址的指令比直接定址多出5條,所以,當RAM bank0溢出時,用戶可以選擇把較少用到的變數定義到其它bank,比較常用的變數定義在bank0。



  13. 中斷服務程式一般,如果兩個函式沒有調用關係,那它們的局域變數是可以分配到同樣的地址,但中斷服務程式不會與主函式共用局域變數的地址,所以,為了減少RAM的使用,中斷程式可以儘量簡單,不宜寫得太過複雜。


資料來源:https://www.holtek.com.tw/documents/10179/178484/Holtek_C_compiler_V3_Users_Guide(950).pdf

2021年8月13日 星期五

MISRA--作為工業標準的C編程規範(1)

 MISRA (The Motor Industry Software Reliability Association汽車工業軟件可靠性聯會)是位於英國的一個跨國汽車工業協會,其成員包括了大部分歐美汽車生產商。

其核心使命是為汽車工業提供服務和協助,幫助廠方開發安全的、高可靠性的嵌入式軟件。
這個組織最出名的成果是所謂的MISRA C Coding Standard,這一標準中包括了127條C語言編碼標準,通常認為,如果能夠完全遵守這些標準,則你的C代碼是易讀、可靠、可移植和易於維護的。
最近很多嵌入式開發者都以MISRA C來衡量自己的編碼風格,比如著名的uC/OS-II就得意地宣稱自己99%遵守MISRA標準。
而《嵌入式開發雜誌》也專門載文號召大家學習。編碼規範通常是一個公司自定的“土政策”,居然有人去做標準,而且還得到廣泛的認可,這不禁引起我強烈的興趣。
可惜這份標準的文本需要花錢去買,而且短短幾十頁,要價非常昂貴。 MISRA在網上公佈了一些文檔,其中有關於MISRA C Coding Standard的Clarification報告,從中間你可以大致猜到MISRA標準本身是什麼。
我仔細閱讀了這些文檔,並且通過閱讀其他一些介紹性文檔,大致了解了MISRA標準的主要內容。這些條款確有過人之處,對於C/C++語言工程項目的代碼質量管理能夠起到良好的指導性作用,對於大部分軟件開發企業來說,在MISRA的基礎上適當修改就可以形成自己的規範。
當然其中也有一些過於嚴苛的東西,這就需要各個開發部門靈活處理了。我個人的體會,編碼規範雖然很簡單,但是要完全執行,不折不扣,需要開發部門有很高的組織性和紀律性,並且有很好的代碼評審機制。因此,如果能夠嚴格地遵守編碼規範,本身就是一個開發部門實力的證明。

這裡不可能將所有規則一一列出(事實上正式文本我一條也沒看到),只列出一些比較有意思的條款,讓大家有機會了解MISRA的風格。
具體的內容,感興趣的朋友可以自己到www.misra.org.uk去了解。

Rule 1.嚴格遵循ANSI C89標準,不允許任何擴展。


Rule 3.如果要嵌入彙編語言,則必須將所有彙編語句包裝在C函數里,而且這些函數中只有彙編語句,沒有常規C語句。

    Rule 7.不得使用三元操作符(? : )

Rule 10.不得殘留被註釋掉的廢代碼。

Rule 11.所有標識符不超過31字符。

Rule 12.不同名空間中的變數名不得相同。
        例如:
             typedef struct MyStruct {... } MyStruct; (違規)

             struct Person {
               char* name;
               ...
             };

             char name[32]; (違規)

Rule 13.不得使用char, int, float, double, long等基本類型,應該用自己定義的類型顯示表示類型的大小,如CHAR8, UCHAR8, INT16, INT32, FLOAT32, LONG64, ULONG64等。
Rule 14.不得使用類型char,必須顯示聲明為unsigned char或者signed char。

Rule 18.所有數字常數應當加上合適的後綴表示類型,例如51L, 42U, 34.12F等。

Rule 19.禁止使用八進制數。 (因為086U這樣的常數很容易引起誤解)。

Rule 21.不得定義與外部作用域中某個標識符同名的對象,以避免遮蓋外部作用域中的標識符。

Rule 23.具有文件作用域的對象盡量聲名為static的。

Rule 24.在同一個編譯單元中,同一個標識符不應該同時具有內部鏈接和外部鏈接的聲名。


        這裡我略作說明:

                我們通常將一些放在頭文件裡的變數聲名為“外部鏈接”的,如:
         extern UINT32 g_count; //俗話叫變數聲明(對應於變數定義,不分配實際空間)

        對於“使用”這個變數的.c文件來說,這很好,因為g_count始終保持外部鏈接性質。可是對於定義g_count(實際分配空間)的.c文件來說,如果包含了上述的頭文件,則在這個編譯單元里就發生了內部鏈接和外部鏈接的衝突。
 解決辦法是,定義g_count的文件盡量不要包含聲名g_count的頭文件。個人感覺這不是任何時候都做得到的,尤其是在對付遺留代碼的時候。

Rule 25.具有外部鏈接性質的標識符應該只聲明一次。

Rule 27.外部對像不得在多個文件中聲名。

Rule 28.禁止使用register關鍵字。

Rule 29.自動對象(棧對象)使用前必須賦初值。

Rule 33.操作符&&和||的右側表達式不得具有副作用(side-effect)。
        也就是說,象if (x == 20 && ++y == 19)這樣的表達式被禁止。

Rule 35.在返回布林值的表達式中不得出現賦值操作。
        也就是說,我們常用的if (!(fp = fopen("fname", "r"))) { /* error */ }
        被禁止。

Rule 37.不得對有符號數施加位操作,例如1 << 4將被禁止,必須寫1UL << 4;

Rule 39.不得對有符號表達式施加一元"-"操作符。

Rule 40.不得對有副作用的表達式施加sizeof操作符。

Rule 42.除了循環控制語句,不得使用逗號表達式。

Rule 44.禁止冗餘的顯式轉型。比如: double pi = (double) 3.1416F;

Rule 45.禁止從任意類型到指標的強制轉型,禁止從指標到任意類型的強制轉型。
        例如:void* p = (void*)0xFFFF8888UL;

Rule 49.顯示測試值是否為零。

Rule 50.不得顯式判斷浮點數的相等性和不等性。

Rule 52.不得遺留“永遠不會用到”的代碼。

Rule 53.所有非空語句必須具有副作用。

Rule 55.除了switch語句,不得使用標號(label)。

Rule 56.不得使用goto.

Rule 57.不得使用continue。

Rule 58.除了switch語句,不得使用break.

Rule 59. if, else if, else, while, do..while, for語句塊必須使用{}括起。
Rule 60.任何if..else if語句,最後必須有一個收尾的else。例如:
         if (ans == 'Y') {
           ...
         }
         else if (ans == 'N') {
           ...
         }
         else if (ans == 'C') {
           ...
         }
         else {
           ;
         }

Rule 67.循環計數器的值不得在循環體內修改。

Rule 70.禁止任何直接和間接的遞歸函數呼叫。

Rule 82.每個函數只能有一個推出點。

Rule 86.如果一個函數可能返回錯誤信息,則呼叫後必須加以測試。

Rule 92.不應該使用#undef

Rule 95.不得將巨集作為參數傳給巨集函數

Rule 98.在一個巨集定義中,#或##符號只能出現一次。

Rule 101.禁止指標運算(代之以陣列下標運算)。

Rule 102.禁止超過兩級的指標。

Rule 104.禁止使用指向函數的非常量指標。

Rule 106.不得將棧對象的地址傳給外部作用域的對象。

************************************************** ******************
後面的規則針對實時嵌入式系統,對其他類型的開發未必適用,如:

Rule 118.禁止使用動態記憶體分配(也就是不得使用malloc, calloc和realloc)。

Rule 119.禁止使用errno。

Rule 120.禁止使用offsetof.

Rule 121.禁止使用<locale.h>

Rule 122.禁止使用setjmp, longjmp.

Rule 123.禁止使用<signal.h>

Rule 124.禁止使用<stdio.h>(不能用printf, scanf了!)

Rule 125.禁止使用atoi, atof, atol。 (這個我很贊成,建議使用strtol, strtod等函數)

Rule 126.禁止使用abort, exit, getenv。

Rule 127.禁止使用<time.h>

資料來源:https://b8807053.pixnet.net/blog/post/3612211

2020年10月23日 星期五

各種程式語言 Hello World!

Hello World!是每一種程式語言問世都會說的一句問候世界的話. 依據TIOBE的統計, 2013年10月份前20大廣受使用的程式語言如下:

就讓Raspberry Pi來以不同的程式語言說說Hello World!

  1. C語言:
    C語言的編譯器gcc原本就已經安裝了, 查看gcc的版本:

    pi@raspberrypi ~ $ gcc -v
    Using built-in specs.
    COLLECT_GCC=gcc
    COLLECT_LTO_WRAPPER=/usr/lib/gcc/arm-linux-gnueabihf/4.6/lto-wrapper
    Target: arm-linux-gnueabihf
    Configured with: ../src/configure -v --with-pkgversion='Debian 4.6.3-14+rpi1' --with-bugurl=file:///usr/share/doc/gcc-4.6/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.6 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.6 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --disable-sjlj-exceptions --with-arch=armv6 --with-fpu=vfp --with-float=hard --enable-checking=release --build=arm-linux-gnueabihf --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf
    Thread model: posix
    gcc version 4.6.3 (Debian 4.6.3-14+rpi1)

    pi@raspberrypi ~ $ nano helloworld.c
    pi@raspberrypi ~ $ cat helloworld.c
    #include <stdio.h>

    int main() {
    printf("Hello World! \n");
    return 0;
    }
    pi@raspberrypi ~ $ gcc -o helloworld helloworld.c
    pi@raspberrypi ~ $ ./helloworld
    Hello World!

  2. Java:
    Oracle推出ARM版的Java:

    pi@raspberrypi ~/workbench $ sudo apt-get install oracle-java7-jdk
    Reading package lists... Done
    Building dependency tree
    Reading state information... Done
    The following extra packages will be installed:
    libx11-6 libx11-data libxau6 libxcb1 libxdmcp6 libxext6 libxi6 libxrender1 libxtst6 x11-common
    The following NEW packages will be installed:
    libx11-6 libx11-data libxau6 libxcb1 libxdmcp6 libxext6 libxi6 libxrender1 libxtst6 oracle-java7-jdk x11-common
    0 upgraded, 11 newly installed, 0 to remove and 0 not upgraded.
    Need to get 72.0 MB/72.3 MB of archives.
    After this operation, 163 MB of additional disk space will be used.
    Do you want to continue [Y/n]? y

    pi@raspberrypi ~/workbench $ nano helloworld.java
    pi@raspberrypi ~/workbench $ cat helloworld.java
    public class helloworld {

    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
    

    }
    pi@raspberrypi ~/workbench $ javac helloworld.java
    pi@raspberrypi ~/workbench $ java helloworld
    Hello World!

  3. Objective-C
    Objective-C是非常OS X的東西, 主要是SmallTalk對C的擴展, 如過在Linux上要編譯Objective-C是可行, 但似乎不是有很大的意義, 通常需要安裝:

    $ sudo apt-get install gnustep gobjc

在此先不探討編譯Objective-C的做法.

  1. C++
    C++可以用gcc編譯, 也可以用g++編譯. 如果用g++編譯, 如下:

    pi@raspberrypi ~/workbench $ nano helloworld.cpp
    pi@raspberrypi ~/workbench $ cat helloworld.cpp
    #include

    int main()
    {
    std::cout << "Hello, world!\n";
    }
    pi@raspberrypi ~/workbench $ g++ -o helloworld helloworld.cpp
    pi@raspberrypi ~/workbench $ ./helloworld
    Hello, world!

如果是用gcc編譯, 就要指定stdc++的函式庫, 如下所示:

pi@raspberrypi ~/workbench $ gcc -o helloworld -lstdc++  helloworld.cpp 
pi@raspberrypi ~/workbench $ ./helloworld 
Hello, world!
  1. PHP:
    之前已經介紹如何安裝PHP-FPM, 現在要用命令列的方式來執行PHP腳本, 就要安裝php5:

    pi@raspberrypi ~/workbench $ sudo apt-get install php5

安裝好PHP命令列, 查看PHP的版本:

pi@raspberrypi ~/workbench $ php -v
PHP 5.4.4-14+deb7u5 (cli) (built: Oct 13 2013 04:49:27) 
Copyright (c) 1997-2012 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2012 Zend Technologies

執行PHP版本的Hello World!:

pi@raspberrypi ~/workbench $ nano helloworld.php
pi@raspberrypi ~/workbench $ cat helloworld.php
<?php
  printf("Hello, world!
");
?>
pi@raspberrypi ~/workbench $ php helloworld.php 
Hello, world!
  1. C#
    C#是微軟.NET平台的語言, MONO (http://www.mono-project.com/Main\_Page)是一個開放原始碼版本的.NET平台, 可以在Linux系統中運行.NET系統, 相容性至少有90%, 也許更高. 在Raspbian中安裝MONO也很容易:

    pi@raspberrypi ~/workbench $ sudo apt-get install mono-complete

但是要安裝的套件有389MB之多:

Need to get 147 MB/147 MB of archives.
After this operation, 389 MB of additional disk space will be used.
Do you want to continue [Y/n]? y

會安裝這麼多的套件是因為要在Raspberry Pi中執行編譯的工作, 只好把整個MONO系統全部安裝進Raspbian中, 如果只是要執行Visual Studio中編譯好的程式, 只需要安裝runtime就好了:

sudo apt-get install mono-runtime

安裝的過程很久, 但總算是可以編譯C#了, 以下是過程:

pi@raspberrypi ~/workbench $ nano helloworld.cs
pi@raspberrypi ~/workbench $ cat helloworld.cs
using System;

public class HelloWorld
{
    public static void Main()
    {
        Console.WriteLine("Hello World!");
    }
}
pi@raspberrypi ~/workbench $ gmcs helloworld.cs
pi@raspberrypi ~/workbench $ ls -ls helloworld.exe 
4 -rwxr-xr-x 1 pi pi 3072 Oct 21 14:20 helloworld.exe
pi@raspberrypi ~/workbench $ mono helloworld.exe
Hello World!
  1. (Visual) Basic
    由於排在第11位的是Visual Basic .NET, 很顯然地, 這是Visual Basic或是一般的Basic. 在Linux上似乎沒有直接支援Basic, 但是有一個有趣的專案: Gambas (http://gambas.sourceforge.net/en/main.html), 這幾乎是Basic了. 在Debian上的安裝也很容易:

    pi@raspberrypi ~/workbench $ sudo apt-get install gambas3

但是Gambas是一個IDE(集成開發環境, Integrated Development Environment), 需要圖形介面, 因此找不到用命令列的方式來說Hello World!.

  1. Python
    很多人喜歡這個語言, 喜歡它的簡潔, Python是Raspberry Pi上受歡迎的語言, 只是, 它的執行速度不是很快, 目前的有兩個並行版本: 2.7.x和3.x, 目前在我的Raspberry Pi上的是2.7.3版.

    pi@raspberrypi ~/workbench $ python --version
    Python 2.7.3

由於Python是解譯語言, 因此可以直接由Python解譯:

pi@raspberrypi ~/workbench $ nano helloworld.py
pi@raspberrypi ~/workbench $ cat helloworld.py
print "Hello, World!" 
pi@raspberrypi ~/workbench $ python helloworld.py 
Hello, World!
  1. Transact-SQL
    這是SQL語言, 要它說Hello World!好像不切實際, 就跳過囉.

  2. Javascript
    這是網頁語言, 之前已經介紹過, 也安裝了node.js, Javascript天天都和世界說Hello.

11.Visual Basic .NET
這又回到了和C#一樣的MONO平台了. 需要安裝一個Visual Basic.NET的編譯器套件:

pi@raspberrypi ~/workbench $ sudo apt-get install mono-vbnc

安裝好vnbc之後, 就可以向世界說Hello了:

pi@raspberrypi ~/workbench $ nano helloworld.vb
pi@raspberrypi ~/workbench $ vbnc helloworld.vb 
Visual Basic.Net Compiler version 0.0.0.5943
Copyright (C) 2004-2010 Rolf Bjarne Kvinge. All rights reserved.

Assembly 'helloworld, Version=0.0, Culture=neutral, PublicKeyToken=null' saved successfully to '/home/pi/workbench/helloworld.exe'.
Compilation successful
Compilation took 00:00:12.4735250
pi@raspberrypi ~/workbench $ mono helloworld.exe 
Hello world!
  1. Perl
    Perl是一個腳本語言, 也是解譯語言, Perl幾乎存在於每一個Linux中, 對Linux來說, 更是一個重大的語言, 用Perl可以開發大型網站系統, 當然, 要Perl說Hello World!也很容易:
pi@raspberrypi ~/workbench $ nano helloworld.pl
pi@raspberrypi ~/workbench $ cat helloworld.pl
#!/usr/bin/perl
print "Hello World!\n";
pi@raspberrypi ~/workbench $ chmod +x helloworld.pl
pi@raspberrypi ~/workbench $ ./helloworld.pl 
Hello World!

資料來源: https://ithelp.ithome.com.tw/articles/10139347