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

2018年11月6日 星期二

[轉] weak symbol

我們用nm看動態庫時,會發現有些符號類型是"V",手冊里解釋如下:
"V" The symbol is a weak object.  When a weak defined symbol is linked with a normal  defined  symbol, the normal defined symbol is used with no error. When a weak undefined symbol is linked and the symbol is not defined, the value of the weak symbol becomes zero with no error.
說的是動態庫中的weak symbol,缺省會被normal symbol替代,如果沒有定義,則該symbol的值為0。
很抽象,是不是,我一直想找一個簡單的例子。
            
最近看過一篇文章:
http://www.cs.virginia.edu/~wh5a/blog/2006/07/20/the-weak-attribute-of-gcc/
終於對所謂的weak symbol有了一點了解。
http://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Function-Attributes.html
講了__attribute__的語法。
 
【weak.c】
extern void foo() __attribute__((weak));
int main() {
  if (foo) foo();
}
程序居然能夠編譯通過,甚至成功執行!讓我們來看看是為什麼?
首先聲明了一個符號foo(),屬性為weak,但並不定義它,這樣,鏈接器會將此未定義的weak symbol賦值為0,也就是說foo()並沒有真正被調用,試試看,去掉if條件,肯定core dump!
 
【strong.c】
extern void foo() ;
int main() {
  if (foo) foo();
}
這個是一般程序,編譯過不了:
strong.c: undefined reference to `foo'
再添上一個定義文件:

【foo.c】
void foo() {
  printf("in foo.\n");
}
OK!
 
用nm檢查一下:
linux:~/test/weak # nm weak.o
         w foo
00000000 T main
linux:~/test/weak # nm foo.o
00000000 T foo
         U printf
鏈接時,前面那個weak symbol會被后面這個代替,如果沒有鏈接foo.o,也沒問題,對應符號為0。
 
這就是weak symbol的意義。
 
 
 
#include <stdio.h>
#include <string.h>
/* malloc example */
#include <stdio.h>
#include <string.h>
#include <process.h>
//extern void f();
//void f () __attribute__ ((weak, alias ("__f")));
void f () __attribute__ ((weak, alias ("__f")));
void __f()
{
 printf("it789is test\n");

}

int main(void)
{
 if(f)
  f();
 else
  printf("it is test\n");
 return 0;
}

2018年11月5日 星期一

[轉] #pragma pack (push,1) and #pragma pack(pop)

#pragma pack是用來指定數據在內存中的對齊方式。
#pragma pack (n)             作用:C編譯器將按照n個字節對齊。
#pragma pack ()               作用:取消自定義字節對齊方式。

#pragma  pack (push,1)     作用:是指把原來對齊方式設置壓棧,並設新的對齊方式設置為一個字節對齊
#pragma pack(pop)            作用:恢復對齊狀態
因此可見,加入push和pop可以使對齊恢復到原來狀態,而不是編譯器默認,可以說後者更優,但是很多時候兩者差別不大
如:
#pragma pack(push) //保存對齊狀態
#pragma pack(4)//設定為4字節對齊
  相當於 #pragma  pack (push,4)  

#pragma  pack (1)           作用:調整結構體的邊界對齊,讓其以一個字節對齊;<使結構體按1字節方式對齊>
#pragma  pack ()
例如:
#pragma pack(1)
struct sample
{
char a;
double b;
};
#pragma pack()
註:若不用#pragma pack(1)和#pragma pack()括起來,則sample按編譯器默認方式對齊(成員中size最大的那個)。
即按8字節(double)對齊,則sizeof(sample)==16.成員char a佔了8個字節(其中7個是空字節);
若用#pragma pack(1),則sample按1字節方式對齊sizeof(sample)==9.(無空字節),比較節省空間啦,有些場和還可使結構體更易於控制。
應用實例
在網絡協議編程中,經常會處理不同協議的數據報文。一種方法是通過指針偏移的方法來得到各種信息,但這樣做不僅編程複雜,而且一旦協議有變化,程序修改起來也比較麻煩。在瞭解了編譯器對結構空間的分配原則之後,我們完全可以利用這一特性定義自己的協議結構,通過訪問結構的成員來獲取各種信息。這樣做,不僅簡化了編程,而且即使協議發生變化,我們也只需修改協議結構的定義即可,其它程序無需修改,省時省力。下面以TCP協議首部為例,說明如何定義協議結構。其協議結構定義如下: 

#pragma pack(1) // 按照1字節方式進行對齊struct TCPHEADER 
{
     short SrcPort; 
// 16位源端口號
     short DstPort; 
// 16位目的端口號
     int SerialNo; 
// 32位序列號
     int AckNo; 
// 32位確認號
     unsigned char HaderLen : 4; 
// 4位首部長度
     unsigned char Reserved1 : 4; 
// 保留6位中的4位
     unsigned char Reserved2 : 2; 
// 保留6位中的2位
     unsigned char URG : 1;
     unsigned char ACK : 1;
     unsigned char PSH : 1;
     unsigned char RST : 1;
     unsigned char SYN : 1;
     unsigned char FIN : 1;
     short WindowSize; 
// 16位窗口大小
     short TcpChkSum; 
// 16位TCP檢驗和
     short UrgentPointer; 
// 16位緊急指針
}; 
#pragma pack()

2018年7月30日 星期一

[5j03] Passing Arrays as Function Arguments in C

https://www.tutorialspoint.com/cprogramming/c_passing_arrays_to_functions.htm

If you want to pass a single-dimension array as an argument in a function, you would have to declare a formal parameter in one of following three ways and all three declaration methods produce similar results because each tells the compiler that an integer pointer is going to be received. Similarly, you can pass multi-dimensional arrays as formal parameters.

Way-1

Formal parameters as a pointer −
void myFunction(int *param) {
   .
   .
   .
}

Way-2

Formal parameters as a sized array −
void myFunction(int param[10]) {
   .
   .
   .
}

Way-3

Formal parameters as an unsized array −
void myFunction(int param[]) {
   .
   .
   .
}

Example

Now, consider the following function, which takes an array as an argument along with another argument and based on the passed arguments, it returns the average of the numbers passed through the array as follows −
double getAverage(int arr[], int size) {

   int i;
   double avg;
   double sum = 0;

   for (i = 0; i < size; ++i) {
      sum += arr[i];
   }

   avg = sum / size;

   return avg;
}
Now, let us call the above function as follows −
#include <stdio.h>
 
/* function declaration */
double getAverage(int arr[], int size);

int main () {

   /* an int array with 5 elements */
   int balance[5] = {1000, 2, 3, 17, 50};
   double avg;

   /* pass pointer to the array as an argument */
   avg = getAverage( balance, 5 ) ;
 
   /* output the returned value */
   printf( "Average value is: %f ", avg );
    
   return 0;
}
When the above code is compiled together and executed, it produces the following result −
Average value is: 214.400000
As you can see, the length of the array doesn't matter as far as the function is concerned because C performs no bounds checking for formal parameters.

2018年4月26日 星期四

[轉] Strong Symbol and Weak Symbol(強型別和弱型別)

http://swaywang.blogspot.tw/2011/10/strong-symbol-and-weak-symbol.html


Strong Symbol and Weak Symbol(強型別和弱型別)

Compiler會把已經初始化的Global varible當作Strong Symbol
未初始化的Global varible為Weak Symbol
我們可以使用GCC提供的 "__attribute__((weak))"
來定義任意一個Strong Symbol為Weak Symbol
以下是一個例子:
extern int ext;

int weak;
int strong = 1;
__attribute__((weak)) weak2 = 2;

int main(){
    return 0;
}

在這個例子中weak和weak2都是Weak Symbol
strong和main是Strong Symbol
ext不是Strong也不是Weak,因為他是一個外部變數

Compiler會按照下列規則處理Strong以及Weak Symbol

Rule1: Strong Symbol不能在不同的Obj檔被多次定義
       這就是我們常常看到的重複定義錯誤

Rule2: 如果一個Symbol在某個檔案是Strong,在其他檔案都是Weak
       Compiler會選擇Strong Symbol

Rule3: 如果一個Symbol在每個檔案都是Weak,會選擇最大的Type
       比如說一個同樣名稱的int和double global varible
       Compiler在Link的時候會選擇double

外部符號的Reference也有分兩種

Strong Reference:  在Link時找不到符號定義會回報錯誤
Weak Reference:   在Link時找不到符號定義不會回報錯誤,通常會預設為0

下面是GCC把foo()宣告成weak reference的擴充keyword
__attribute__((weakref)) void foo();

int main()
{
    foo();
}
上面這段code可以編譯成執行檔且不會產生錯誤

但是我們執行程式的話,因為沒有定義foo(),foo的位置為0
因此會發生不合法的位置存取錯誤

Weak Symbol和 Weak Reference對函式庫的設計非常有用
函式庫可以定義一些Weak Symbol的函式
使用者可以自己定義一些Strong Symbol達到擴充功能
因為Strong Symbol會蓋掉Weak Symobl

我們也可以透過Weak Reference使用一些擴充功能
以後就算我們把擴充功能去掉,程式還是可以正常Link

2017年12月11日 星期一

[轉] [C/C++] 靜態函式 (static function) 2011

static function is a function whose scope is limited to the current source file.  Scope refers to the visibility of a function or variable. If the function or variable is visible outside of the current source file, it is said to have global, or externalscope. If the function or variable is not visible outside of the current source file, it is said to have local, or static scope.
意思是說靜態函式只能被該檔案所看見,其它檔案無法得知該檔案是否有其靜態函式。
有一些 C 的語法,在 C++ 的程序員相對少用。但就是因為這個原因,有時就會忽略了。
假設我們有一個Header檔 Foo.h
static void f1(){
cout << “f1()" << endl;
}
void f2(){
cout << “f2()" << endl;
}
f1 和 f2 的差別在於 static 這個 keyword 。在這裡的 f1 被宣告和定義為 static ,是指它只在這個 Compilation Unit 中生效。而 f2 沒有被定義為 static ,亦即是它可以被其他 Compilation Unit 訪問。
但到底什麼是 Compilation Unit 呢?首先,一個程式編譯過程如下:
compliation_process這裡的 a.cpp , b.cpp 和 c.cpp 也 引入了 Foo.h 這個檔案,經過前處理器後,Foo.h 的內容會被加入到 a.cpp, b.cpp 和 c.cpp 中,再經過編譯器,變成為 a.o , b.o 和 c.o 這些目的碼,最後經過連結器,變成執行檔
要留意的地方是,每個經過前處理器處理後的.cpp 檔,和它的目的檔是一一對應的,而Compilation Unit,就是這些被處理後的.cpp 檔了。
若以Foo.h 的 f1 為例子,雖然在每個 .cpp 檔也被定義了,但經過編譯後,所有的f1 也會被隱藏在自己的目的檔中,連結器在找尋symbol的過程中,是會忽略的。
但f2 就不同了,所有的f2 在目的檔中,也是不會被隱藏,所以在連結器找尋symbol,會找到多份的f2,那連結就會有錯誤了。
所以在大部份的情況下,在Header檔中定義函數,也是需要 static 這個 keyword 的。就算加上了 inline,情況也是一樣的。
static inline void f3(){
cout << “f3()" << endl;
}

2017年11月30日 星期四

[轉] (assertion)敘述

維護(assertion)敘述

        當自己寫的函式庫要提供他人使用時,適當的利用維護敘述,可以建立安全的使用
        介面,避免他人因為使用不當,而造成不可預期的後果。
        C 語言有自己的維護函式- assert() ,使用方法如下:

           assert(iTotalNumber < 1000);

        當程式執行到該行時,若 iTotalNumber < 1000 則程式可以繼續執行;若
        iTotalNumber >= 1000 ,則會秀出維護錯誤訊息的字串,並結束程式。
        維護字串包含有:判斷式子、程式檔名及該行的行號。

        // Test.CPP -- test assert message
        #include 
        main()
        {
           int iTotalNumber=10000;
           assert(iTotalNumber<1000);
        }

        C:LCPPBIN>tt
        Assertion failed: iTotalNumber<1000, file test.cpp, line 6
        Abnormal program termination

2016年10月19日 星期三

function pointer 用法

 http://ccckmit.wikidot.com/cp:function





=========================================================================

當你在宣告一個變數時是這樣的:
int ImVar;//<-----------------------1
當你在宣告一個函式時卻是這樣:
int ImFun(...);//---------------------2
變數宣告時名稱在最後面,而函式名稱卻在中間,
你會不會覺得這很奇怪?
本來用一個小括號括起來的參數定義就是函式名稱的附屬品
你可以當它是函式名稱的一部份。沒有了它函式名稱就不完整了。
(注意在C++中不同參數的同名函式在編譯器的內部函式名稱是不同的)

typedef int INT;//<------------------3
typedef int *PINT;//<--------------4
typedef int (*PINT);//<--------------5
3式是定義一個int的型態,名為INT
4式是定義一個int的指標型態,名為PINT
5式是定義一個指向int的指標型態,名為PINT
4式和5式的結果是等效的。

現在我們嘗試為函式定義型態:
typedef int IntFun(...);//<------------6
先注意到有關2式的說明,就不應再對為何函式名稱後還有(...)
6式定義一個型態(或返回)int函式,名稱為IntFun。
我們知道,函式名本身俱有隱性指標的性質,所以IntFun和 *IntFun是
等效的。
那麼可以明白的定義IntFun為指標嗎,應該可以的!直觀的感覺是套入
4式:
typedef int * IntFun(...);//<------------7
問題出來了,任何一個編譯器都會把7式解讀為:
定義一個型態(或返回)int *函式,名稱為IntFun。
這不是我們要的,那要如何指定指標('*')給IntFun而不是int呢?
答案就是括號,也就是套入5式而不是4式:
typedef int (*IntFun)(...);//<------------8
這就是原提問要的解答了,唯要注意的是
對型態的定義來說 4式和5式是等效的,
但對函式的定義來說6式和8式才是等效的;
那麼使用6式或8式效好?
一般都使用8弍,它有較好的可讀性,隱式指標總是令人較為困感的。
而且也不敢保證所有的編譯器都可以接受6式的敘述。





==========================================================================

Function Pointer:(指向函數的指標)

int (*pf)(int);

在指標的藝術一書(p.87)中又名Pointer to function。目的是經過宣告後,會有一個指標是指向函數的起始位址,當我們要使用某個函數的時候,只要將此指標指向那個函數,就可使用,其實函數名稱是一個位址,因此只要將函數名稱設定給此指標即可。

使用函數指標的時候必須注意指標的引入與回傳參數是否與原函式匹配,就說原函式引入、回傳的資料型態,引入參數個數,通通要一樣才可以。

例如三個副函式:

(1) int add(int);
(2) float add2(float);
(3) int add3(int,int);

並且宣告一個函式指標:
int (*pf)(int);

則:

(a.) pf = add;     //正確
(b.) pf = add2;   //錯誤,參數資料型態不匹配
(c.) pf = add3;   //錯誤,引入參數個數不匹配

#################################################################################

Function return a pointer:(回傳指標的函數)

int *pf(int); //少了一個括號cf: int (*pf)(int);

表示pf是一個函數,這個函數會引入一個整數型態的參數,經過函數運算後會回傳一個整數型態的指標(回傳的東西是一個位址,假設回傳x,則x可以是指標(x存的內容是指向的位址),也可以是變數的位址,若x是一般變數,則回傳&x;若x是陣列則回傳x。

#################################################################################

typedef與函數指標(from wiki typedef)

Using typedef with function pointers[edit source | editbeta]

Function pointers are somewhat different than all other types because the syntax does not follow the pattern typedef <old type name> <new alias>;. Instead, the new alias for the type appears in the middle between the return type (on the left) and the argument types (on the right). Consider the following code, which does not use a typedef:
int do_math(float arg1, int arg2) {
    return arg2;
}
 
int call_a_func(int (*call_this)(float, int)) {
    int output = call_this(5.5, 7);
    return output;
}
 
int final_result = call_a_func(do_math);
This code can be rewritten with a typedef as follows:
typedef int (*MathFunc)(float, int);
 
int do_math(float arg1, int arg2) {
    return arg2;
}
 
int call_a_func(MathFunc call_this) {
    int output = call_this(5.5, 7);
    return output;
}
 
int final_result = call_a_func(do_math);
Here, MathFunc is the new alias for the type. A MathFunc is a pointer to a function that returns an integer and takes as arguments a float followed by an integer.

typedef int (*MathFunc)(float, int);是別名的宣告,就只是在最前面加上typedef則以後就可以只用MathFunc這樣的名稱來宣告同類型的函數指標。