日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

3012C语言_数据

發布時間:2024/1/17 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 3012C语言_数据 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第二章 數據

  • 2.1 數據類型
    • 2.1.1 數據類型決定
      • 1. 數據占內存字節數
      • 2. 數據取值范圍
      • 3. 其上可進行的操作
  • 2.2基本數據類型
    • 2.2.1分類 基本類型
        • 類型 符號 關鍵字 字節 16位/32位/64位 位數 32位 數的表示范圍 32位
          • 整型 ?
              • [signed] int 2/4/4 4*8=32 -2^31 ~ 2^31-1
              • [signed] short [int] 2/2/2 2*8=16 -2^15 ~ 2^15-1
              • [signed] long [int] 4/4/4 32 -2^31 ~ 2^31-1
              • [signed] long long [int] 8/8/8 64 -2^63 ~ 2^63-1
              • unsigned short [int] 2/2/2 16 0 ~ 2^16-1
              • unsigned int 2/4/4 32 0 ~ 2^32-1
              • unsigned long [int] 4/4/8 32 0 ~ 2^32-1
              • unsigned long long [int] 8/8/8 64 0 ~ 2^64-1
          • 實型
            • 有 float 4/4/4 32 0&土1.2e土38
            • 有 double/longdouble有8有16 8/8/8 64 0&土1.2e土308
          • 字符(變量)
            • 有 [signed] char 1/1/1 8 -128~127 -2^7 ~ 2^7-1
            • 無 unsigned char 1/1/1 8 0~255 0~ 2^8-1
          • 字符(常量) ? ? /4/ 字節
          • 指針類型
            • 指針 無 char*/int*(指針變量) 2/4/8 32
        • (1)指針大小
          • 指針的大小是由內存尋址空間決定的,即地址總線決定。
          • char*(即指針變量): 4個字節(32位的尋址空間是2^32,即32個bit,也就是4個字節。同理64位編譯器)
          • 一般32位機尋址空間4G,所以指針占4字節;
          • 一般16位的單片機尋址空間是64k,所以指針占2字節。
        • (2)字符型符號
          • C標準規定為 Implementation Defined(由實作環境決定)
          • ?arm-linux-gcc規定 char 為 unsigned char
          • ?vc 編譯器、x86上的 gcc 規定 char 為 signed char
          • 缺省情況下,編譯器默認數據為signed類型,但是char類型除外。
          • 為了代碼移植,一定不要用 char,用 signed char
        • (3)鐵則
          • int,long int,short int的寬度都可能隨編譯器而異。
          • 但有幾條鐵定的原則(ANSI/ISO制訂的):
          • ?sizeof(shortint)<=sizeof(int)
          • ?sizeof(int)<=sizeof(longint)
          • ?shortint至少應為16位(2字節)
          • ?longint至少應為32位。
    • 2.2.2? 常量
        • 在程序運行中,其值不能被改變的量稱為常量。
      • 2.2.2.1? 分類
        • 符號常量:
          • 用標識符代表常量
          • 定義格式: #define?? 符號常量?? 常量
            • 一般用大寫字母
            • 是宏定義預處理命令,不是C語句
        • 直接常量:
          • 整型常量
          • 實型常量
          • 字符常量
          • 字符串常量
          • 符號常量
      • 2.2.2.2? 整型常量
        • (1)整型常量有3種形式:十進制整型常量、八進制整型常量和十六進制整型常量。
        • (注意:c語言中沒有直接表示二進制的整型常量,在c語言源程序中不會出現二進制。)
        • 整型常量后可以用u或U明確說明為無符號整型;用l或L明確說明為長整型數
          • ?? 二進制:
            • 所有數字由0,1構成,逢二進一,二進制數中不會出現2.。
            • 例:110101
          • ? ?八進制:
            • 以數字0(注意不是以字母O,o)開頭,所有數字由0~7構成,逢八進一,八進制數中不會出現8。
            • 八進制整型常量:051 ,-026 ,0773 等
          • ?? 十進制:
            • 所有數字由0~9構成,逢十進一,十進制數中不會出現10。
            • 十進制整型常量:123 , 0 ,-24 , 85L(長整型常量)等
          • ?? 十六進制:以0x或者0X(數字0加字母x)開頭,
            • 所有數字由0~9,A~F(或者a~f)構成,逢十六進一
            • (其中A、B、C、D、E、F分別代表10、11、12、13、14、15)
            • 十六進制整型常量:0x55 , 0x1101 , 0x , 0x5AC0 , -0xFF。
      • 2.2.2.3? 實型常量
        • 實型常量有兩種表示形式:小數形式和指數形式。
        • 編碼形式存儲。
          • 小數形式:5.4?? 0.074?????-23.0
          • 指數形式:5.4e04.3e-3 -3.3e4 -3.3E4??? e可大小寫
        • (1)小數部分為0的實型常量,可以寫為453.0 或453。
        • (2)用小數表示時,小數點的兩邊必須有數,不能寫成“ .453“和“453.“,而應該寫成“0.453“和“453.0“。
        • (3)用指數寫法時,e前必須有數字,e后面的指數必須為整數
        • (注意:整數階碼可以是正數,負數,也可以是八進制數、十六進制數,但必須為整數)。
      • 2.2.2.4? 字符常量
        • 字符常量的標志是一對單引號‘ ’,c語言中的字符常量有兩類:
        • (1)????由一對單引號括起來的一個字符,如‘a ’, ‘r’,‘#’。
          • 注意: ′a′ 和 ′A′ 是兩個不同的字符常量。
          • '1' 是字符占一個字節,"1"是字符串占兩個字節(含有一個結束符號)。
          • '0' 的ASCII數值表示為48,'a' 的ASCII數值是97,'A'的ASCII數值是65。
          • 一般考試表示單個字符錯誤的形式:'65'??? "1"
          • 字符是可以進行算術運算的,記住:'0'-0=48
          • 大寫字母和小寫字母轉換的方法:'A'+32='a'??相互之間相差32。
        • (2)由一對單引號括起來,以反斜杠\開頭,后跟若干數字或者字母,比如‘\n’,其中“\“是轉義的意思,后面跟不同的字符表示不同的意思,這類字符常量叫轉義字符。具體如圖所示 。
          • ‘A’? =? ‘\101’?=? ‘\x41’? =? 65
          • 在程序中 int a = 0x6d,是把一個十六進制的數給變量a 注意這里的0x必須存在。
          • 在程序中 int a = 06d, 是一個八進制的形式。
          • 在轉義字符中
          • ’\x6d’ 才是合法的,0不能寫,并且x是小寫。
          • ‘\141’ 是合法的, 0是不能寫的。
          • ‘\108’是非法的,因為不可以出現8。
      • 2.2.2.5? 字符串常量
        • C語言中,以雙引號括起來的,由若干個字符組成的序列即為字符串常量。
          • 例:“nihao”?? “happy”等等
        • 字符串的概念
        • 簡單而言,字符串是若干有效字符的序列,可包含轉義字符、ASCⅡ碼表中的字符;
          • 形式為: 用雙引號括起來的字符序列;
          • 例:"I am a? student."? , "Hello "
          • "a[5]="; "%f\n"。
        • 字符串的結束標志:‘\0’。
      • 2.2.2.6? 符號常量
        • 符號常量是由宏定義“#define“定義的常量,在C程序中可用標識符代表一個常量。
          • 例:計算圓的面積的c程序。
            • #include<stdio.h>
            • #define? PI??3.14159
            • main()
            • {
            • float?r,s;
            • r=12.5;
            • s=PI*r*r;
            • printf("s= %f",s);
            • }
        • 說明:
          • #define 是宏定義,此程序中所有出現PI的地方都代表3.14159,同時PI稱為符號常量。習慣上我們用大寫字母來表示符號常量,小寫字母表示變量,這樣比較容易區別。
          • define? f(x)(x*x) 和 define f(x) x*x 之間的差別。一定要好好的注意這寫容易錯的地方,替換的時候有括號和沒有括號是很大的區別。
    • 2.2.3? 變量
      • 定義
        • 變量就是其值可以改變的量。變量要有變量名,在內存中占據一定的存儲單元,存儲單元里存放的是該變量的值。不同類型的變量其存儲單元的大小不同,變量在使用前必須定義。
      • 2.2.3.1? 整型變量
        • 整型變量分為4種:
          • 基本型(int)、
          • 短整型(short int 或short)、
          • 長整型(long int 或 long)
          • 無符號型(unsigned int ,unsigned short,unsigned long)。
        • 不同的編譯系統對上述四種整型數據所占用的位數和數值范圍有不同的規定。
        • 說明:
          • 單詞signed來說明“有符號”(即有正負數之分),不寫signed也隱含說明為有符號,unsigned用來說明“無符號”(只表示正數)。
      • 2.2.3.2? 實型變量
        • C語言中,實型變量分為單精度類型( float )和雙精度類型( double )兩種。如:
          • float? a , b ;
          • double? m ;
        • 單精度實數提供7位有效數字,雙精度實數提供15~16位有效數字。
        • 實型常量不分float型和double型,一個實型常量可以賦給一個float 型或double型變量,但變量根據其類型截取實型常量中相應的有效數字。
        • 注意:實型變量只能存放實型值,不能用整型變量存放實型值,也不能用實型變量存放整型值。
      • 2.2.3.3? 字符變量
        • 字符變量用來存放字符常量,定義形式:
          • char? 變量名;
        • 其中關鍵字char定義字符型數據類型,占用一個字節的存儲單元。
          • 例:char? cr1,cr2;
          • cr1= ‘A’ , cr2=‘B’ ;
        • 將一個字符賦給一個字符變量時,并不是將該字符本身存儲到內存中,而是將該字符對應的ASCII碼存儲到內存單元中。
          • 例如,字符 ′A′ 的ASCII碼為65,在內存中的存放形式如下:01000001
        • 由于在內存中字符以ASCII碼存放,它的存儲形式和整數的存儲形式類似,所以C語言中字符型數據與整型數據之間可以通用,一個字符能用字符的形式輸出,也能用整數的形式輸出,字符數據也能進行算術運算,此時相當于對它們的ASCII碼進行運算。
      • 2.2.4.4? 類型的自動轉換和強制轉換
        • 當同一表達式中各數據的類型不同時,編譯程序會自動把它們轉變成同一類型后再進行計算。轉換優先級為:
          • 即下邊級別“低“的類型向上邊轉換。具體地說,若在表達式中優先級最高的數據是double型,則此表達式中的其他數據均被轉換成double型,且計算結果也是double型;若在表達式中優先級最高的數據是float型,則此表達式中的其他數據均被轉換成float型,且計算結果也是float型。
          • 在做賦值運算時,若賦值號左右兩邊的類型不同,則賦值號右邊的類型向左邊的類型轉換;當右邊的類型高于左邊的類型時,則在轉換時對右邊的數據進行截取。
        • 除自動轉換外,還有強制轉換,表示形式是:
          • 一般形式:(類型說明符)(表達式)
          • 功能:把表達式的運算結果強制轉換成類型說明符所表示的類型
          • 例:(int)(a+b)
            • 一定是 (int)a 不是? int(a),注意類型上一定有括號的。
            • 注意(int)(a+b) 和(int)a+b 的區別。 前是把a+b轉型,后是把a轉型再加b。
            • 例1表達式(int)((double)(5/2)+2.5)的值是4。
              • (int)((double)(5/2)+2.5)
              • →(int)((double)2)+2.5)
              • →(int)(2.000000+2.5)
              • →(int)(4.500000)
              • →4。
            • 例2:以下程序運行后的輸出結果是(3) 。
              • main()
              • { ??? int a;
              • a=(int)((double)(3/2)+0.5+(int)1.99*2);
              • printf("%d\n",a);
              • }
              • (3/2)=1,
              • (double)(3/2)+0.5=1.5,
              • (int)1.99*2=2,(double)(3/2)+0.5+(int)1.99*2=3.5,故a=3。
        • 三種取整丟小數的情況:
          • 1、int a=1.6;
          • 2、(int)a;
          • 3、1/2; 3/2; //除法
      • 2.2.4.5? 全局變量和局部變量
        • 在C語言中,用戶命名的標識符都有一個有效的作用域。
        • 所謂標識符的“作用域”就是指程序中的某一部分,在這部分中,該標識符是有定義的,可以被C編譯和連接程序所識別。
        • 我們知道每個變量都有自己的作用域,在一個函數內定義的變量,不能在其他函數中引用。顯然,變量的作用域與其定義語句在程序中出現的位置有直接的關系,據此變量可以劃分為局部變量和全局變量。
        • 注意“定義”和 “說明”兩個詞的區別。“定義”是指給變量分配確定的存儲單元;“說明”只是說明變量的性質。
        • (一)局部變量
          • 在一個函數內部定義的變量,它們只在本函數范圍內有效,即只有本函數才能使用它們,其他函數不能使用這些變量,我們將這些變量稱為“局部變量”。不同函數中可以使用相同名字的局部變量,它們代表不同的對象,在內存中占不同的單元,互不干擾。
        • (二)全局變量
          • 在函數之外定義的變量稱為外部變量,外部變量是全局變量。全局變量可以為本文件中其他函數所共用,它的有效范圍從定義變量開始到本文件結束。
          • 如果在同一個源文件中,外部變量與局部變量同名,則在局部變量的作用范圍內,外部變量被“屏蔽”,即它不起作用。
        • 變量作用域和生存期
          • 生存期:什么時候這個變量開始出現了,到什么時候消亡了
          • 作用域:?在(代碼的)?什么范圍內可以訪問這個變量(這個變量可以起作用)
          • 對于本地變量,這兩個問題的答案是統一的:大括號內一塊
      • 2.2.4.6? 變量的存儲類別
        • 變量值存在的時間(即生存期)
        • 靜態存儲方式和動態存儲方式。
          • 所謂靜態存儲方式是指在程序運行期間分配固定的存儲空間的方式,而動態存儲方式是在程序運行期間根據需要動態分配存儲空間的方式
          • 在內存中供用戶使用的空間可以分為程序區、靜態存儲區和動態存儲區3個部分。數據分別被存放在靜態存儲區和動態存儲區中。靜態存儲區中存放的是全局變量,在程序開始執行時就給全局變量分配存儲區。程序執行過程中它們占據固定的存儲單元,程序執行完畢這些存儲單元就被釋放。
          • 每一個變量和函數所具有的屬性是:數據的存儲類別和數據類型(在前面已經介紹過)。所謂的存儲類別指的是數據在內存中存儲的方法,其可分為兩類:靜態存儲類和動態存儲類。具體包括自動(auto)、靜態(static)、寄存器(register)和外部(extern),共4種。
        • (一)auto變量
          • 當在函數內部或復合語句內定義變量時,如果沒有指定存儲類別,或使用了auto說明符,系統就認為所定義的變量具有自動類別。如:
            • float a;等價于auto float a;
            • auto變量的存儲單元被分配在內存的動態存儲區,每當進入函數體(或復合語句)時,系統自動為auto變量分配存儲單元,退出時自動釋放這些存儲單元另做他用。因此,這類局部變量的作用域是從定義的位置起,到函數體(或復合語句)結束止。
            • 所有自動類局部變量的存儲單元都是在進入這些局部變量所在的函數體(或復合語句)時生成,退出其所在的函數體(或復合語句)時消失(變為無定義)。這就是自動類局部變量的“生存期“。當再次進入函數體(或復合語句)時,系統將為它們另行分配存儲單元,因此變量的值不可能被保留。隨著函數的頻繁調用,動態存儲區內為某個變量分配的存儲單元位置會隨程序的運行而改變。
        • (二)register變量
          • 寄存器變量也是自動類變量。它與auto變量的區別僅在于:用register說明變量是建議編譯程序將變量的值保留在CPU的寄存器中,而不是像一般變量那樣占用內存單元。程序運行時,訪問寄存器內的值要比訪問內存中的值快得多。因此,當程序對運行速度有較高要求時,把那些頻繁引用的少數變量,指定為register變量,有助于提高程序運行的效率。
            • 說明:
              • (1)CPU中寄存器的數目是有限的,因此只能說明少量的寄存器變量。在同一個函數中,允許說明為寄存器變量的數目不僅取決于CPU的類型,也與所用的C編譯程序有關。當沒有足夠的寄存器來存放指定的變量,或編譯程序認為指定的變量不適合放在寄存器中時,將自動按auto變量來處理。因此,register說明只是對編譯程序的一種建議,而不是強制性的。
              • (2)由于register變量的值存放在寄存器內而不是存放在內存中,所以register變量沒有地址,也就不能對它實行求地址運算。
              • (3)register變量的說明應盡量靠近其使用的地方,用完之后盡快釋放,以便提高寄存器的利用效率。
        • (三)靜態存儲類別的局部變量
          • 當函數體(或復合語句)內部用static來說明一個變量時,可以稱該變量為靜態局部變量。它與auto變量、register變量的本質區別是:
            • (1)在整個程序運行期間,靜態局部變量在內存中的靜態存儲區中占據著永久性的存儲單元。即使退出函數后,下次再進入該函數時,靜態局部變量仍使用原來的存儲單元。由于不釋放這些存儲單元,這些存儲單元中的值得以保留,因而可以繼續使用存儲單元中原來的值。由此可知,靜態局部變量的生存期將一直延長到程序運行結束。
            • (2)靜態局部變量的初值是在編譯時賦予的,在程序執行期間不再賦以初值。對未賦值的局部變量,C語言編譯程序自動給它賦初值為0。
        • (四)用static聲明外部變量
          • 有時在程序設計中希望某些外部變量只限于本文件使用,而不能被其他文件引用,這時可以在定義外部變量時加一個static聲明。
            • 并不是對外部變量加上static才是靜態存儲而不加static的是動態存儲。兩種形式的外部變量都是靜態存儲方式,只是作用范圍不同而已。
        • (五)用extern聲明外部變量
          • ① 在一個文件內聲明外部變量
            • 當全局變量定義在后,引用它的函數在前時,應該在引用它的函數中用extern對此全局變量進行說明,以便通知編譯程序,該變量是一個已在外部定義了的全局變量,已經分配了存儲單元,不需要為它另開辟存儲單元。這時其作用域從extern說明處起,延伸到該函數末尾。
            • 全局變量的說明與全局變量的定義不同。變量的定義(開辟存儲單元)只能出現一次,在定義全局變量時,不可使用extern說明符;而對全局變量的說明,則可以多次出現在需要的地方,這時必須用extern進行說明。
          • ② 在多文件的程序中聲明外部變量
            • 當一個程序由多個單獨編譯的源文件組成,并且在每個文件中均需要引用同一個全局變量時,若在每個文件中都定義了所需的同名全局變量,則在“連接”時將會產生“重復定義”的錯誤。在這種情況下,單獨編譯每個文件時并無異常,編譯程序將按定義分別給它們開辟存儲空間,而當進行連接時,就會顯示出錯信息。解決的方法是:在其中一個文件中定義所有全局變量,而在其他用到這些全局變量的文件中用extern對這些變量進行說明。
  • 2.3構造類型
    • 2.3.1? 數組
      • 2.3.1.1? 一維數組的定義和引用
        • (一)數組的概念
          • 數組是由屬于同一個數據類型的有序數據集構成的。數組中的每一個數據稱為“元素“。可以用一個統一的數組名和下標來唯一地標識數組中的元素。
        • (二)一維數組的定義
          • 一維數組的定義方式為:
          • 類型說明符??數組名[常量表達式];
          • 如:
            • char c[20];
          • c是數組名,此數組共有20個元素,并且每個元素的類型都為字符型。
        • (三)一維數組元素的引用
          • 數組元素的表示形式為:
            • 數組名[下標];
              • 引用數組元素時,數組的下標可以是整型常量,也可以是整型表達式。
              • 和變量一樣,數組必須先定義后使用。數組元素只能逐個引用而不能把數組當做一個整體一次引用。
        • (四)一維數組的初始化
          • 當數組定義后,系統會為該數組在內存中開辟一串連續的存儲單元,但這些存儲單元中并沒有確定的值。可以在定義數組時為所包含的數組元素賦初值,如:
            • int a[6]={ 0,1,2,3,4,5 };
            • 所賦初值放在一對花括號中,數值類型必須與所說明類型一致。所賦初值之間用逗號隔開,系統將按這些數值的排列順序,從a[0]元素開始依次給a數組中的元素賦初值。以上語句將a[0]賦值0,a[1]賦值1, …… ,a[5]賦值5。在指定初值時,第一個初值必定賦給下標為0的元素。也就是說數組元素的下標是從0開始的。同時,不可能跳過前面的元素給后面的元素賦初值,但是允許為前面元素賦值為0。當所賦初值個數少于所定義數組的元素個數時,將自動給后面的其他元素補以初值0;當所賦初值個數多于所定義數組的元素個數時,也就是說超出了數組已經定義的范圍,在編譯時系統將給出出錯信息。
          • C語言規定可以通過賦初值來定義數組的大小,這時一對方括號中可以不指定數組大小。
    • 2.3.1.2? 一維數組與函數
      • 一、 一維數組元素作為實參
        • 無論是一維數組元素或者二維數組元素,和普通變量的使用沒有任何區別,他們之間僅僅是變量名不同,一維數組元素作為實參傳遞給形參,形參數據改變不會影響實參變化。
      • 二、一維數組元素地址作為實參
        • 一維數組元素的地址作為實參,對應的形參必須是與實參基類型相同的指針變量。此時可以通過被調用函數改變調用函數中的數據。
          • 例 有以下程序段
            • #include<stdio.h>
            • voidfun(int x,int *p)
            • {
            • x*=2;
            • p[0]=p[-1]+p[1];
            • }
            • main()
            • {
            • int a[10]={1,2,3,4,5,6,7,8,9,10};
            • fun(a[2],&a[6]);
            • printf("%d? %d\n",a[2],a[6]);
            • }
              • 程序運行后的輸出結果是 3? 14
              • 本題主函數中有函數調用 fun (a[2],&a[6]);
              • 第一個傳遞的實參是a[2]的值3,對原數不影響
              • 第二個傳遞的實參是&a[6] (a[6]的地址),指針變量p存儲a[6]的地址,根據指針變量加下標表示數據的方法,p[0]存儲的是a[6]的值,p[-1]存儲的是a[5]的值6,p[1]存儲的是a[7]的值8。經過p[0]=p[-1]+p[1]計算;值為14。
      • 三、一維數組名作為實參
        • 一維數組名為地址常量,表示數組的首地址,如果一維數組名作為實參,對應的形參應該是一個指針變量,此指針變量的基本類型必須與數組的類型一致。
        • 通常被調用函數的首部可以有以下3中方式:
          • (1)fun(int *a)
          • (2)fun(int a[N])
          • (3)fun(int a[ ])
            • 例? 有以下程序段
              • #include<stdio.h>
              • int fun(int *x,int n)
              • {
              • int i , sum = 0 ;
              • for (i=0;i<n;i++)
              • sum=sum+x[i];
              • return sum;
              • }
              • main()
              • {
              • int a[]={1,2,3,4,5 },s=0;
              • s=fun (a,5);
              • printf("%d? \n",s);
              • }
                • 程序運行后的輸出結果是15
                • 本題main函數中定義了一維數組a,含有5個int類型的數組元素,所以地址常量a的基本類型為int類型,對應的形參x的基本類型也是int類型,可以進行參數的傳遞,傳遞后依然用指針變量加下標的方式表示數據。
    • 2.3.1.3 二維數組的定義和引用
    • (一)二維數組的定義
      • 在C語言中,二維數組中元素排列的順序是:按行存放,即在內存中先順序存放第一行的元素,再存放第二行的元素。因此,二維數組元素的存儲與一維數組元素存儲相類似,總是占用一塊連續的內存單元。
      • 二維數組的一般形式為:
        • 類型說明符??數組名[常量表達式][常量表達式];
        • 如:int c[3][4]; 定義c為3×4 (3行4列)的數組。
          • 注意:不能寫成c[3,4]。C語言對二維數組采用這樣的定義方式:我們可以把二維數組當做是一種特殊的一維數組。
            • 例如,可以把c看成是一個一維數組,它有3個元素c[0]、c[1]、c[2],每個元素又是一個包含4個元素的一維數組。可以把c[0]、c[1]、c[2]看做是3個一維數組的名字。
    • (二)二維數組的引用
      • 二維數組的表示形式為:
        • 數組名[下標][下標]
          • 數組的下標可以是整型表達式,如 c[3-1][3×2-2];
      • 數組元素可以出現在表達式中,也可以被賦值。
      • 定義數組時用的c[3][4]和引用元素時的c[3][4]的區別:前者用來定義數組的維數和各維的大小,共有3行4列;后者中的3和4是下標值,c[3][4]代表該數組中的一個元素。如果a[3][4]是二維數組中最后一個元素,那么該數組共有4行5列。
    • (三)二維數組的初始化
      • 可以在定義二維數組的同時給二維數組的各元素賦初值。
        • 如:
          • float m[2][2]={{1.5,3.2},{0.8}};
      • 全部初值放在一對花括號中,每一行的初值又分別括在一對花括號中,之間用逗號隔開。當某行一對花括號內的初值個數少于該行中元素的個數時,系統將自動地給后面的元素補初值0。同樣,不能跳過每行前面的元素而給后面的元素賦初值。
    • (四)通過賦初值定義二維數組的大小
      • 對于一維數組,可以在數組定義語句中省略方括號中的常量表達式,通過所賦初值的個數來確定數組的大小;對于二維數組,只可以省略第一個方括號中的常量表達式,而不能省略第二個方括號中的常量表達式。
      • 如:
        • int a[][3]={{1,2,3},{4,5},{6},{8}};
      • a數組的第一維方括號中的常量表達式省略,在所賦初值中,含有4個花括號,則第一維的大小由花括號的個數來決定。因此,該數組其實是與a[4][3]等價的。當用以下形式賦初值時:
        • int c[][3]={1,2,3,4,5};
      • 第一維的大小按以下規則決定:
        • (1)當初值的個數能被第二維的常量表達式的值除盡時,所得商數就是第一維的大小。
        • (2)當初值的個數不能被第二維的常量表達式的值除盡時,則:
          • 第一維的大小 =所得商數 + 1 。
      • 因此,按此規則,以上c數組第一維的大小應該是2,也就是說語句等同于
      • int c[2][3]={{1,2,3},{4,5}};。
    • 2.3.1.4 二維數組與函數
      • 二維數組名作為實參
        • 二維數組元素作為實參,與普通變量作為實參沒有任何區別,二維數組名為行指針常量,如果二維數組名作為實參,對應的形參必須是一個行指針變量。
        • 例如:有以下定義和函數調用語句:
          • #define? M? 5
          • #define? N? 3
          • main()
          • {
          • int s[M][N];
          • …….
          • fun(s);
          • …….
          • }
          • 此時,實參為二維數組名(即行指針),則fun函數的首部可以是以下3中形式之一:
            • (1)fun ( int? (*a)[N])
            • (2) fun ( int ?a[ ][N])
            • (3) fun ( int? a[M][N])
          • 注意:行下標可以省略,列下標不可以省略。無論哪種方式,系統都把a看作一個行指針變量。
      • 二、指針數組名作為實參
        • 指針數組名師指向指針的指針常量,因此當指針數組名作為實參時,對應的形參應該為一個指向指針的指針變量。
        • 例如:有以下定義和函數調用語句:
          • #define? M? 5
          • #define? N? 3
          • main()
          • {
          • int??s[M][N],*p[M];
          • …….
          • for(i=0;i<M;i++)? p[i]=s[i];
          • fun(p);
          • …….
          • }
          • 此時,實參為一維數組名(即行指針),則fun函數的首部可以是以下3中形式之一:
            • (2)fun ( int? *a[M])
            • (2) fun ( int?? *a[ ])
            • (3) fun ( int? **a)
      • 2.3.1.5? 字符數組
        • 注:C語言無字符串類型,字符串是存放在字符數組中的。
        • (一)字符數組的定義
          • 字符數組就是數組中的每個元素都是字符,定義方法同普通數組的定義相同,即逐個對數組元素賦值。如:
          • char c[11];
          • c為該數組名,該數組共有11個元素,并且每個元素都為字符型。
        • (二)字符數組的初始化及引用
          • 對字符數組初始化,可逐個元素地賦值,即把字符逐個賦給數組元素。如:
            • char a[9]={ ′T′, ′h′, ′a′, ′n′, ′k′, ′′, ′y′, ′o′,′u′};
            • 如果花括號中提供的初值個數(即字符個數)大于數組長度,則按語法錯誤處理。
            • 如果初值個數小于數組長度,則將這些字符賦給數組中前面那些元素,其余的元素自動定為空字符(′\0′)。如:
              • char c[6]={′G′,′o′,′o′,′d′};
            • 字符數組的引用形式與其他數組的引用形式相同,采用下標引用,
              • 即 數組名[下標]。
                • 例如:
                • #include<stdio.h>
                • main()
                • { char c[9]={'T','h','a','n','k',',','y','o','u'};
                • int i;
                • for(i=0; i<9; i++)
                • printf(“%c“,c[i]);
                • }
                • 輸出的結果為Thank,you。
        • (三)字符串和字符串結束標志
          • C語言中,將字符串作為字符數組來處理。為了測定字符串的實際長度,C語言規定了一個字符串結束標志,以字符'\0'代表。就是說,在遇到字符'\0'時,表示字符串結束,由它前面的字符組成字符串。
          • 系統對字符串常量也自動加一個'\0'作為結束符。
            • 例如:
            • char c[]=“c program“;
            • 數組c共有9個字符,但在內存中占10個字節,最后一個字節'\0'是由系統自動加上的。有了結束標志'\0'后,在程序中往往依靠檢測 '\0'的位置來判定字符串是否結束,而不是根據數組的長度來決定字符串長度。
            • 說明:'\0'代表ASCII碼為0的字符,是一個“空操作符“,它什么也不干。在輸出時也不輸出'\0',它只是一個結束的標志。
        • (四)字符數組的初始化
          • 方法:將字符常量以逗號分隔寫在花括號中
          • ①在定義字符數組時進行初始化
            • charch[7]={‘s’,’t’,’u’,’d’,’e’,’n’,’t’};
          • ②在對全部元素指定初值時,可省寫數組長度。
            • char ch[]={‘s’,’t’,’u’,’d’,’e’,’n’,’t’};
          • ③如果花括弧內提供的初值個數大于數組長度?
        • (五)用字符串來直接初始化字符數組
          • 可直接把字符串寫在花括號中來初始化字符數組.
            • 如:charch[9]={“student”};
          • 系統將雙引號括起來的字符依次賦給字符數組的各個元素, 并自動在末尾補上字符串結束標志字符'\0'。
          • 幾點說明:
            • (1)字符串結束標志'\0'僅用于判斷字符串是否結束,輸出字符串時不會輸出。
            • (2)在對有確定大小的字符數組用字符串初始化時,數組長度應大于字符串長度。如: char s[7]={"student"};是錯誤的.
            • (3)在初始化一個一維字符數組時,可以省略花括號。
            • 如:? char s[8]="student";
            • ( 4 )不能直接將字符串賦值給字符數組名。下面的操作是錯誤的。
            • 如:? s=”student”;
      • 2.3.1.6 注意
        • 兩種重要的數組長度
          • char a[]={‘a’,’b’,’c’};?? //不安全,無 \0
            • 數組長度為3,字符串長度不定。sizeof(a)為3。
          • char a[5]={ ‘a’,’b’,’c’}
            • 數組長度為5,字符串長度3。sizeof(a)為5。
            • #include"stdio.h"
            • #include"string.h"
            • main()
            • {
            • char m[] = "abc";
            • //???? char n[] = {'a','b','c','\0'};?????? //4 4?3 3
            • char n[] = {'a','b','c'};??????????? //4 3? 3 3
            • printf("%d %d\n",sizeof(m),sizeof(n));
            • printf("%d %d\n",strlen(m),strlen(n));
            • return 0;
            • }
        • 數組的重要概念
          • 對a[10]這個數組的討論。
            • 1、a表示數組名,是第一個元素的地址,也就是元素a[0]的地址。
            • 2、a是地址常量,所以只要出現a++,或者是a=a+2賦值的都是錯誤的。
            • 3、a是一維數組名,所以它是列指針,也就是說a+1是跳一列。
          • 對a[3][3]的討論。
            • 1、a表示數組名,是第一個元素的地址,也就是元素a[0] [0]的地址。
            • 2、a是地址常量,所以只要出現a++,或者是a=a+2賦值的都是錯誤的。
            • 3、a是二維數組名,所以它是行指針,也就是說a+1是跳一行。
            • 4、a[0]、a[1]、a[2]也都是地址常量,不可以對它進行賦值操作,同時它們都是列指針,a[0]+1,a[1]+1,a[2]+1都是跳一列。
            • 5、注意a和a[0] 、a[1]、a[2]是不同的,它們的基類型是不同的。前者是一行元素,后三者是一列元素。
        • 數組的初始化
          • 一維和二維的,一維可以不寫,二維第二個一定要寫
            • int a[]={1,2} 合法。??
            • int a[][4]={2,3,4}合法。??
            • int a[4][]={2,3,4}非法。
        • 二維數組中的行指針
          • int a[1][2];
          • 其中a現在就是一個行指針,a+1跳一行數組元素。?搭配(*p)[2]指針
          • a[0],a[1]現在就是一個列指針。a[0]+1 跳一個數組元素。搭配*p[2]指針數組使用
        • 脫衣服法則
          • a[2]? 變成? *(a+2)
          • a[2][3]變成 *(a+2)[3]再可以變成? *(*(a+2)+3)
          • 這個思想很重要!
    • 2.3.2結構體
      • 2.3.2.1? 結構體類型
        • 在實際工作中,當我們需要把一些不同類型,但相互之間又存在著聯系的信息組合應用時,就要用到結構體。結構體是一種看似復雜但卻非常靈活的構造型數據類型。在通常情況下,一個結構體類型由若干個稱為成員(或稱為域)的部分組成。不同的結構體類型可根據需要由不同的成員組成。但對于某個具體的結構體類型,其成員的數量必須固定,這一點與數組相同;但該結構體中各個成員的類型可以不同,這是結構體與數組的重要區別。例如,我們常用的“時間“可以由以下3個部分描述:小時(hour)、分(minute)、秒(second)。它們都可以用整型數表示,可以把這3個成員組成一個整體,并給它取名為time,這就是一個簡單的結構體。
      • 2.3.2.2? 結構體聲明
        • 聲明一個結構體類型的一般形式為:
          • struct? 結構體名
          • {? 成員表列? };
          • struct是C語言中的關鍵字,是結構體類型的標志。“結構體名“用做結構體類型的標志,它又稱“結構體標記“(structure tag)。大括弧內是該結構體中的各個成員,成員表列是由若干個變量類型名及變量名組成的。這些成員共同組成一個結構體。
            • 例如,上面提到的“時間“結構體類型可以說明如下:
              • structtime
              • {
              • inthour;
              • intminute;
              • intsecond;
              • };
                • 其中,time就是結構體名, hour、minute、second都是成員,并且各成員都應進行類型聲明,每個成員也就是結構體中的一個域。
          • 成員名命名規則與變量名相同。所以結構體類型也可以用以下形式說明:
            • struct 結構體標識名
            • {
            • 類型名1??? 結構體成員名表1;
            • 類型名2??? 結構體成員名表2;
            • ……
            • 類型名n??? 結構體成員名表n;
            • };
            • 說明:
              • (1)“結構體標識名”和“結構體成員名表”都必須是合法的用戶定義的標識符。
              • (2)每個“結構體成員名表“中都可以含有多個同類型的成員名,它們之間以逗號分隔。
              • (3)結構體類型說明中的“類型名1”~“類型名n”,不僅可以是簡單數據類型,也可以是某種結構體類型。當結構體說明中又包含結構體時,稱為結構體的嵌套。
              • (4)ANSI C標準規定結構體至多允許嵌套15層,并且允許內嵌結構體成員的名字與外層成員的名字相同。
      • 2.3.2.3? 結構體類型變量的定義
        • 前面只是指定了一個結構體類型,為了能在程序中使用結構體類型的數據,就需要定義結構體類型的變量,并在其中存放具體的數據。可以用如下方法定義結構體類型變量。
          • (一)先聲明結構體類型再定義變量名
            • 如上面已經定義了一個結構體類型struct? time,可以如下定義:
            • struct time??? time1,time2;
            • 結構體類型名??? 結構體變量名;
            • time1和time2為struct time類型變量,即它們都具有struct time類型的結構。
          • (二)在聲明類型的同時定義變量
            • 其一般形式為:
            • struct? 結構體名 { 成員表列 } 變量名表列;
          • (三)直接定義結構體類型變量
            • 其一般形式為:?struct { 成員表列 } 變量名表列;
            • 即不出現結構體名。
            • 類型與變量是兩個不同的概念,使用時應注意區別。只能對變量賦值、存取或運算,而不能對一個類型進行賦值、存取或運算。可以單獨使用結構體中的成員,它與普通變量的作用相同。
      • 2.3.2.4? 結構體變量引用
        • 在定義了結構體變量以后,當然可以引用這個變量。但應注意:
        • (1)結構體變量不能作為一個整體而對其進行任何操作,只能對結構體變量中的各個成員分別進行輸入和輸出等操作。結構體變量中的成員用以下方式引用:
          • 結構體變量名.成員名
        • (2)如果結構體的某個成員本身又是一個結構體類型,則可以使用若干個成員運算符一級一級地找到最低的一級成員,只能對最低一級的成員進行賦值或存取及運算。
        • (3)結構體變量的初始化,是指逐個對結構體變量的各個成員進行初始化的過程。
      • 2.3.2.5 ?結構體數組
        • 和普通數組一樣,結構體數組中的每個元素都屬于同一數據類型(結構體類型),只不過各個元素本身又都包含多個成員項。例如,一個結構體變量中存放著一組數據(如某產品的名稱、型號、尺寸、顏色等數據),現在如果有10個這樣產品的數據需要參加運算,顯然應當用到結構體數組。和定義結構體變量的方法相仿,只需說明其為數組即可。
        • 其一般形式為:
          • struct? 結構體變量名 { 成員表列} 數組名[常量表達式];
        • 結構體數組的初始化
          • 結構體數組的初始值應順序地放在一對花括號中,由于數組中的每一個元素都是一個結構體,因此通常將其成員的值依次放在一對花括號中,以便區分各個元素。
      • 2.3.2.6 指向結構體類型數據的指針
        • 一個結構體變量的指針就是用來指向該結構體類型的存儲單元,并指向結構體變量所占據的內存段的起始地址。
        • (一)指向結構體變量的指針
          • 結構體變量.成員名”、“(*結構體指針變量名).成員名“和“結構體指針變量名->成員名”這3種形式是等價的,其中“->“稱為指向運算符,它由兩部分組成:“-”減號和“>”大于號,它們之間不能有空格
          • 看下面的例子:
            • #include<stdio.h>
            • #include<string.h>
            • main()
            • {
            • struct objects
            • {
            • char name[20];
            • int size;
            • char color[10];
            • float weight;
            • float height;
            • };
            • struct objects obj1;
            • struct objects *p;
            • p=&obj1;
            • strcpy(obj1.name,"pen");
            • obj1.size=10;
            • strcpy(obj1.color,"black");
            • obj1.weight=50.5;
            • obj1.height=18.5;
            • printf("name: %s\nsize: %d\ncolor:%s\nweight: %f\nheight:%f\n",obj1.name,obj1.size,obj1.color,obj1.weight,obj1.height);?? printf("name: %s\nsize: %d\ncolor: %s\nweight:%f\nheight: %f\n",(*p).name,(*p).size,(*p).color,(*p).weight,(*p).height);
            • }
            • 我們聲明了一個struct objects類型,并且定義了一個該類型的變量obj1,又定義了一個指向struct objects類型的數據的指針p,并且將p指向obj1,接下來是對各成員賦值。第一個printf語句用“.”的方式將obj1的成員的值輸出。第二個printf語句用(*p)將obj1的成員的值輸出,因為成員運算符“.”的優先級高于“*”運算符,所以(*p)的兩側的圓括號不能省略。以上兩個printf函數語句的輸出結果是相同的,我們可用p->name來代替(*p).name
        • (二)指向結構體數組的指針
          • 結構體數組及其元素也可以用指針變量來指向。在使用指針變量指向結構體數組時,只要把該結構體數組中的每個元素當做普通的結構體變量使用就可以了,例如:
            • #include<stdio.h>
            • #include<string.h>
            • structobjects
            • {
            • char name[20];
            • int size;
            • char color[10];
            • float weight;
            • float height;
            • };
            • structobjects obj[3]= {{"pen",10,"black",50.5,18.5},
            • {"notebook",20,"blue",180,19.5},
            • {"bag",50,"red",2000,37.5}};
            • main()
            • {
            • struct objects *p;
            • printf("name size color weightheight\n");
            • for(p=obj;p<obj+3;p++)
            • printf("%10s%d%-20s%6.5f%6.5f\n",p->name,p->size,p->color,p->weight,p->height);
            • }
          • 這樣就可以利用指針變量來逐個把結構體數組中的元素的各個域輸出。
          • 說明:
            • 如果p的初值為obj,即指向第一個元素,則p+1就指向下一個元素。
              • 例如:
                • (++p)->name;先使p自加1,然后得到它指向的元素中的name成員值。
                • 而(p++)->name;先得到p->name的值,然后使p自加1,指向obj[1];。
                • p只能指向一個structobjects類型的數據,不能指向obj數組元素中的某一成員(即p的地址不是成員的地址)。例如,p=&obj[1].name;是不對的。對結構體變量中的每個成員,都可以像普通變量一樣,對它進行同類變量所允許的任何操作。
        • (三)用結構體變量和指向結構體的指針作為函數參數
          • 將一個結構體變量的值傳遞給另一個函數,有如下方法:
            • (1)結構體變量的成員作為實參傳遞給主調函數。
            • (2)可以用結構體變量作為一個整體實參。
            • (3)C語言中,允許將結構體變量的地址作為實參傳遞,這時,對應的形參應該是一個基類型相同的結構體類型的指針。
    • 2.3.3共用體
      • 共用體的類型說明和變量的定義方式與結構體的類型說明和變量定義的方式完全相同。不同的是,結構體中的成員各自占有自己的存儲空間,而共用體的變量中的所有成員占有同一個存儲空間。可以把一個整型變量、一個字符型變量、一個實型變量放在同一個地址開始的內存單元中。以上3個變量在內存中所占的字節數不同,但都從同一個起始地址開始存放,也就是使用覆蓋技術,幾個變量相互覆蓋。
      • (一)共用體類型的說明
        • 共用體類型說明的一般形式為:
          • union 共用體標識名
          • {
          • 類型名1共用體成員名1;
          • 類型名2共用體成員名2;
          • .
          • 類型名n共用體成員名n;
          • };
            • 例如:
              • unionexample
              • {
              • int a;
              • float b;
              • char? c;
              • };
            • 其中,union是關鍵字,是共用體類型的標志,example是共用體標識名。“共用體標識名“和“共用體成員名“都是由用戶定義的合法標識符,按語法規定共用體標識名是可選項,在說明中可以不出現。
        • (二)共用體變量的定義
          • 和結構體相似,共用體變量的定義也可采用3種方式,一種方法如下:
            • union un
            • {
            • int i;
            • float x;
            • }s1,s2,*p;
          • 說明:
            • (1)共用體變量在定義的同時只能用第一個成員的類型的值進行初始化。
            • (2)“共用體“與“結構體“的定義形式相似,但它們的含義是不同的。結構體變量所占內存長度是各成員占的內存長度之和,每個成員分別占有其自己的內存單元,而共用體變量所占的內存長度等于變量中所占字節最長的成員的長度。例如,上面的共用體占4字節(因為一個實型變量占4字節)。
        • (三)共用體變量中成員的引用
          • 共用體變量中每個成員的引用方式與結構體完全相同,可以使用以下3種形式之一:
            • (1)共用體變量.成員名
            • (2)(*共用體指針變量名).成員名
            • (3)共用體指針變量名->成員名
          • 共用體中的成員變量同樣可參與其所屬類型允許的任何操作,但在訪問共用體成員時應注意:共用體變量中起作用的是最近一次存入的成員變量的值,原有成員變量的值將被覆蓋。
          • 另外,ANSIC標準允許在兩個類型相同的共用體變量之間進行賦值操作。同結構體變量一樣,共用體類型的變量可以作為實參進行傳遞,也可以傳遞共用體變量的地址。
          • 共用體的考查:
            • union TT
            • { int a;
            • char ch[2];}
            • 考點一:sizeof (structTT) = 2;
            • 考點二:TT t1 ;? t1=0x1234;
            • 那么 ch[0]=0x 34;? ch[1]=0x12
  • 2.4指針類型
    • 2.4.1 關于地址和指針
      • 在內存區中每一個字節都有一個編號,這個編號就是“地址“,它相當于每個變量的房間號。變量的數據就存放在地址所標識的內存單元中,變量中的數據其實就相當于倉庫中各個房間存放的貨物。如果內存中沒有對字節進行編號,系統將無法對內存進行管理。內存的存儲空間是連續的,因此內存中的地址號也是連續的,并且用二進制數表示,為了直觀起見,在這里我們將用二進制數進行描述。
      • 一般微機使用的C系統為整型變量分配4個字節,為實型變量分配4個字節,為字符型變量分配1個字節,為雙精度類型變量分配8個字節。當某一變量被定義后,其內存中的地址也就確定了。
      • 在一般情況下,我們在程序中只需定義變量并指出變量名,無須去知道每個變量在內存中的具體地址,由C編譯系統來完成每個變量與其具體地址發生聯系的操作。在程序中我們對變量進行存取操作,實際上也就是對某個變量的地址存儲單元進行操作。這種直接按變量的地址存取變量的方式稱為“直接存取“方式。
      • 在C語言中,還可以用另一種稱為“間接存取“的方式來完成對變量進行存取的操作,即將變量的地址存放在另一種類型的變量中,從而通過這種新的變量類型來得到變量的值。按C語言規定,可以在程序中定義整型變量、實型變量、字符變量等,也可以定義這樣一種特殊的變量,它是專門用來存放地址的。
      • 由于通過地址能找到所需的變量單元,我們就可以說:地址“指向“該變量單元。 所謂“指向“就是通過地址來體現。
      • 在C語言中,將地址形象地稱為“指針“,意思是通過它能找到以它為地址的內存單元,這里包含有一個方向指向的意思。一個變量的地址稱為變量的“指針“。一個專門用來存放另一個變量的地址的變量(即指針),則稱它為“指針變量“。變量的指針就是變量的地址。存放變量地址的變量是指針變量。即在C語言中,允許用一個變量來存放指針,這種變量稱為指針變量。因此,一個指針變量的值就是某個變量的地址或稱為某變量的指針。
      • 為了表示指針變量和它所指向的變量之間的關系,在程序中用“*”符號表示“指向”,例如,i_p代表指針變量,而*i_p是i_p所指向的變量。因此,下面兩個語句作用相同:i=3;? 語句是把3賦值給變量單元i。*i_p=3;? 語句是把3賦值給i_p指向的變量單元。兩者都能正確存儲數據。
    • 2.4.2 變量的指針和指向變量的指針變量
      • (一)指針變量的定義
        • 定義指針變量的一般形式如下:
          • 類型名? *指針變量名1,*指針變量名2,… ;
          • 如:int?*p,*t;
            • int *p;和int* p;一樣
        • 以上定義語句中,p和t都是合法用戶標識符,在每個變量前的星號(*)是一個類型說明符,用來標識該變量是指針變量。
        • 變量前的星號不可省略,若省略了星號說明符,就變成了把p和t定義為整型變量(int是類型名)。在這里,說明了p和t是兩個指向整型(int 類型)變量的指針,也就是說變量p和t中只能存放int類型變量的地址,這時我們稱int是指針變量p和t的“基類型“。基類型用來指定該指針變量可以指向的變量的類型。
          • 例如:??? int *p1;
            • 表示p1是一個指針變量,它的值是某個整型變量的地址。或者說p1指向一個整型變量。至于p1究竟指向哪一個整型變量,應由向p1賦予的地址來決定。
          • 再如:
            • int*p2;??????? /*p2是指向整型變量的指針變量*/
            • float*p3;????? /*p3是指向浮點變量的指針變量*/
            • char*p4;?????? /*p4是指向字符變量的指針變量*/
              • 應該注意的是,一個指針變量只能指向同類型的變量,如P3 只能指向浮點變量,不能時而指向一個浮點變量,時而又指向一個字符變量。
      • (二)指針變量的引用
        • 指針變量中只能存放地址(指針),將一個整型變量(或任何其他非地址類型的數據)賦給一個指針變量是不允許的。
          • 如:
            • int? *p; /*定義一個指向整型變量的指針*/
            • p=300;? /*300為整數*/
            • 是不合法的賦值。
        • 與指針相關的兩個運算符是“&“(取地址運算)和“*“(指針運算符)。
          • 1)?????? &:取地址運算符。
          • 2)?????? *:指針運算符(或稱“間接訪問” 運算符)。
        • C語言中提供了地址運算符&來表示變量的地址。
          • 其一般形式為:
            • &變量名;
            • 如:
              • int?? i,*p;
              • P=&i;
        • 注意
            • #include"stdio.h"
            • main1()????????? //錯誤:運行時出錯,P沒有指向明確的單元,p是隨機數,非常危險,萬一是重要地址
            • {
            • int *p;??????????? //解決方法 int k, *p = &k;??????????????????????? inta, *p; ?????? p = &a;是對的
            • *p = 5;
            • printf("%d\n", *p);
            • }
            • //*p=NULL是將p指向的內存賦值為NUll,而p本身不會變
            • //p=NULL是改變了p本身的值,將它指向的地址改為NULL,
            • //所以在使用地址指針的時候出錯的時候,都是將指針變量賦值為NULL的
            • swap(int*p1, int *p2)?????????? //錯誤,運行報錯,指針變量在使用前必須賦值
            • {
            • int*p;?????????????????? // 和int *p;?? *p = 5;一個錯誤 ,p指向不明確 ,即不知道5存在哪
            • //? int k; p = &k; ??????? //改正
            • *p =*p1;
            • *p1= *p2;
            • *p2= *p;
            • }
            • main2()
            • {
            • int a, b;
            • int *ip1, *ip2;
            • scanf("%d%d",&a, &b);
            • ip1 = &a; ip2 = &b;
            • if(a < b)
            • swap(ip1,ip2);
            • printf("%d %d\n", a, b);
            • }
            • main()?????????????????? //錯的,指針類型不同,編譯報錯 [Error] cannot convert 'float*' to 'int*' in assignment
            • {
            • int*p;
            • floatc;
            • p =&c;
            • }
            • /*
            • int a[5] = {1, 2, 3, 4, 5}, i; //a地址常量,數組首地址?? a[i]電腦存儲方式 *(a + i)
            • int *p = a;
            • 下標法??????????????????????????????? 指針法
            • 第1個元素:
            • 地址:??? a? ???????? ?p???????????????? ???? a?????????????????? p
            • &a[0]????? &p[0]
            • 內容:??? a[0]? p[0] ????????????? *a????????????????? *p
            • 第i個元素:
            • 地址:??? &a[i]?????? &p[i]?????? ???? ????? a +i?????? p + i
            • 內容:??? a[i]?? p[i]??????????????? *a????????????????? *(p + i)
            • */
            • inta[10];
            • printf("%p\n",&a); ????????????? //0xbff8dd44
            • printf("%p\n",a); ? ?????????? //0xbff8dd44
            • printf("%p\n",&a[0]); ????????? //0xbff8dd44
            • printf("%p\n",&a[1]); ????????? //0xbff8dd48
      • (三)指針變量作為函數參數
        • 前面的章節中介紹過,函數參數可以是整型、實型、字符型等數據,指針類型數據同樣也可以作為函數參數來進行傳遞。它的作用是將一個變量的地址傳送到另一個函數中,參與該函數的運算。
        • 如果想通過函數調用從而得到n個要改變的值,可以采用如下方法:
          • (1)在主調函數中設n個變量,分別用n個指針變量指向它們。
          • (2)然后將各個指針變量作為實參,即將這n個變量的地址傳給所調用的函數的形參。
          • (3)通過形參指針變量的改變,從而改變這n個變量的值。
          • (4)主調函數中就可以使用這些改變了值的變量。
          • 形參指針變量的值的改變不能使實參指針變量的值發生改變。
    • 2.4.3 數組與指針
      • 一個數組包含若干個元素(變量),在定義時被分配了一段連續的內存單元。因此,可以用一個指針變量來指向數組的首地址,通過該首地址就可以依次找到其他數組元素,同樣指針變量也可以指向數組中的某一個元素。所謂數組的指針是指數組的起始地址,數組元素的指針是各個數組元素的地址。
      • (一)指向數組元素的指針
        • C語言規定數組名代表數組的首地址,也就是數組中第0號元素的地址。有如下語句:
          • int c[10]={0};
          • int *p;
          • p=c;
        • 則語句p=c;與語句p=&c[0];是等價的。
        • 數組c不代表整個數組。上述“p=c;“的作用是把數組c的首地址賦值給指針變量p,而不是把數組c中各元素的值賦給p。
        • 定義指向數組元素的指針變量的方法,與定義指向變量的指針變量相同。例如:
          • int c[10],*p;
          • p=&c[5];
          • 指針變量p指向了數組c中下標為5的那個元素,即p用來保存c[5]的地址。
      • (二)通過指針引用數組元素
        • 按C語言的規定:如果指針變量p已指向數組中的一個元素,則p+1指向同一數組中的下一個元素(而不是將p的值簡單加1),這里的加1是指增加一個長度單位(與數組基類型所占存儲單元相同)。例如,數組元素是浮點型,每個元素占4個字節,則p+1意味著使p的值(是一個地址)加4個字節,以使它指向下一個元素。
        • 將++和--運算符用于指針變量是十分有效的,可以使用指針變量自動向前或向后移動,指向下一個或上一個數組元素。不過要小心利用,否則會導致內存錯誤。
      • (三)用數組名作為函數參數
        • 數組名可以用做函數的形參和實參。當數組名作為參數被傳遞時,若形參數組中各元素發生了變化,則原實參數組各元素的值也隨之變化。因為數組名作為實參時,在調用函數時是把數組的首地址傳送給形參,因此實參數組與形參數組共占一段內存單元。而如果用數組元素作為實參的情況就與用變量作為實參時一樣,是“值傳遞“方式,單向傳遞,即使形參數組元素值發生了變化,原實參的數組元素值也不會受影響。
      • (四)指向多維數組的指針和指針變量
        • 有如下定義:
          • int c[3][4],*p;
        • c是該二維數組的數組名,它代表了整個數組的首地址,也就是第0行的首地址,c+1代表第1行的首地址,即c+1是c[1]的地址。c[0]、c[1]、c[2]既然是一維數組名,而C語言又規定了數組名代表數組的首地址,因此c[0]代表第0行的一維數組中第0列元素的地址,即&c[0][0]。同樣地,c[1]的值是&c[1][0]。
        • 第0行第1列元素的地址怎么表示呢?可以用c[0]+1來表示。此時“c[0]+1“中的1代表1個列元素的字節數,即兩個字節。如c[0]的值是2020,則c[0]+1的值是2022。
        • 根據上面的介紹,c[i]和*(c+i)等價,因此,c[0]+1和*(c+0)+1的值都是&c[0][1],都是用來表示第0行第1列的元素的地址。c[i]從形式上看是數組c中第i個元素。如果c是二維數組名,則c[i]代表一維數組名,c[i]本身并不占實際的內存單元,它也不存放數組中各個元素的值,它只是一個地址。c、c+i、c[i]、*(c+i)+j、c[i]+j都是地址,*(c[i]+j)、*(*(c+i)+j)是元素的值。
    • 2.4.5 字符串與指針
      • (一)字符串的表示形式
        • (1)用字符數組存放一個字符串,然后輸出該字符串。
          • 例如:
            • char str[]=“I am a student??“;
            • printf(“%s\n“,str);
        • (2)用字符指針指向一個字符串。
          • 可以不定義數組,而定義一個字符指針,用字符指針指向字符串中的字符。
          • 例如:
            • char*str=“I am a student??“;
            • /*定義str為指針變量,并指向字符串的首地址*/
            • printf(“%s\n“,str);
          • 在這里沒有使用字符數組,而是在程序中定義了一個字符指針變量str,并使該指針變量指向一個字符串的首地址。C語言對字符串常量是按字符數組進行處理的,在內存中開辟了一個字符數組來存放字符串常量。程序在定義字符指針變量str時,把字符串的首地址賦給str。str只能指向一個字符變量或其他字符類型數據,不能同時指向多個字符數據,更不能理解為把字符串中的全部字符存放到str中(指針變量只能存放地址)。在輸出時,利用字符型指針str的移動來控制輸出,直到遇到字符串結束標志'\0'為止。
          • 通過字符數組名或字符指針變量可以一次性輸出的只有字符數組(即字符串),而對一個數值型的數組,是不能企圖用數組名輸出它的全部元素的,只能借助于循環逐個輸出元素。
          • 顯然,用%s可以控制對一個字符串進行整體的輸入輸出。對字符串中字符的存取,與操作其他數組的方法相同,既可以用下標方法,又可以用指針方法。
      • (二)字符串指針作函數參數
        • 將一個字符串從一個函數傳遞到另一個函數,可以用地址傳遞的辦法,即用字符數組名作為參數或用指向字符串的指針變量作為參數,進行傳遞。
        • 字符串指針變量作為函數實參,形參可以是字符指針變量,同樣也可以是字符數組名。當字符數組名作為函數實參時,形參可以是字符數組名,同樣也可以是字符指針變量。
      • (三)字符指針變量和字符數組的區別
        • 雖然字符數組和字符指針變量都能實現對字符的存儲和運算,但它們兩者之間有如下區別:
        • (1)字符數組是由若干個元素組成的,每個元素中存放一個字符,而字符指針變量中存放的是地址(字符串的首地址),絕不是將字符串的內容存放到字符指針變量中。
        • (2)賦值方式。
          • 只能對字符數組各個元素賦值,不能用以下方法對字符數組賦值:
            • char str[20];
            • str=“Iam happy“;
          • 而對字符指針變量,可以采用以下方法賦值:
            • char *s;
            • s=“I amhappy??“;
        • (3)字符數組可以在定義時對其整體賦初值(即初始化),但在賦值語句中不能完成整體賦值。下面的做法是不允許的:
          • char s[30];
          • s[]=“I am so happy“;
          • 而字符指針變量既可以在定義時賦初值,也可以出現在賦值語句中,相對來說要比字符數組使用起來靈活一點。
        • (4)如果定義了一個字符數組,在編譯時,系統會為它分配一段連續的內存單元,它的地址是確定的。而當定義了一個字符指針變量后,就要即時給該指針變量分配內存單元,該指針變量中可以存放一個地址值,也就是說,該指針變量可以指向一個字符型數據,但如果未對它賦以一個地址值,則它并未具體指向一個確定的字符數據。如:
          • char s[10];
          • scanf(“%s“,s);
          • 是可以的。但用下面的方法是極其危險的:
            • char *s;
            • scanf(“%s“,s);
          • 因為編譯時雖然給指針變量s分配了內存單元,s的地址(即&s)已經指定了,但s的值并未指定。在s單元中是一個不可預料的值,在執行scanf()函數時要求將一個字符串輸入到s所指向的一段內存單元中,即以s的值(地址)開始的一段內存單元。而s的值如今卻是不可預料的,它可能指向內存中空白的存儲區(未用的用戶存儲區),這樣固然可以,但它也有可能指向內存中已存放指令或數據的有用內存段。這就會破壞程序,甚至破壞系統,造成嚴重的后果。應當這樣:
            • char*a,s[10];
            • a=s;
            • scanf(“%s“,a);
            • 先使a有確定值,也就是使a指向一個數組的首地址,然后輸入一個字符串,把它存放在以該地址開始的若干單元中。
        • (5)在程序中指針變量的值可以改變。例如:
          • char*s=“china”;
          • s=s+2;
          • 指針變量s的值可以改變,當要輸出字符串時,從s當前所指向的單元開始輸出各個字符(本題中從字符i開始輸出),直到遇到'\0'為止。而數組名雖然代表了地址,但它的值是一個固定的值,是不能改變的。下面是錯的:
          • char str[]={“china”};
          • str=str+2;
    • 2.4.6? 指向函數的指針
      • 用函數指針變量調用函數
        • 我們已經知道,可以用指針變量指向整型變量、字符型變量、字符串、數組,同樣指針變量也可以指向一個函數。編譯時,一個函數將被分配給一個入口地址,這個入口地址就稱為該函數的指針。因此,可以通過使用一個指向函數的指針變量調用此函數。
        • 說明:
          • (1)指向函數的指針變量的一般定義形式為:
            • 數據類型 (*指針變量名)( );
              • 如int(*s)();,“數據類型”指該函數返回值的類型。
          • (2)(*s)()表示定義了一個指向函數的指針變量,但目前它不是固定指向哪一個函數,而只是表示定義了這樣一個類型的變量,它的作用是專門用來存放函數的入口地址。在程序中實現把某一個函數的地址賦給它,它就指向那一個函數,這樣它的值也就確定了。在一個程序中,一個指針變量以先后指向不同的函數,也就是說指向函數的指針變量和普通指針變量一樣,可以多次使用。
          • (3)在給函數指針變量賦值時,只需給出函數名而不必給出參數。如:
            • s=fun;? /*? fun為已有定義的有參函數*/
            • 因為是將函數入口地址賦給s,不涉及到參數的問題,不能寫成:
            • s=fun(a,b);
          • (4)用函數指針變量調用函數時,只需將(*s)代替函數名即可(s為已經定義過的指向函數的指針變量名),在(*s)之后的括號中根據需要寫上實參。
          • (5)對指向函數的指針變量,有些運算,如++s、--s、s+3等都是沒有意義的。
        • 函數指針的用法(*f)()記住一個例子:
          • intadd(int x, int y)
          • {....}
          • main()
          • {int? (*f)();
          • f=add;
          • }
          • 賦值之后:合法的調用形式為
            • 1、add(2,3);
            • 2、f(2,3);
            • 3、(*f)(2,3)
    • 2.4.7 返回指針的函數
      • 返回指針值的函數
      • 一個函數的返回值可以是一個整型值、字符型值、實型值等,同樣地,函數的返回值也可以是指針類型的數據,即地址。這種返回指針值的函數,一般定義形式為:
      • 類型名??? *函數名(參數表);
      • 例如:
      • int*fun(int a,int b);
      • fun是函數名,調用它可以得到一個指向整型數據的指針(地址)。a、b是兩個整形變量,是函數fun()的形參。注意:*fun在兩側沒有括弧,在fun的兩側分別為*運算符和()運算符。而()優先級高于*,因此fun先與()結合,顯然這是函數形式。這個函數前面有一個*,表示此函數是指針型函數。
    • 2.4.8 指針數組和指向指針的指針
      • (一)指針數組的概念
        • 若在一個數組中,其元素均為指針類型數據,這樣的數組稱為指針數組,也就是說,指針數組中的每一個元素都相當于一個指針變量。一維指針數組的定義形式為:
        • 類型名? *數組名[數組長度];
          • 例如:
          • int *a[10];
          • 由于[]運算符的優先級比*運算符的優先級高,因此a先與[10]結合,形成a[10]形式,然后與前面的“*“結合,“*“表示此數組是指針類型的,每個數組元素都可指向一個整型變量。
          • 指針數組的一個重要用途是可以用來指向若干個字符串。例如,在對庫房物品進行管理時,想把物品名稱放在一個數組中,然后對這些物品進行統計和查詢,我們可以分別定義一些字符串來存放各物品名稱,然后利用指針數組中的元素分別指向各字符串。如果還想對字符串排序,不必改動字符串的位置,只需改動指針數組中各元素的指向(即改變各元素的值,這些值是各字符串的首地址)。這樣,各字符串的長度可以不同,而且移動指針變量的值(地址)要比移動字符串所花的時間少。
      • (二)指向指針的指針
        • 指向指針數據的指針變量,簡稱為指向指針的指針,通常稱為二級指針。定義一個指向指針數據的指針變量的形式:
          • 類型名  **a;
          • a前面有兩個“*“號,*a是指針變量的定義形式,現在它前面又有一個“*“號,表示指針變量是指向某種類型的指針變量的。
    • 2.4.9 注意
      • 二級指針和行指針區別
        • #####################################################################################################################################################################################
      • 指針變量的本質是用來放地址,而一般的變量是放數值的
        • int? *p 中?? *p和p的差別:
          • *p可以當做變量來用;*的作用是取后面地址p里面的數值
          • p是當作地址來使用。
        • *p++ 和(*p)++的之間的差別:
          • *p++是 地址會變化。
          • (*p)++ 是數值會要變化。
      • 三名主義:
        • 數組名:表示第一個元素的地址。數組名不可以自加,他是地址常量名。
        • 函數名:表示該函數的入口地址。
        • 字符串常量名:表示第一個字符的地址。
      • 指針變量是存放地址的。并且指向哪個就等價哪個,所有出現*p的地方都可以用它等價的代替。
        • 例如:int a=2,*p=&a;
        • *p=*p+2;
        • (由于*p指向變量a,所以指向哪個就等價哪個,這里*p等價于a,可以相當于是a=a+2)
      • 指針變量兩種初始化
        • 方法一:int a=2,*p=&a;(定義的同時初始化)
        • 方法二:int a=2,*p; p=&a;  (定義之后初始化)
    • 2.4.10 指針應用--鏈表
      • (一)鏈表的概念
        • 鏈表是一種常見的重要的數據結構,它是動態地進行存儲單元分配的一種結構。
        • 鏈表中的各元素在內存中不一定是連續存放的。要找鏈表中某一元素,必須先找到上一個元素,根據該元素提供的下一元素的地址才能找到下一個元素。所以,如果沒有頭指針(head),則整個鏈表都無法訪問。另外一點,這種鏈表的數據結構,必須利用指針變量才能實現。即一個節點中應包含一個指針變量,用它存放下一節點的地址。當然也可以不通過指針變量,用其他方式也可以構建簡單鏈表,請參考有關數據結構的教材。
        • 下面通過一個例子來說明如何建立和輸出一個簡單鏈表。
          • #include<stdio.h>
          • #include<string.h>
          • structnode
          • {
          • int data;
          • struct node *next;
          • };
          • typedefstruct node NODETYPE;
          • main()
          • {
          • NODETYPE s1,s2,s3,*begin,*p;
          • s1.data=100;/*給變量中的data域賦值*/
          • s2.data=200;
          • s3.data=300;
          • begin=&s1;
          • s1.next=&s2;/*使s1的域next指向s2*/
          • s2.next=&s3;
          • s3.next='\0';
          • p=begin;/*移動p,使之依次指向s1、s2、s3,輸出它們data域中的值*/
          • while(p)
          • {
          • printf("%d",p->data);
          • p=p->next;?? /* p順序后移 */
          • }
          • printf("\n");
          • }
        • main()函數中定義的變量s1、s2、s3都是結構體變量,它們都含有data和next兩個成員。變量begin和p是指向NODETYPE結構體類型的指針變量,它們與結構體變量s1、s2、s3中的成員變量next類型相同。執行賦值語句后,begin中存放s1變量的地址,變量s1的成員s1->next中存放變量s2的地址……最后一個變量s3的成員s3->next置成'\0'(NULL),從而把同一類型的結構體變量s1、s2、s3“鏈接”到一起,形成“鏈表”。
        • 在此例中,鏈接到一起的每個節點(結構體變量s1、s2、s3)都是通過定義,由系統在內存中開辟了固定的存儲單元(不一定連續)。在程序執行的過程中,不可能人為地再產生新的存儲單元,也不可能人為地使已開辟的存儲單元消失。從這一角度出發,可稱這種鏈表為“靜態鏈表“。在實際中,使用更廣泛的是一種“動態鏈表”。
      • (二)建立動態鏈表(主要針對單向鏈表)
        • 建立單向鏈表的主要步驟如下:
          • (1)讀取數據。
          • (2)生成新節點。
          • (3)將數據存入節點的成員變量中。
          • (4)將新節點插入到鏈表中,重復上述操作直至輸入結束。
        • 編寫函數creatlist(),建立帶有頭節點的單向鏈表。節點數據域中的數值從鍵盤輸入,以-1作為輸入結束標志。鏈表的頭節點的地址由函數值返回。
        • 我們在函數中定義了一個名為begin的指針變量,用于存放頭節點的地址,另外還定義了兩個工作指針:current和end。其中指針current用來指向新生成的節點,指針end總是指向鏈表當前的尾節點。每當把current所指的新開辟的節點連接到表尾后,end便移向這一新的表尾節點。這時又可以用current去指向下一個新開辟的節點。鏈表最后一個節點的指針域中置'\0'(NULL值)作為單向鏈表的結束標志。
        • 鏈表建成后,頭節點的地址由creatlist()返回,賦給main()函數中的指針變量head。函數如下:
          • #include<stdio.h>
          • #include<string.h>
          • #include<stdlib.h>
          • structnode
          • {
          • int data;
          • structnode *next;
          • };
          • typedefstruct node NODETYPE;
          • NODETYPE*creatlist()
          • {
          • int i;
          • NODETYPE *begin,*end,*current;
          • begin=(NODETYPE*)malloc(sizeof(NODETYPE));/*生成頭節點*/
          • end=begin;
          • scanf("%d",&i);/*輸入數據*/
          • while(i!=-1)/*未讀到數據結束標志時進入循環*/
          • {
          • current=(NODETYPE*)malloc(sizeof(NODETYPE));/*生成一個新節點*/
          • current->data=i;/*讀入的數據存入新節點的data域*/
          • end->next=current;/*新節點連到表尾*/
          • end=current;/*end指向當前表尾*/
          • scanf("%d",&i);/*讀入數據*/
          • }
          • end->next='\0';/*置鏈表結束標志*/
          • return begin;/*返回表頭指針*/
          • }
          • main()
          • {
          • NODETYPE *head;
          • head=creatlist();/*調用鏈表建立函數,得到頭節點的地址*/
          • }
        • 以上creatlist()函數中,當一開始輸入-1時,并不進入while循環,而直接執行循環之后的end->next='\0';語句,這時建立的是一個“空鏈表“。由此可見,可用條件begin->next=='\0'來判斷鏈表是否為空。
      • (三)順序訪問鏈表中各節點的數據域
        • 所謂“訪問“,可以理解為取各節點的數據域中的值進行各種運算、修改各節點的數據域中的值等一系列的操作。
        • 輸出單向鏈表各節點數據域中內容的算法比較簡單,只需利用一個工作指針(p),從頭到尾依次指向鏈表中的每個節點,當指針指向某個節點時,就輸出該節點數據域中的內容,直到遇到鏈表結束標志為止。如果是空鏈表,就只輸出提示信息并返回調用函數。
        • 函數如下:
          • voidprintlist(NODETYPE *head)
          • {
          • NODETYPE*p;
          • p=head->next;/*指向頭節點后的第一個節點*/
          • if(p=='\0')/*鏈表為空時*/
          • printf(“Linklistis null\n”);
          • else/*鏈表不為空時*/
          • {
          • printf(“head”);
          • do
          • {
          • printf(“->%d”,p->data);/*輸出當前節點數據域中的值*/
          • p=p->next;/*move指向下一個節點*/
          • }while(p!=′\0′);/*未到鏈表尾,繼續循環下去*/
          • }
          • printf(“->end\n”);
          • }
      • (四)在鏈表中插入節點
        • 在單向鏈表中插入節點,首先要確定插入的位置插入節點在指針p所指的節點之前稱為“前插“,插入節點在指針p所指的節點之后稱為“后插“。“前插“操作中各指針的指向
        • 當進行前插操作時,需要3個工作指針:s指向新開辟的節點,用p指向插入的位置,q指向要插入的前趨節點。
      • (五)刪除鏈表中的節點
        • 為了刪除單向鏈表中的某個節點,首先要找到待刪除的節點的前趨節點(即當前要刪除節點的前面一個節點),然后將此前趨節點的指針域去指向待刪除節點的后續節點(即當前要刪除節點的下一個節點),最后釋放被刪除節點所占的存儲空間即可。
  • 2.5用typedef說明一種新類型名
    • C語言可以用typedef說明一種新類型名,說明新類型名的語句一般形式為:
      • typedef?? 類型名??? 標識符;
      • 其中,“類型名“一定是在此語句之前已有定義的類型標識符。“標識符“是一個用戶定義標識符,用來標識新的類型名。typedef語句的作用僅僅是用“標識符“來代表已存在的“類型名“,并沒有產生新的數據類型,因此,原有的類型名依然有效。
    • 聲明一個新的類型名的具體步驟如下:
      • (1)先按定義變量的方法寫出定義的主體(如float a;)。
      • (2)將變量名換成新類型名(如將a換成FLO)。
      • (3)在最左面加上關鍵字typedef(如 typedef? float FLO;)。
      • (4)然后可以用新類型名去定義其他的變量(如FLO? b;)。
  • 2.6 sizeof()計算實際所占字節數
    • 概念
      • sizeof是C語言的一種單目操作符,如C語言的其他操作符++、--等。它并不是函數。sizeof操作符以字節形式給出了其操作數的存儲大小。操作數可以是一個表達式或括在括號內的類型名。操作數的存儲大小由操作數的類型決定。作用就是返回一個對象或者類型所占的內存字節數。
    • 2.6.1語法
      • sizeof有三種語法形式,如下:
        • 1) sizeof( object ); // sizeof( 對象 );
        • 2) sizeof( type_name ); // sizeof( 類型 );
        • 3) sizeof object; // sizeof 對象;
      • 舉例
        • sizeof(int); ???//64位 4
        • int a=3; sizeof(a);????? //64位 4?? 建議使用此方法
        • sizeof(3); ?//64位 4
        • sizeof 3???? //64位4??? 是對的,不建議使用
        • sizeof int???? //Error
    • 2.6.2數組
      • 數組的sizeof值等于數組所占用的內存字節數,如:
        • char a1[] = "abc"; sizeof( a1 ); // 結果為4,字符末尾還存在一個NULL終止符
        • int a2[3]; sizeof( a2 ); // 結果為3*4=12(依賴于int)
      • sizeof不是求數組元素的個數,求數組元素的個數,通常有下面兩種寫法:
        • int c1 = sizeof( a1 ) / sizeof( char ); // 總長度/單個元素的長度
        • int c2 = sizeof( a1 ) / sizeof( a1[0] ); // 總長度/第一個元素的長度
      • 舉例
        • char array[8] = “China”; sizeof(array)?//64位 8??? 數組實際長度
        • sizeof(“hi”); ?//64位 3
        • char a[5]="CHINA"; ??printf("%d",sizeof(a));??? //64位 5
        • char a[5]="CHINAA"; ?printf("%d", sizeof(a));??? //64位 警告 結果5
        • int a[3]; sizeof(a);??//64位 3*4=12??????????????? 數組實際長度,整形一個元素4個字節
    • 2.6.3字符
      • sizeof(char) ???//64位 1 s 字符型變量是1字節
      • sizeof(‘s’) ????//64位 4???? ‘s’轉換為一個數,數占四個字節
      • sizeof(‘\101’) ????//64位 4???? ‘\101’為一個數,數占四個字節
        • ‘A’? =? ‘\101’?=? ‘\x41’? =? 65
      • char c;? sizeof(c)???????????? //64位 1
      • C和C++比較
        • C語言環境下:
          • char a = 'a' ;
          • sizeof(char) = 1 ;
          • sizeof(a) = 1 ;
          • sizeof('a') =4? ;
        • C++語言環境下:
          • char a = 'a';
          • sizeof(char) = 1;
          • sizeof(a) = 1 ;
          • sizeof('a') =1;
        • 字符型變量是1字節這個沒錯,奇怪就奇怪在C語言認為'a'是4字節,而C++語言認為'a'是1字節。
        • 原因如下:
          • C99標準的規定,'a'叫做整型字符常量(integer? character constant),被看成是int型,所以在32位機器上占4字節。
          • ISO C++標準規定,'a'叫做字符字面量(character literal),被看成是char型,所以占1字節
          • 字符是指計算機中使用的字母、數字、字和符號。1個漢字字符存儲需要2個字節,1個英文字符存儲需要1個字節。ASCII是一個字節,Unicode是兩個字節。Java的字符是Unicode的,所以是兩個字節。
    • 2.6.4結構體
      • 1.對齊
        • structS1
        • {
        • char c;
        • int i;
        • };
        • sizeof(s1)為8,字節對齊,有助于加快計算機的取數速度,否則就得多花指令周期了。為此,編譯器默認會對結構體進行處理(實際上其它地方的數據變量也是如此),讓寬度為2的基本數據類型(short等)都位于能被2整除的地址上,讓寬度為4的基本數據類型(int等)都位于能被4整除的地址上,以此類推。這樣,兩個數中間就可能需要加入填充字節,所以整個結構體的sizeof值就增長了。
      • 2.字節對齊的細節和編譯器實現相關,但一般而言,滿足三個準則:
        • 1) 結構體變量的首地址能夠被其最寬基本類型成員的大小所整除;
        • 2) 結構體每個成員相對于結構體首地址的偏移量(offset)都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充字節(internaladding);
        • 3) 結構體的總大小為結構體最寬基本類型成員大小的整數倍,如有需要編譯器會在最末一個成員之后加上填充字節(trailing padding)。
        • 舉例
          • struct S3 整除4 一共3*4=12 測試###############################################################################################################################################################################
          • {
          • char c1; 整除1
          • S1 s; 整除4
            • structS1
            • {
            • char c;
            • int i;
            • };
          • char c2 整除1
          • };
          • S1的最寬簡單成員的類型為int,S3在考慮最寬簡單類型成員時是將S1“打散”看的,所以S3的最寬簡單類型為int,這樣,通過S3定義的變量,其存儲空間首地址需要被4整除,整個sizeof(S3)的值也應該被4整除。
      • 3.“空結構體”(不含數據成員)的大小不為0,而是1。試想一個“不占空間”的變量如何被取地址、兩個不同的“空結構體”變量又如何得以區分呢于是,“空結構體”變量也得被存儲,這樣編譯器也就只能為其分配一個字節的空間用于占位了。如下:
        • structS5 { };
        • sizeof(S5 ); // 結果為1
    • 2.6.5聯合體
      • 結構體在內存組織上是順序式的,聯合體則是重疊式,各成員共享一段內存,所以整個聯合體的sizeof也就是每個成員sizeof的最大值。結構體的成員也可以是復合類型,這里,復合類型成員是被作為整體考慮的。
      • 所以,下面例子中,U的sizeof值等于sizeof(s)。
        • structS1
        • {
        • char f1;
        • int f2;
        • char*f3;
        • };
        • union U
        • {
        • int i;
        • char c;
        • S1 s;
        • };
    • 2.6.6表達式和調用函數
      • sizeof可以對一個表達式求值,編譯器根據表達式的最終結果類型來確定大小,一般不會對表達式進行計算
        • 1.# include <stdio.h>
        • int main()
        • {
        • int i;
        • i = 10;
        • printf("i : %d\n",i);
        • printf("sizeof(i++) is: %d\n",sizeof(++i));
        • printf("i : %d\n",i);
        • return 0;
        • }
          • 結果:i : 10
          • sizeof(i++) is: 4i : 10
        • 2.????chara="255";printf("%d",sizeof(a++));
          • /*打印的值是1,這一步并不對表達式a++進行計算,所以char a="255"*/
        • 3.?????sizeof( 2 + 3.14 );
          • // 3.14的類型為double,2也會被提升成double類型,所以等價于 sizeof( double );但是不計算,相當于比較里面最大類型##########################測試#################sizeof(i=2+3.14)###########################################################################
      • sizeof也可以對一個函數調用求值,其結果是函數返回類型的大小,函數并不會被調用,我們來看一個完整的例子:
        • charfoo()
        • {
        • printf("foo()has been called.\n");
        • return'a';
        • }
        • intmain()
        • {
        • size_tsz = sizeof( foo() );
        • /*foo() 的返回值類型為char,所以sz = sizeof(char ),foo()并不會被調用*/
        • printf("sizeof(foo() ) = %d\n", sz);
        • }
    • 2.6.7 不可用
      • C99標準規定,函數、不能確定類型的表達式以及位域(bit-field)成員不能被計算sizeof值,即sizeof操作符不能用于函數類型,不完全類型或位字段。不完全類型指具有未知存儲大小的數據類型,如未知存儲大小的數組類型、未知內容的結構或聯合類型、void類型等。下面這些寫法都是錯誤的:
        • 1. sizeof(foo );? // error
        • 2. void foo2() { } sizeof(foo2() );? // error
        • 3. struct S
          • {
          • unsignedint f1 : 1;? // error 屬于位字段
          • unsignedint f2 : 5;
          • unsignedint f3 : 12;
          • };
          • sizeof(S.f1 );? // error
    • 2.6.8 sizeof的常量性
      • sizeof的計算發生在編譯時刻,所以它可以被當作常量表達式使用,如:
        • charary[ sizeof( int ) * 10 ]; // ok
      • 最新的C99標準規定sizeof也可以在運行時刻進行計算,如下面的程序在Dev-C++中可以正確執行:
        • int n;
        • n = 10;// n動態賦值
        • charary[n]; // C99也支持數組的動態定義
        • printf("%d\n",sizeof(ary)); // ok. 輸出10
        • 但在沒有完全實現C99標準的編譯器中就行不通了,上面的代碼在VC6中就通不過編譯。所以我們最好還是認為sizeof是在編譯期執行的,這樣不會帶來錯誤,讓程序的可移植性強些。

轉載于:https://www.cnblogs.com/ZanderZhao/p/10013860.html

總結

以上是生活随笔為你收集整理的3012C语言_数据的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。