Linux_C編(1)數據類型

一、數據類型

數據類型包含兩方面的內容——數據的表示和對數據加工的操作。數據的全部可能表示構成數據類型的值的集合,數據全部合理的操作構成數據類型的操作集合。
在 C語言中,把整型、實型和字符型稱爲基本數據類型,又稱整型和實型爲數值型。C語言中還有構造類型、指針類型、枚舉類型和空類型。

1、變量

(1)什麼是變量
其值在其作用域中可以改變的量稱爲變量。每一個變量都有自己的名字,在內存中佔據一定的存儲空間。變量在使用前必須先定義,每一個變量都有自己的地址。變量根據其定義的類型又可以分爲整型變量,字符型變量,浮點型變量和指針型變量等。變量的值可以發生改變,意味着它可以被覆蓋、被寫入、被幅值。每個變量必須要有一個名字和它所在內存空間綁定。
代碼中聲明整型變量a,它的類型已經決定,那麼它的大小也就是4個字節(32位機),那麼在內存中就是有連續的四個字節與之對應,a變量名就代表了這4字節的空間。a變量的地址就是連續4字節的開始的地址0x000。

(2)變量名和變量值
變量名是在變量的聲明的時候,該名字就和內存中的一塊地址綁定在一起了,可以通過變量名直接找到對應的內存區域,也可以通過地址找到其內存區域。
變量的值是變量所對應的內存區域內存放的二進制序列,變量的值不會因爲變量的類型發生了改變而改變,當變量被轉換爲對應類型時,內存區域內的二進制序列以該類型的形式翻譯出來,這也就是強制類型轉換存在的原因
例如:

int a = 97;
char ch1 = 'a';
char ch2 = (char) a;
char *p = (cahr *) a;

第一行代碼:整型變量a在內存中是以97的二進制形式存放的,當使用它時,會被以十進制形式打印出來。
第二行代碼:字符變量 ch1 的ASCII值是97,也是以97的二進制存放的,使用時,會被以字符“a”的形式打印出來。
第三行代碼:將整型變量a強制類型轉換成字符型,a變量裏的值沒有發生變化,變的是它的類型,它裏面的值還是97的二進制,它類型變成了char,97的二進制變成了char,表現出來就是字符“a”。
第四行代碼:聲明一個字符型指針變量p,p是個變量,它裏面的值可變,它的值是整型變量a的值強制類型轉換成了字符型指針類型,這個時候p裏的值還是97的二進制,只不過這個97的意義已經代表了一個字符型指針,也就是一個指向字符的地址了。

由此可見:變量在內存中存放和它的值沒有關係,而是和它的類型相關的,同樣我們可以得出:一個二進制序列對於計算機本身而言沒有意義,計算機根本不知道這個二進制數據是幹嘛用的,只有具體到它的類型的時候或者出現在合適的場合時,才能代表具體的意義。如果一串二進制的數據出現在地址總線上,那麼它代表一個地址,如果該相同的數據出現在數據總線上,那麼它就代表一個數據。
例如:test1.c

char ch = 'a';
int a = (int)ch;
printf("%d %c \n",a,ch);

(3)局部變量和全局變量
函數形參變量只有在被調用期間才能分配內存單元,調用結束立即釋放。這一點表明形參變量只有在函數內纔是有效的,離開該函數就不能再使用了。這種變量有效性的範圍被稱爲變量的作用域。不僅對於形參變量,C語言中的所有的變量都有自己的作用域,按作用域範圍可分爲兩種,即局部變量和全局變量。
(a)局部變量
局部變量也稱爲內部變量,局部變量是在函數內作定義說明的,其作用域僅限於函數內,離開函數後再使用這種變量是非法的。
例如:

int f1(int a)  //函數f1
{
	int b,c;
	......
}

這個函數內a、b、c有效

int f2(int x)       //函數f2
{
	int y,z;
	......
}

這個函數內x、y、z有效

int main()
{
	int m,n;
	......
}

這個函數內m、n有效
在函數f1中,a爲形參,b、c爲一般變量,在函數f1中a、b、c有效,在其他函數中a、b、c無效,其他同理。

關於局部變量的作用域:

  1. 主函數中定義的變量也只能在主函數中使用,不能在其他函數中使用。同時,主函數也不能調用其他函數的變量。因爲主函數也是一個函數,與其他函數一樣。
  2. 形參變量是屬於被調函數的局部變量,實參變量則是屬於主函數的局部變量。
  3. 允許在不同的函數中使用相同的變量名,他們代表不同的對象,分配不同的單元。互不干擾,也不會發生混淆。
  4. 在複合語句中也可以定義變量,其作用域只在複合語句範圍內。

(b)全局變量
全局變量也稱外部變量,它是在函數外部定義中的變量,它不屬於那一個函數,它屬於一個源程序文件,其作用域是整個源程序。在函數中使用全局變量,一般應做全局變量說明,只有在函數內經過說明的全局變量纔會被其他函數使用。全局變量的說明符是extern,但在一個函數前定義的全局變量,在該函數內使用就可以不用加以說明。
例如:

int a,b;     //外部變量
void f1()
{
	......
}
float x,y;
int  f2()
{
 	......
}
int main()
{
	......
}

2、常量

其值不會發生改變的量稱爲常量,它們可以和數據類型接合起來分類,如整型常量,浮點型常量,字符常量等等。常量是可以不經過定義和初始化,而直接使用的,常量又分爲直接常量和符號常量,直接常量又叫做字面常量,如12,0,4.6,‘a’,“abcd”;符號常量如宏定義。
常量的值在其作用域內不會發生改變,也不能被賦值,在其出現時就被當成一個直接數使用,它可以被訪問,被讀取,而不能被寫,被賦值。

3、基本內置類型

整型
整型數據在其存儲在內存中的二進制信息的最高位是當做數值信息爲還是當做數據的符號位,可以將整型數據分爲帶符號整型和無符號整型兩種。每種整型又按照所需的字節個數的多少分爲三種,所以整型共有六種:帶符號整型(int),帶符號短整型(short int),帶符號長整型(long int 或long),無符號整型(unsigned int)、無符號短整型(unsigned short int),以及無符號長整型(unsigned long)。

實型
實型數據有表示範圍和精度兩個不同的特徵,爲了適應數的範圍和精度的不同要求,實型數據分爲三種類型:單精度型(float,也稱浮點型),雙精度型(double),長雙精度型(long double)。

構造類型
構造類型是指由若干個相關的數據組合在一起形成的一種複雜數據類型,構造數據類型的成分數據可以是基本數據類型的,也可以是別的構造類型的。按構造方式和構造要求區分,構造類型主要有數組類型、結構類型和共用類型。數組類型是由相同類型的數據組成;結構類型可以由不同類型的數據組成;當不同數據類型不會同時使用時,以節約內存,讓不同數據佔用同一區域,這就是共用類型。

指針類型
指針類型是取程序對象在內存中佔據的地址爲值的一種特殊的數據類型。

枚舉類型
當變量只取很少幾種可能的值,並分別用標識符對值命名時,這種變量的數據類型可以用枚舉類型來表示,如變量表示一個星期的某一天,就可以用枚舉類型來描述該變量的類型。

void類型
用保留字void表示的數據類型有兩種完全相反的意思,可以表示沒有數據(沒有結果),也可以表示某種任意類型的數據,如void *,可以指向任意的數據類型,但是最好要強制轉換一下。
void表示空類型。

數據類型的大小

類型說明符 數的範圍
int -2^31 ~2^31-1
unsigned short 0~65535(2^16-1)
short int -32768~32767(2^15-1)
unsigned short int 0~65533(2^16-1)
long int -2147483648~2147483647(2^31-1)
unsigned long 0~4294967295(2^32-1)
char -128~127(2^7-1)
unsigned char 0-255(2^8-1)

4、聲明與定義

聲明與定義
例如:

int i;
extern int i;

1、定義
所謂的定義就是編譯器創造一個對象,併爲這個對象分配一塊內存並且爲它取上一個名字,這個名字就是我們經常說的變量名。一個變量或者對象在一定的區域只能被定義一次,如果多次定義,編譯器會報錯。
2、聲明
聲明有兩重含義
第一重含義:告訴編譯器,這個名字已經匹配到一塊內存上了,上面第2行代碼用到變量或者對象是在別的地方定義的,聲明可以出現多次。
第二重含義:告訴編譯器,這個名字已經預定了,別的地方再也不能用它來作爲變量名和對象名。

5、static和extern

static
簡單來說static修飾變量,就是指該變量空間獨立於函數的auto變量或者棧變量,static變量空間在內存中的靜態區分配

  1. 修飾局部變量
    一般情況下,局部變量是存放在棧區的,並且局部變量的聲明週期在該語句塊執行結束便結束了。但是如果用static進行修飾的話,該變量變存放在靜態數據區,其生命週期一直持續到整個函數程序執行結束爲止。但是值得注意的是,該局部變量的作用域不變。
#include <stdio.h>
void fun()
{
	static int a = 1; 
	a++;
	printf("%d\n",a);
}
int main()
{
	fun();
	fun();
	return 0;
}

程序執行結果爲 2 3
說明在第二次調用fun()函數時,a的值爲2,並沒有進行初始化賦值,直接進行自增運算,所以得到的結果是3.

  1. 修飾全局變量
    對於一個全局變量,它既可以在本源文件中被訪問,也可以在同一個工程的其他源文件中被訪問,記住需要extern聲明。
    (1)file1.c
int a = 1;

(2)file2.c

#include <stdio.h>
extern int a; //這裏不是定義,而是聲明,聲明使用。
int main()
{
	printf("%d\n",a);
	return 0;
}
// 執行結果爲 1;

以上的結果爲1,但是如果把int a = 1 改成static int a = 1;那麼在file2.c是無法訪問到變量a的

  1. 修飾函數
    修飾函數和全局變量類似,改變了函數的作用域。

extern
extern 是指當前變量或者函數不是在本源文件中聲明的,它是外部變量或者外部函數,當我們試圖引用一個外部聲明的全局變量或者函數時,可以在前面加上extern。
extern可以修飾變量和函數,表示該變量或者函數在其他地方被定義過,在這裏聲明使用它。
1、extern 變量名
在任何函數體外聲明或者定義變量時,不加extern可能是定義也可能是聲明,但是加extern肯定是聲明,如果不想被其他源文件鏈接到,需要使用static關鍵字。
在函數體內聲明(注意是聲明,在函數體內不能定義全局變量)使用其他源文件的定義的變量時,必須使用extern關鍵字,因爲在函數體內默認爲局部變量。
3、extern 函數
函數默認是外部的(在函數體內或者函數體外聲明一個外部函數,extern關鍵字均可以省略),如果不想被其他源文件鏈接到,在函數前面加上關鍵字extern。

extern int fun1();

6、const

const是constant的簡寫,表示海枯石爛,只要一旦給變量前面加上關鍵字const修飾,那麼意味着該變量裏的數據可以被訪問,不能被修改。

const int a = 1;

7、auto

auto一般我們看不見,但是它又是無處不在的,在函數生命週期中聲明的變量通常叫做局部變量,也叫作自動變量。

int fun()
{
	int a = 10; //auto int a = 10;
	// do something
	return 0;
}

編譯器會在int a = 10前面加上auto關鍵字。auto的出現意味着當前變了的作用域爲當前函數或者代碼段的局部變量。

8、register

  1. 作用
    如果一個變量被register修飾,那意味着該變量會作爲一個寄存器變量,讓該變量的訪問速度達到最快。例如,一個程序邏輯中有一個很大的循環,循環中有幾個變量要頻繁進行操作,這些變量可以聲明爲register變量。
  2. 寄存器變量
    寄存器變量是指一個變量直接引用寄存器,也就是對變量名的操作的結果是直接對寄存器進行訪問。寄存器是CPU的親信,CPU操作的每個操作數和操作結果,都由寄存器來暫時保存,最後才寫入內存中或者從內存中讀取出來。
    在使用寄存器變量時,應注意:
    (1) 待聲明爲寄存器變量類型應該是CPU寄存器所能接收的類型,意味着寄存器變量單個變量,變量長度應該小於等於寄存器長度;
    (2)不能對寄存器變量使用取地址符“&”,因爲該變量沒有內存地址;
    (3)儘量在大量頻繁操作時使用寄存器變量,且聲明的變量個數應該儘可能少。

9、volatile

volatile修飾的變量表示該變量的值很容易由於外部因素髮生變化,強烈請求編譯器要老老實實地在每次對變量進行訪問時去內存中讀取。
例如:

int a = 10;
int b = a;
int c = a;

編譯器掃描代碼發現第一行將10賦值給a,接着a的值沒有發生變化,直接賦值給b,接着將a賦值給c,因爲CPU訪問內存的速度較慢,編譯器爲了提高效率,直接將10賦值給c。
注:volatite告訴編譯器,每行都要執行,按照順序執行,不要給我直接優化代碼。

10、typedef

作用:爲一種數據類型定義一個新的名字,簡化一些比較複雜的類型聲明。
1、typedef與結構的問題

typedef struct tag_node
{
	char *p_item;
	p_node p_next;
} *p_node;

typedef的最簡單使用 typedef long byte_4
給已知數據類型long起個新的名字,叫做byte_4;

typedef  struct tag_my_struct
{
	int i_num;
	long l_length;
} my_struct;

第一步,定義一個新的結構類型
第二步,typedef爲這個新的結構起了一個名字,叫做my_struct

2、typedef與#define的問題

typedef char * p_str;
#define p_str char *;

上面兩種定義p_str數據類型的方法,一般來說typedef比#define好。
因爲#define只是替換,而typedef是起一個新的名字。例如

#define f(x) x*x;

就需要注意括號的問題,但是typedef不需要注意括號的問題。

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