今天我們繼續來學習C語言的入門知識點 11. 作用域規則 任何一種編程中,作用域是程式中定義的變數所存在的區域,超過該區域變數就不能被訪問。C 語言中有三個地方可以聲明變數: 在函數或塊內部的局部變數 在所有函數外部的全局變數 在形式參數的函數參數定義中 讓我們來看看什麼是局部變數、全局變數和形式參 ...
今天我們繼續來學習C語言的入門知識點
11. 作用域規則
任何一種編程中,作用域是程式中定義的變數所存在的區域,超過該區域變數就不能被訪問。C 語言中有三個地方可以聲明變數:
在函數或塊內部的局部變數
在所有函數外部的全局變數
在形式參數的函數參數定義中
讓我們來看看什麼是局部變數、全局變數和形式參數。
局部變數
在某個函數或塊的內部聲明的變數稱為局部變數。它們只能被該函數或該代碼塊內部的語句使用。局部變數在函數外部是不可知的。下麵是使用局部變數的實例。在這裡,所有的變數 a、b 和 c 是 main() 函數的局部變數。
void main(){
//局部變數
int a, b;
int c;
//初始化局部變數
a = 10;
b = 20;
c = a + b;
//%d:以十進位形式輸出帶符號整數(正數不輸出符號)
printf("values of a = %d,b = %d and c = %d \n", a, b, c);
}
輸出:values of a = 10,b = 20 and c = 30
全局變數
全局變數是定義在函數外部,通常是在程式的頂部。全局變數在整個程式生命周期內都是有效的,在任意的函數內部能訪問全局變數。
全局變數可以被任何函數訪問。也就是說,全局變數在聲明後整個程式中都是可用的。下麵是使用全局變數和局部變數的實例:
//全局變數聲明
int g;
void main(){
int a, b;
//初始化局部變數
a = 10;
b = 20;
//全部變數賦值
g = a + c;
printf("values of a = %d,bc = %d and g = %d \n", a, c, g);
}
輸出:values of a = 10,bc = 30 and g = 40
形式參數
函數的參數,形式參數,被當作該函數內的局部變數,如果與全局變數同名它們會優先使用。下麵是一個實例:
int sumA(int a, int b) {
printf("value of a in sum() = %d\n", a);
printf("value of b in sum() = %d\n", b);
return x + y;
}
void main(){
int a, b,c;
//初始化局部變數
a = 10;
b = 20;
c = sumA(a, b);
printf("value of c in main() = %d\n", c);
}
輸出:value of a in main() = 30
全局變數和局部變數的區別
(1)全局變數保存在記憶體的全局存儲區中,占用靜態的存儲單元;
(2)局部變數保存在棧中,只有在所在函數被調用時才動態地為變數分配存儲單元。
初始化局部變數和全局變數的預設值
12. 數組
C 語言支持數組數據結構,它可以存儲一個固定大小的相同類型元素的順序集合。數組是用來存儲一系列數據,但它往往被認為是一系列相同類型的變數。
數組的聲明並不是聲明一個個單獨的變數,比如 number0、number1、...、number99,而是聲明一個數組變數,比如 numbers,然後使用 numbers[0]、numbers[1]、...、numbers[99] 來代表一個個單獨的變數。數組中的特定元素可以通過索引訪問。
所有的數組都是由連續的記憶體位置組成。最低的地址對應第一個元素,最高的地址對應最後一個元素。
聲明數組
在 C 中要聲明一個數組,需要指定元素的類型和元素的數量,如下所示:
type arrayName [ arraySize ];
這叫做一維數組。arraySize 必須是一個大於零的整數常量,type 可以是任意有效的 C 數據類型。例如,要聲明一個類型為 double 的包含 10 個元素的數組 balance,聲明語句如下:
double balance[10];
初始化數組
void main(){
double balance[10] = {1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2.0}
}
大括弧 { } 之間的值的數目不能大於我們在數組聲明時在方括弧 [ ] 中指定的元素數目。
如果您省略掉了數組的大小,數組的大小則為初始化時元素的個數。因此,如果:
void main(){
double balance[] = {1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2.0}
}
您將創建一個數組,它與前一個實例中所創建的數組是完全相同的。下麵是一個為數組中某個元素賦值的實例:
balance[1] = 50.5;
訪問數組元素
//跟 Java 一樣
double value = balance[1]
例子:
void main() {
//定義一個長度為 10 的整數數組
int n[10];
int i, j;
//初始化數組元素
for (i = 0; i < 10; i++) {
n[i] = 2 * i;
}
//輸出元素中的數據
for (int k = 0; k < 10; ++k) {
printf("Element[%d] = %d \n", k, n[k]);
}
//總的大小除以其中一個大小就得到了 數組長度
printf("整數數組 n 的長度: %d \n", sizeof(n) / sizeof(n[0]));
//輸出元素中的數據
for (int k = 0; k < sizeof(n) / sizeof(n[0]); ++k) {
printf("Element[%d] = %d \n", k, n[k]);
}
}
輸出:
Element[0] = 0
Element[1] = 2
Element[2] = 4
Element[3] = 6
Element[4] = 8
Element[5] = 10
Element[6] = 12
Element[7] = 14
Element[8] = 16
Element[9] = 18
整數數組 n 的長度: 10
Element[0] = 0
Element[1] = 2
Element[2] = 4
Element[3] = 6
Element[4] = 8
Element[5] = 10
Element[6] = 12
Element[7] = 14
Element[8] = 16
Element[9] = 18
C 中數組詳解
在 C 中,數組是非常重要的,我們需要瞭解更多有關數組的細節。下麵列出了 C 程式員必須清楚的一些與數組相關的重要概念:
13. 枚舉
枚舉是 C 語言中的一種基本數據類型,它可以讓數據更簡潔,更易讀。
枚舉語法定義格式為:
enum 枚舉名 {枚舉元素1,枚舉元素2,……};
接下來我們舉個例子,比如:一星期有 7 天,如果不用枚舉,我們需要使用 #define 來為每個整數定義一個別名:
#define MON 1
#define TUE 2
#define WED 3
#define THU 4
#define FRI 5
#define SAT 6
#define SUN 7
這個看起來代碼量就比較多,接下來我們看看使用枚舉的方式:
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
這樣看起來是不是更簡潔了。
**註意:**第一個枚舉成員的預設值為整型的 0,後續枚舉成員的值在前一個成員上加 1。我們在這個實例中把第一個枚舉成員的值定義為 1,第二個就為 2,以此類推。
可以在定義枚舉類型時改變枚舉元素的值:
enum season {spring, summer=3, autumn, winter};
沒有指定值的枚舉元素,其值為前一元素加 1。也就說 spring 的值為 0,summer 的值為 3,autumn 的值為 4,winter 的值為 5
枚舉變數的定義
前面我們只是聲明瞭枚舉類型,接下來我們看看如何定義枚舉變數。
我們可以通過以下三種方式來定義枚舉變數
1、先定義枚舉類型,再定義枚舉變數
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day;
2、定義枚舉類型的同時定義枚舉變數
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
3、省略枚舉名稱,直接定義枚舉變數
enum
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
14. 指針
學習 C 語言的指針既簡單又有趣。通過指針,可以簡化一些 C 編程任務的執行,還有一些任務,如動態記憶體分配,沒有指針是無法執行的。所以,想要成為一名優秀的 C 程式員,學習指針是很有必要的。
正如您所知道的,每一個變數都有一個記憶體位置,每一個記憶體位置都定義了可使用連字型大小(&)運算符訪問的地址,它表示了在記憶體中的一個地址。請看下麵的實例,它將輸出定義的變數地址:
void main(){
int var1;
char var2[10];
//%p : 輸出指針地址
printf("var1 變數的地址:%p \n", &var1);
printf("var2 變數的地址:%p \n", &var2);
}
輸出:
var1 變數的地址:0x7ffee7e976b8
var2 變數的地址:0x7ffee7e976be
通過上面的實例,我們瞭解了什麼是記憶體地址以及如何訪問它。接下來讓我們看看什麼是指針。
什麼是指針?
指針是一個變數,其值為另一個變數的地址,即記憶體位置的直接地址。就像其他變數或常量一樣,您必須在使用指針存儲其他變數地址之前,對其進行聲明。指針變數聲明的一般形式為:
type *var-name
在這裡,type 是指針的基類型,它必須是一個有效的 C 數據類型,var-name 是指針變數的名稱。用來聲明指針的星號 * 與乘法中使用的星號是相同的。但是,在這個語句中,星號是用來指定一個變數是指針。以下是有效的指針聲明:
int *i; //一個整型的指針
double *d;//double 型指針
float *f;//浮點型指針
char *ch//字元型指針
所有實際數據類型,不管是整型、浮點型、字元型,還是其他的數據類型,對應指針的值的類型都是一樣的,都是一個代表記憶體地址的長的十六進位數。
不同數據類型的指針之間唯一的不同是,指針所指向的變數或常量的數據類型不同。
如何使用指針?
使用指針時會頻繁進行以下幾個操作:定義一個指針變數、把變數地址賦值給指針、訪問指針變數中可用地址的值。這些是通過使用一元運算符 ***** 來返回位於操作數所指定地址的變數的值。下麵的實例涉及到了這些操作:
例子:
//如何使用指針
int var = 66;//實際變數的聲明
int *ip;//指針變數的聲明
ip = &var; //指針變數中存儲 var 的地址
printf("var 的地址 : %p \n", var);
//在指針變數中存儲的地址
printf("ip 的地址:%p \n", ip);
//使用指針訪問地址
printf("ip 指針對應的地址:%p \n", *ip);
//使用指針訪問地址對應的值
printf("ip 指針對應的地址:%d \n", *ip);
輸出:
var 的地址 : 0x42
ip 的地址:0x7ffee96eb6b4
ip 指針對應的地址:0x42
ip 指針對應的地址:66
C 中的 NULL 指針
在變數聲明的時候,如果沒有確切的地址可以賦值,為指針變數賦一個 NULL 值是一個良好的編程習慣。賦為 NULL 值的指針被稱為空指針。
NULL 指針是一個定義在標準庫中的值為零的常量。請看下麵的程式:
void main(){
//賦值一個 NULL 指針
int *ptr = NULL;
printf("ptr 的地址是: %p \n", ptr);
//檢查一個空指針
if (ptr) printf("如果 ptr 不是空指針,則執行"); else printf("如果 ptr 是空指針,則執行");
}
輸出:ptr 的地址是: 0x0 ptr 是空指針
C 指針詳解
在 C 中,有很多指針相關的概念,這些概念都很簡單,但是都很重要。下麵列出了 C 程式員必須清楚的一些與指針相關的重要概念:
15. 函數指針與回調函數
函數指針是指向函數的指針變數。
通常我們說的指針變數是指向一個整型、字元型或數組等變數,而函數指針是指向函數。
函數指針可以像一般函數一樣,用於調用函數、傳遞參數。
函數指針變數的聲明:
typedef int (*fun_ptr)(int,int)//聲明一個指向同樣參數,返回值得函數指針類型
回調函數
函數指針變數可以作為某個函數的參數來使用的,回調函數就是一個通過函數指針調用的函數。
簡單講:回調函數是由別人的函數執行時調用你實現的函數。
例子:
例子中 populate_array 函數定義了三個參數,其中第三個參數是函數的指針,通過該函數來設置數組的值。
實例中我們定義了回調函數 getNextRandomValue,它返回一個隨機值,它作為一個函數指針傳遞給 populate_array 函數。
populate_array 將調用 10 次回調函數,並將回調函數的返回值賦值給數組。
#include <stdlib.h>
#include <stdio.h>
//回調函數
void populate_array(int *array, size_t arraySize, int(*getNextValue)(void)) {
printf("array 地址:%p \n", array);
for (size_t i = 0; i < arraySize; i++) {
array[i] = getNextValue();
printf(" array[%d] ,存儲值:%d \n", i, array[i]);
}
}
//獲取一個隨機數
int getNextRandomValue(void) {
return rand();
}
void main() {
//回調函數
int array[10];
printf("Int array 地址:%p \n", array);
populate_array(array, sizeof(array)/sizeof(array[0]), getNextRandomValue);
for (int i = 0; i < sizeof(array)/sizeof(array[0]); ++i) {
printf(" array[%d] , 對應值為:%d \n", i, array[i]);
}
}
輸出:
Int array 地址:0x7ffeebf1a650
array 地址:0x7ffeebf1a650
array[0] ,存儲值:16807
array[1] ,存儲值:282475249
array[2] ,存儲值:1622650073
array[3] ,存儲值:984943658
array[4] ,存儲值:1144108930
array[5] ,存儲值:470211272
array[6] ,存儲值:101027544
array[7] ,存儲值:1457850878
array[8] ,存儲值:1458777923
array[9] ,存儲值:2007237709
array[0] , 對應值為:16807
array[1] , 對應值為:282475249
array[2] , 對應值為:1622650073
array[3] , 對應值為:984943658
array[4] , 對應值為:1144108930
array[5] , 對應值為:470211272
array[6] , 對應值為:101027544
array[7] , 對應值為:1457850878
array[8] , 對應值為:1458777923
array[9] , 對應值為:2007237709
16. 字元串
在 C 語言中,字元串實際上是使用null字元 '\0' 終止的一維字元數組。因此,一個以 null 結尾的字元串,包含了組成字元串的字元。
下麵的聲明和初始化創建了一個 "Hello" 字元串。由於在數組的末尾存儲了空字元,所以字元數組的大小比單詞 "Hello" 的字元數多一個。
char ch[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
也可以使用以下簡寫模式:
char ch[6] = "Hello"
字元串在 C/C++ 中記憶體表示:
其實,您不需要把 null 字元放在字元串常量的末尾。C 編譯器會在初始化數組時,自動把 '\0' 放在字元串的末尾。讓我們嘗試輸出上面的字元串:
void main(){
//定義一個 char 數組
char string[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
//簡寫
char string2[6] = "Hello";
//%s:輸出字元串
printf("string message : %s\n", string);
}
輸出:string message : Hello
C 中對字元串操作的 API
17. 結構體
C 數組允許定義可存儲相同類型數據項的變數,結構是 C 編程中另一種用戶自定義的可用的數據類型,它允許您存儲不同類型的數據項。
結構用於表示一條記錄,假設您想要跟蹤圖書館中書本的動態,您可能需要跟蹤每本書的下列屬性:
(1)Title
(2)Author
(3)Subject
(4)Book ID
定義結構
為了定義結構,您必須使用struct語句。struct 語句定義了一個包含多個成員的新的數據類型,struct 語句的格式如下:
struct name{
member-list;
member-list;
...
}name_tag,
name是結構的標簽。
member-list是標準的變數定義,比如 int i;或者 float f,或者其它有效的變數定義。
name_tag結構變數,定義在結構的末尾,最後一個分號之前,你可以指定一個或多個結構變數,下麵是聲明 Book 的結構方式:
struct Books{
char title[50];
char author[50];
char subject[100];
int book_id;
} book;
註意:在定義結構體的時候name、member-list、name_tag 這 3 部分至少要出現 2 個。
結構體變數的初始化
和其它類型變數一樣,在初始化的時候可以指定初始值。
//定義一個 Books 結構,類似於 Java 中的數據 bean
struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
double rmb;
} book = {"Java", "Android", "C 語言", 666, 55.5};
void main(){
//列印 Books
printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\nrmb: %f\n", book.title,
book.author, book.subject, book.book_id, book.rmb);
}
輸出:
title : Java
author: Android
subject: C 語言
book_id: 666
rmb: 55.500000
訪問結構成員
struct Books2 {
char title[50];
char author[50];
char subject[100];
int book_id;
};
void main(){
//訪問 Books2 結構成員
struct Books2 Books2A;//聲明 Books2A 類型為 Books2
struct Books2 Books2B;//聲明 Books2B 類型為 Books2
//Books2A 詳述
strcpy(Books2A.title, "C Plus");
strcpy(Books2A.author, "Nuha Ali");
strcpy(Books2A.subject, "C");
Books2A.book_id = 666888;
//Books2B 詳述
strcpy(Books2B.title, "C++ Plus");
strcpy(Books2B.author, "DevYK");
strcpy(Books2B.subject, "C++");
Books2B.book_id = 666999;
// 輸出 Book1 信息
printf("Book 1 title : %s\n", Books2A.title);
printf("Book 1 author : %s\n", Books2A.author);
printf("Book 1 subject : %s\n", Books2A.subject);
printf("Book 1 book_id : %d\n", Books2A.book_id);
// 輸出 Book2 信息
printf("Book 2 title : %s\n", Books2B.title);
printf("Book 2 author : %s\n", Books2B.author);
printf("Book 2 subject : %s\n", Books2B.subject);
printf("Book 2 book_id : %d\n", Books2B.book_id);
}
輸出:
Book 1 title : C Plus
Book 1 author : Nuha Ali
Book 1 subject : C
Book 1 book_id : 666888
Book 2 title : C++ Plus
Book 2 author : DevYK
Book 2 subject : C++
Book 2 book_id : 666999
結構作為函數參數
//函數聲明
void printBook(struct Books2 books2);
void main(){
//訪問 Books2 結構成員
struct Books2 Books2A;//聲明 Books2A 類型為 Books2
struct Books2 Books2B;//聲明 Books2B 類型為 Books2
//Books2A 詳述 ,將 CPlus copy 到 title 中
strcpy(Books2A.title, "C Plus");
strcpy(Books2A.author, "Nuha Ali");
strcpy(Books2A.subject, "C");
Books2A.book_id = 666888;
//Books2B 詳述
strcpy(Books2B.title, "C++ Plus");
strcpy(Books2B.author, "DevYK");
strcpy(Books2B.subject, "C++");
Books2B.book_id = 666999;
// 輸出 Book1 信息
printf("Book 1 title : %s\n", Books2A.title);
printf("Book 1 author : %s\n", Books2A.author);
printf("Book 1 subject : %s\n", Books2A.subject);
printf("Book 1 book_id : %d\n", Books2A.book_id);
// 輸出 Book2 信息
printf("Book 2 title : %s\n", Books2B.title);
printf("Book 2 author : %s\n", Books2B.author);
printf("Book 2 subject : %s\n", Books2B.subject);
printf("Book 2 book_id : %d\n", Books2B.book_id);
printf("\n\n\n");
//結構作為函數參數
printBook(Books2A);
printBook(Books2B);
}
void printBook(struct Books2 book) {
printf("Book title : %s\n", book.title);
printf("Book author : %s\n", book.author);
printf("Book subject : %s\n", book.subject);
printf("Book book_id : %d\n", book.book_id);
}
輸出:
Book 1 title : C Plus
Book 1 author : Nuha Ali
Book 1 subject : C
Book 1 book_id : 666888
Book 2 title : C++ Plus
Book 2 author : DevYK
Book 2 subject : C++
Book 2 book_id : 666999
Book title : C Plus
Book author : Nuha Ali
Book subject : C
Book book_id : 666888
Book title : C++ Plus
Book author : DevYK
Book subject : C++
Book book_id : 666999
指向結構的指針
您可以定義指向結構的指針,方式與定義指向其他類型變數的指針相似,如下所示:
struct Books *struct_pointer;
現在,您可以在上述定義的指針變數中存儲結構變數的地址。為了查找結構變數的地址,請把 & 運算符放在結構名稱的前面,如下所示:
struct_pointer = &Book1;
為了使用指向該結構的指針訪問結構的成員,您必須使用 -> 運算符,如下所示:
struct_pointer->title;
例子:
//定義指向結構的指針
void printBookZZ(struct Books2 *books2);
void main(){
//訪問 Books2 結構成員
struct Books2 Books2A;//聲明 Books2A 類型為 Books2
struct Books2 Books2B;//聲明 Books2B 類型為 Books2
//Books2A 詳述 ,將 CPlus copy 到 title 中
strcpy(Books2A.title, "C Plus");
strcpy(Books2A.author, "Nuha Ali");
strcpy(Books2A.subject, "C");
Books2A.book_id = 666888;
//Books2B 詳述
strcpy(Books2B.title, "C++ Plus");
strcpy(Books2B.author, "DevYK");
strcpy(Books2B.subject, "C++");
Books2B.book_id = 666999;
//通過記憶體地址傳遞信息,為了查找結構變數的地址,請把 & 運算符放在結構名稱的前面
printBookZZ(&Books2A);
printBookZZ(&Books2B);
}
/**
* 為了使用指向該結構的指針訪問結構的成員,您必須使用 -> 運算符,如下所示:
* @param book
*/
void printBookZZ(struct Books2 *book) {
printf("Book -> title : %s\n", book->title);
printf("Book -> author : %s\n", book->author);
printf("Book -> subject : %s\n", book->subject);
printf("Book -> book_id : %d\n", book->book_id);
}
位域
有些信息在存儲時,並不需要占用一個完整的位元組,而只需占幾個或一個二進位位。例如在存放一個開關量時,只有 0 和 1 兩種狀態,用 1 位二進位即可。為了節省存儲空間,並使處理簡便,C 語言又提供了一種數據結構,稱為"位域"或"位段"。
所謂"位域"是把一個位元組中的二進位劃分為幾個不同的區域,並說明每個區域的位數。每個域有一個功能變數名稱,允許在程式中按功能變數名稱進行操作。這樣就可以把幾個不同的對象用一個位元組的二進位位域來表示。
典型的實例:
(1)用 1 位二進位存放一個開關量時,只有 0 和 1 兩種狀態。
(2)讀取外部文件格式——可以讀取非標準的文件格式。
位域ed 定義:
struct 位域結構名稱{
位域列表
};
位域列表的形式為:類型說明符 位功能變數名稱:位域長度
例:
struct bean {
int a:8;
int b:4;
int c:4;
}data;
說明 data 為 bean 變數,共占 2個位元組。其中位域 a 占 8 位,位域 b 占 4 位,位域 c 占 4 位。
註意:
一個位域存儲在同一個位元組中,如一個位元組所剩空間不夠存放另一位域時,則會從下一單元起存放該位域。也可以有意使某位域從下一單元開始。例如:
struct bean{
unsigned a:4;
unsigned :4;//空域
unsigned b:4;//從下一個單元開始存放
unsigned c:4;
}
在這個位域定義中共占用 2 個位元組,a 占第一位元組的 4 位,後 4 位填 0 表示不使用,b 從第二位元組開始,占用 4 位,c 占用 4 位。
由於位域不允許跨兩個位元組,因此位域的長度不能大於一個位元組的長度,也就是說不能超過8位二進位。如果最大長度大於電腦的整數字長,一些編譯器可能會允許域的記憶體重疊,另外一些編譯器可能會把大於一個域的部分存儲在下一個字中。
位域可以是無名位域,這時它只用來作填充或調整位置。無名的位域是不能使用的。例如:
struct k{
int a:1;
int :2; /* 該 2 位不能使用 */
int b:3;
int c:2;
};
從以上分析可以看出,位域在本質上就是一種結構類型,不過其成員是按二進位分配的。
位域的使用
位域的使用和結構成員的使用相同,其一般形式為:
(1)位域變數名.位功能變數名稱
(2)位域變數名->位功能變數名稱
位域允許用各種格式輸出。
18. 共用體
共用體是一種特殊的數據類型,允許您在相同的記憶體位置存儲不同的數據類型。您可以定義一個帶有多成員的共用體,但是任何時候只能有一個成員帶有值。共用體提供了一種使用相同的記憶體位置的有效方式。
定義共同體
為了定義共用體,您必須使用union語句,方式與定義結構類似。union 語句定義了一個新的數據類型,帶有多個成員。union 語句的格式如下:
union [union tag]
{
member definition;
member definition;
...
member definition;
}[one or more union variables];
union tag是可選的,每個 member definition 是標準的變數定義,比如 int i; 或者 float f; 或者其他有效的變數定義。在共用體定義的末尾,最後一個分號之前,您可以指定一個或多個共用體變數,這是可選的。下麵定義一個名為 Data 的共用體類型,有三個成員 i、f 和 str:
union Data
{
int i;
float f;
char str[20];
}
現在,Data類型的變數可以存儲一個整數、一個浮點數,或者一個字元串。這意味著一個變數(相同的記憶體位置)可以存儲多個多種類型的數據。您可以根據需要在一個共用體內使用任何內置的或者用戶自定義的數據類型。
共用體占用的記憶體應足夠存儲共用體中最大的成員。例如,在上面的實例中,Data 將占用 20 個位元組的記憶體空間,因為在各個成員中,字元串所占用的空間是最大的。下麵的實例將顯示上面的共用體占用的總記憶體大小:
union Data {
int i;
float f;
char str[20];
};
void main(){
union Data data;
printf("Memory size occupied by data: %d\n", sizeof(data));
}
輸出:Memory size occupied by data: 20
訪問共同體成員
為了訪問共用體的成員,我們使用成員訪問運算符(.)。成員訪問運算符是共用體變數名稱和我們要訪問的共用體成員之間的一個句號。您可以使用 union 關鍵字來定義共用體類型的變數。下麵的實例演示了共用體的用法:
union Data {
int i;
float f;
char str[20];
};
void main() {
//1. 訪問共同體 no
data.i = 10;
data.f = 1314.520;
strcpy(data.str,"C/C++");
printf( "data.i : %d\n", data.i);
printf( "data.f : %f\n", data.f);
printf( "data.str : %s\n", data.str);
printf("\n\n\n");
//2. 訪問共同體 yes
data.i = 10;
printf( "data.i : %d\n", data.i);
data.f = 1314.520;
printf( "data.f : %f\n", data.f);
strcpy(data.str,"C/C++");
printf( "data.str : %s\n", data.str);
}
輸出:
data.i : 725823299
data.f : 0.000000
data.str : C/C++
data.i : 10
data.f : 1314.520020
data.str : C/C++
在這裡,我們可以看到上面註釋 1 共用體的 i 和 f 成員的值有損壞,因為最後賦給變數的值占用了記憶體位置,這也是 str 成員能夠完好輸出的原因。我們看註釋 2 ,這次我們在同一時間只使用一個變數成員,所以都能完好輸出。
19. 位域
參考 17.(位域的介紹)
20. typedef
C 語言提供了 typedef 關鍵字,您可以使用它來為類型取一個新的名字。下麵的實例為單位元組數字定義了一個術語 BYTE:
typedef unsigned char BYTE;
在這個類型定義之後,標識符 BYTE 可作為類型 unsigned char 的縮寫,例如:
BYTE b1, b2;
按照慣例,定義時會大寫字母,以便提醒用戶類型名稱是一個象徵性的縮寫,但您也可以使用小寫字母,如下:
typedef unsigned char byte;
您也可以使用 typedef 來為用戶自定義的數據類型取一個新的名字。例如,您可以對結構體使用 typedef 來定義一個新的數據類型名字,然後使用這個新的數據類型來直接定義結構變數,如下:
typedef struct Books {
char title[50];
char author[50];
char subject[50];
int book_id;
} Book;
#define TRUE 1
#define FALSE 0
void main(){
Book book;
strcpy( book.title, "C 教程");
strcpy( book.author, "Runoob");
strcpy( book.subject, "編程語言");
book.book_id = 12345;
printf( "書標題 : %s\n", book.title);
printf( "書作者 : %s\n", book.author);
printf( "書類目 : %s\n", book.subject);
printf( "書 ID : %d\n", book.book_id);
printf( "TRUE 的值: %d\n", TRUE);
printf( "FALSE 的值: %d\n", FALSE);
}
輸出:
書標題 : C 教程
書作者 : Runoob
書類目 : 編程語言
書 ID : 12345
TRUE 的值: 1
FALSE 的值: 0
typedef vs define
define是 C 指令,用於為各種數據類型定義別名,與typedef類似,但是它們有以下幾點不同:
(1)typedef僅限於為類型定義符號名稱,#define不僅可以為類型定義別名,也能為數值定義別名,比如您可以定義 1 為 ONE。
(2)typedef是由編譯器執行解釋的,#define語句是由預編譯器進行處理的。
例子可以參考上面是 #define 使用。
本節知識將會以分節的形式向大家展示,又想要學習C語言的小伙伴可以關註筆者!一起來加油呀~
自學C/C++編程難度很大,不妨和一些志同道合的小伙伴一起學習成長!
C語言C++編程學習交流圈子,【點擊進入】微信公眾號:C語言編程學習基地
有一些源碼和資料分享,歡迎轉行也學習編程的伙伴,和大家一起交流成長會比自己琢磨更快哦!