預處理、結構體預習

1. 帶參數的宏與自定義函數的區別

  1.函數調用時,先求出實參表達式的值,然後帶入形參。而使用帶參的宏只是進行簡單的字符替換。
  2.函數調用是在程序運行時處理的,分配臨時的內存單元;而宏展開則是在編譯時進行的,在展開時並不分配內存單元,不進行值的傳遞處理,也沒有“返回值”的概念。
  3.對函數中的實參和形參都要定義類型,二者的類型要求一致,如不一致,應進行類型轉換;而宏不存在類型問題,宏名無類型,它的參數也無類型,只是一個符號代表,展開時帶入指定的字符即可。宏定義時,字符串可以是任何類型的數據。
  4.調用函數只可得到一個返回值,而用宏可以設法得到幾個結果。
  5.使用宏次數多時,宏展開後源程序長,因爲每展開一次都使程序增長,而函數調用不使源程序變長。
  6.宏替換不佔運行時間,只佔編譯時間;而函數調用則佔運行時間(分配單元、保留現場、值傳遞、返回)。
一般來說,用宏來代表簡短的表達式比較合適。
有時使用宏時會引起理解錯誤:
例:
#i nclude<iostream.h>
#define max(a,b) ((a>b)?a:b)
void main()
{
 int i=3,j=2;
 cout<<max(++i,j)<<endl;
 cout<<i<<"  "<<j<<endl;
}
運行結果:
5


2.文件包含中的文件用“ ”與用< >的區別

例如:

#include “book.h”
#include<iostream.h>
在剛開始學習都會有這種迷惑,有的程序用<>,有的卻用"",那麼二者到底什麼區別呢,什麼情況下使用呢?
<>和""表示編譯器在搜索頭文件時的順序不同,<>表示從系統目錄下開始搜索,然後再搜索PATH環境變量所列出的目錄,不搜索當前目錄,""是表示從當前目錄開始搜索,然後是系統目錄和PATH環境變量所列出的目錄。
所以,系統頭文件一般用<>,用戶自己定義的則可以使用"",加快搜索速度。


3.條件編譯有哪幾種形式以及使用的規則是什麼

一般情況下,源代碼文件中的所有行都參加編譯,但有時需要指定一部分代碼在某個條件下才被編譯,這就叫做條件編譯。
條件編譯發生在預處理階段,在C中,主要通過#if、#elif、#else、#ifdef、#ifndef、#endif來給一段代碼附加上編譯條件,然後預處理器收集滿足條件的可以進行編譯的代碼,這樣經過預處理,不滿足條件的代碼就不會被編譯。


再來說一下上面幾個預處理指令的用法:

#if 條件1
    語句塊1
#elif 條件2
    語句塊2
#else 
    語句塊3
#endif
可以看到,它和普通的條件結構語句很相似,就是滿足條件1就編譯語句塊1,否則如果滿足條件2就編譯語句塊2,否則編譯語句塊3。最後必須用#endif結尾。


#ifdef和#ifndef用法是:

#ifdef 符號
    語句塊
#endif
 
#ifndef 符號
    語句塊
#endif
ifdef意思是如果定義了指定的符號就編譯下面的語句塊;ifndef相反,如果沒定義指定的符號,就編譯下面的語句塊。
    語句塊中也可以加預處理指令,這些預處理指令和其他語句一樣,只有當滿足條件時,纔會被處理。
    另外,ifndef常用於防止一個頭文件的重複引用。


4.結構體與數組的比較

  1.都是由多個元素組成的

  2.各個元素在內存中的存儲空間是連續的

  3.數組中各個元素的數據類型相同,而結構體中的各個元素的數據類型可以不相同


5.有哪些引用結構體變量成員的方法

  1.結構體變量名.成員名 如 stu1.name

  2.結構體指針變量—>成員名, 如 ps->name

  3.(*結構體指針變量).成員名 如 (*ps).name

  4.結構體變量數組名.成員名 如 stu[10].name


6.內存對齊的正式原則有哪些

內存對齊,正是因爲內存對齊的影響,導致結果不同。
對於大多數的程序員來說,內存對齊基本上是透明的,這是編譯器該乾的活,編譯器爲程序中的每個數據單元安排在合適的位置上,從而導致了相同的變量,不同聲明順序的結構體大小的不同。
 
       那麼編譯器爲什麼要進行內存對齊呢?程序1中結構體按常理來理解sizeof(st1)和sizeof(st2)結果都應該是7,4(int) + 2(short) + 1(char) = 7 。經過內存對齊後,結構體的空間反而增大了。
在解釋內存對齊的作用前,先來看下內存對齊的規則:
1、  對於結構的各個成員,第一個成員位於偏移爲0的位置,以後每個數據成員的偏移量必須是min(#pragma pack()指定的數,這個數據成員的自身長度) 的倍數。
2、  在數據成員完成各自對齊之後,結構(或聯合)本身也要進行對齊,對齊將按照#pragma pack指定的數值和結構(或聯合)最大數據成員長度中,比較小的那個進行。
 
#pragma pack(n) 表示設置爲n字節對齊。 VC6默認8字節對齊
以程序1爲例解釋對齊的規則 :
St1 :char佔一個字節,起始偏移爲0 ,int 佔4個字節,min(#pragma pack()指定的數,這個數據成員的自身長度) = 4(VC6默認8字節對齊),所以int按4字節對齊,起始偏移必須爲4的倍數,所以起始偏移爲4,在char後編譯器會添加3個字節的額外字節,不存放任意數據。short佔2個字節,按2字節對齊,起始偏移爲8,正好是2的倍數,無須添加額外字節。到此規則1的數據成員對齊結束,此時的內存狀態爲:
oxxx|oooo|oo


0123 4567 89 (地址)
(x表示額外添加的字節)
共佔10個字節。還要繼續進行結構本身的對齊,對齊將按照#pragma pack指定的數值和結構(或聯合)最大數據成員長度中,比較小的那個進行,st1結構中最大數據成員長度爲int,佔4字節,而默認的#pragma pack 指定的值爲8,所以結果本身按照4字節對齊,結構總大小必須爲4的倍數,需添加2個額外字節使結構的總大小爲12 。此時的內存狀態爲:
oxxx|oooo|ooxx
0123 4567 89ab  (地址)
到此內存對齊結束。St1佔用了12個字節而非7個字節。
 
St2 的對齊方法和st1相同,讀者可自己完成。
 
內存對齊的主要作用是:
1、  平臺原因(移植原因):不是所有的硬件平臺都能訪問任意地址上的任意數據的;某些硬件平臺只能在某些地址處取某些特定類型的數據,否則拋出硬件異常。
2、  性能原因:經過內存對齊後,CPU的內存訪問速度大大提升。


7.define與tpyedef的區別

typedef只是爲了增加可讀性而爲標識符另起的新名稱(僅僅只是個別名),而#define原本在C中是爲了定義常量
,到了C++,const、enum、inline的出現使它也漸漸成爲了起別名的工具。有時很容易搞不清楚與typedef兩者到底該用哪個好,如#define
INT int這樣的語句,用typedef一樣可以完成,用哪個好呢?我主張用typedef,因爲在早期的許多C編譯器中這條語句是非法的,只是現今的
編譯器又做了擴充。爲了儘可能地兼容,一般都遵循#define定義“可讀”的常量以及一些宏語句的任務,而typedef則常用來定義關鍵字、冗
長的類型的別名。
宏定義只是簡單的字符串代換(原地擴展),而typedef則不是原地擴展,它的新名字具有一定的封裝性,以致於新命名的標識符具有更易定義變
量的功能。請看上面第一大點代碼的第三行:
typedef    (int*)      pINT;
以及下面這行:
#define    pINT2    int*
效果相同?實則不同!實踐中見差別:pINT a,b;的效果同int *a; int *b;表示定義了兩個整型指針變量。而pINT2 a,b;的效果同int *a, b;
表示定義了一個整型指針變量a和整型變量b。


8.結構體與聯合體的主要區別

構造數據類型,也叫聯合體
用途:使幾個不同類型的變量共佔一段內存(相互覆蓋)


結構體是一種構造數據類型
用途:把不同類型的數據組合成一個整體-------自定義數據類型


結構體變量所佔內存長度是各成員佔的內存長度的總和。
共同體變量所佔內存長度是各最長的成員佔的內存長度。
共同體每次只能存放哪個的一種!!
共同體變量中起作用的成員是尊後一次存放的成員,
在存入新的成員後原有的成員失去了作用!


Structure 與 Union主要有以下區別:
1. struct和union都是由多個不同的數據類型成員組成, 但在任何同一時刻, union中只存放了一個被選中的成員, 而struct的所有成員都存在。在struct中,各成員都佔有自己的內存空間,它們是同時存在的。一個struct變量的總長度等於所有成員長度之和。在Union中,所有成員不能同時佔用它的內存空間,它們不能同時存在。Union變量的長度等於最長的成員的長度。
2. 對於union的不同成員賦值, 將會對其它成員重寫, 原來成員的值就不存在了, 而對於struct的不同成員賦值是互不影響的。
舉一個例子:
例:
#include <stdio.h>
void main()

     
                         union{                                                      
                                    int i;      
                                    struct{                                           
                                              char first;      
                                              char second;      
                                              }half;      
                                }number;      
                          number.i=0x4241;       
                        printf("%c%cn", number.half.first, number.half.second);      
                          number.half.first='a';                        
                         number.half.second='b';      
                         printf("%xn", number.i);      
                       }
輸出結果爲:
AB
6261


9.如何理解位段

在前面各章中, 我們已經討論過字節概念了。在大多數的計算機系統中, 一個字節是由八個更小的, 稱作爲位的單位組成的。位是比字節更小的單位。位只有兩個值, 1 或 0 。因此, 存儲在計算機存儲器中的一個字節可以看成由八個二進制數字形成的串。
例如, 一個存放值 36 的字節是八個二進制數字的串: 可以表示成 00100100。 存入值24 的字節可以表示成 00010100。
有時, 我們希望不僅對字節進行操作, 也要能對位進行操作。例如, 用布爾真或假條件表示的標誌, 在計算機中可用位來表示。
但是, 說明一個用作標誌的普通變量至少要用一個字節---8 位, 而在某些計算機系統中則可能是 16 位。 如果我們想在一個很大的表中存儲很多標誌, 那麼 "被浪費" 的內存空間是很可觀的。在 C 語言中, 一種方法是用叫做位段的構造類型來定義一個壓縮信息的結構。


10.define的使用注意點

1.簡單的define定義
#define MAXTIME 1000
一個簡單的MAXTIME就定義好了,它代表1000,如果在程序裏面寫
if(i<MAXTIME){.........}
編譯器在處理這個代碼之前會對MAXTIME進行處理替換爲1000。
這樣的定義看起來類似於普通的常量定義CONST,但也有着不同,因爲define的定義更像是簡單的文本替換,而不是作爲一個量來使用,這個問題在下面反映的尤爲突出。
2.define的“函數定義”
define可以像函數那樣接受一些參數,如下
#define max(x,y) (x)>(y)?(x):(y);
這個定義就將返回兩個數中較大的那個,看到了嗎?因爲這個“函數”沒有類型檢查,就好像一個函數模板似的,當然,它絕對沒有模板那麼安全就是了。可以作爲一個簡單的模板來使用而已。
但是這樣做的話存在隱患,例子如下:
#define Add(a,b) a+b;
在一般使用的時候是沒有問題的,但是如果遇到如:c * Add(a,b) * d的時候就會出現問題,代數式的本意是a+b然後去和c,d相乘,但是因爲使用了define(它只是一個簡單的替換),所以式子實際上變成了
c*a + b*d
另外舉一個例子:
#define pin (int*);
pin a,b;
本意是a和b都是int型指針,但是實際上變成int* a,b;
a是int型指針,而b是int型變量。
這是應該使用typedef來代替define,這樣a和b就都是int型指針了。
所以我們在定義的時候,養成一個良好的習慣,建議所有的層次都要加括號。
3.宏的單行定義
#define A(x) T_##x
#define B(x) #@x
#define C(x) #x
我們假設:x=1,則有:
A(1)------〉T_1
B(1)------〉'1'
C(1)------〉"1"
(這裏參考了 hustli的文章)
3.define的多行定義
define可以替代多行的代碼,例如MFC中的宏定義(非常的經典,雖然讓人看了噁心)
#define MACRO(arg1, arg2) do { /
/* declarations */ /
stmt1; /
stmt2; /
/* ... */ /
} while(0) /* (no trailing ; ) */
關鍵是要在每一個換行的時候加上一個"/"
摘抄自http://www.blog.edu.cn/user1/16293/archives/2005/115370.shtml 修補了幾個bug
4.在大規模的開發過程中,特別是跨平臺和系統的軟件裏,define最重要的功能是條件編譯。
就是:
#ifdef WINDOWS
......
......
#endif
#ifdef LINUX
......
......
#endif
可以在編譯的時候通過#define設置編譯環境
5.如何定義宏、取消宏
//定義宏
#define [MacroName] [MacroValue]
//取消宏
#undef [MacroName]
//普通宏
#define PI (3.1415926)
帶參數的宏
#define max(a,b) ((a)>(b)? (a),(b))
關鍵是十分容易產生錯誤,包括機器和人理解上的差異等等。
6.條件編譯
#ifdef XXX…(#else) … #endif
例如
#ifdef DV22_AUX_INPUT
#define AUX_MODE 3 
#else
#define AUY_MODE 3
#endif
#ifndef XXX … (#else) … #endif
7.頭文件(.h)可以被頭文件或C文件包含;
重複包含(重複定義)
由於頭文件包含可以嵌套,那麼C文件就有可能包含多次同一個頭文件,就可能出現重複定義的問題的。
通過條件編譯開關來避免重複包含(重複定義)
例如
#ifndef __headerfileXXX__
#define __headerfileXXX__

//文件內容

#endif

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章