⚠️數組名和指針常量類似,但並非指針!(也由於是指針常量,因此數組名也是un-assignable的)程序員
只有兩種狀況下數組名不用指針常量來表示:數組
sizeof
:對數組名使用sizeof
將返回整個數組的長度(以字節爲單位)&
取址:獲得指向數組的指針(而不是指向第一個元素的指針的指針)經過指針來訪問數組中的元素可能比用下標的效率更高,由於經過下標訪問數組老是要作如下運算:安全
arr + (index * sizeof(elementOfArr)) // 此處的乘法沒法避免
而某些狀況(主要是循環體)下,對該偏移值的計算將被編譯器優化爲純加法(一個在現代基本沒什麼用的知識。。):函數
for (int* arrPtr = arr; arrPtr < arr + 10; arrPtr ++) { ... } // arrPtr++ 所加上的值只須要計算第一次
指針不只在作加法時須要考慮元素大小,兩指針相減的結果也會被經過除法轉換爲元素個數(單純比較是否相等則不須要)優化
⚠️函數接收多維數組作參數時,必須指定除第一維外其餘全部維度的大小,不然尋址時函數沒法計算偏移量指針
void func(int x, int y, int arr[][]) { // 未指定第二維大小 printf("%d", arr[x][y]); // 訪問arr的第(x * sizeOfRow + y)個元素,但函數不知道sizeOfRow }
字符數組的初始化與字符串常量code
char msg[] = "Hello"; char *msg = "Hello"; // 字符串常量是read only的
⚠️聲明一個指向數組的指針:內存
int arr[3][10]; int (*arrPtr)[10] = arr; // arrPtr是一個指向擁有10個int元素的數組的指針,arrPtr + 1將指向下一行的10個int的開始
多維數組能夠經過如下形式傳遞給函數:element
void func(int (*mat)[10]); void func(int mat[][10]);
但當多維數組各維度的大小並不總固定時(個人最愛~),能夠經過如下方式將它以一維數組的形式傳遞給函數:字符串
int *arrPtr = &arr[0][0]; int *arrPtr = arr[0];
可是如下這種聲明可能會致使嚴重的問題:
int arr[3][10]; int (*arrPtr)[] = arr; // 此時指針arrPtr不知道數組arr的size信息,它將會把0看成arr的二維寬度(而不是3)
下標引用[]
的優先級高於間接訪問*
int *p[10]; // p被聲明爲擁有10個int*元素的數組
strlen
的返回值爲size_t
,是一個unsigned值(永遠大於等於0)NULL
結尾(在必要的狀況下)且大小合適strcpy
、strcat
等函數會將目標字符串的地址做爲返回值,這個特性令它們能夠被嵌套使用strerror
函數接收一個錯誤代碼,並返回一個指向描述該錯誤的字符串的指針⚠️struct
(忽然發現我根本不知道struct
的標準語法。。)
struct Tag { // struct關鍵字和花括號之間的部分爲該struct的標籤,爲可選部分,這樣聲明的結構體每次使用時都要加上struct關鍵字 int val; char name[10]; } tag1, *tag2; // 此處能夠順便聲明該類型的變量,但此後聲明新變量必須使用struct Tag tag;,由於Tag實際上不是一種類型 typedef struct { // 將這種struct定義爲一種類型,所以之後使用時不用加struct關鍵字 int val; char name[10]; } Tag; // 之後能夠直接使用Tag tag;來聲明新變量
因此struct {};
纔是一個總體,它能夠被加上標籤做爲記號,也能夠被typedef
爲一種新的結構體
(我居然一直覺得是在主函數外聲明新變量就要在類型前加struct關鍵字。。因此我這麼久都寫的啥==)
點操做符.
的優先級高於間接訪問*
,因此C提供了->
來更爲方便地經過結構體指針訪問它的成員(原來如此!!)
(*ptr).val = 0; ptr->val = 0; // 顯然下面這種寫法更爲直觀
以及優先級上->
高於.
高於*
:
*p->a.b; // 將會從p指向的結構體中取出a結構體的b成員並對它進行間接訪問,即*((p->a).b)
不完整聲明:使兩個(或更多)結構體能夠互相包含對方的指針
struct B; // 提早聲明B的存在 struct A { struct B *bPtr; ... }; struct B { struct A *aPtr; ... };
typedef struct foo foo;
是合法的了,可是這種寫法在以前的編譯器上(可能!)會出現兼容性問題⚠️C中的空結構體爲undefined行爲(但C++容許結構體爲空),以及結構體實際上容許嵌套聲明,所以可能無心間聲明空結構體:
struct A { struct B { // 此處實際只是struct B的定義,而非變量聲明。所以沒有其餘成員的struct A實際爲空結構體 int id; ... }; };
sizeof
將返回它實際佔用的字節數,包括那些爲了邊界對其而跳過的字節。所以要肯定一個成員對於結構體起始位置的實際偏移量,應使用offset(type, member)
宏(位於stddef.h
),type
爲結構類型,member
爲該成員的名字。這個宏將返回一個size_t
值
位段(bit filed):(本質上是不可移植的。。hmmm)
int
或unsigned int
類型struct CHAR { unsigned ch : 7; unsigned font : 16; unsigned size : 19; }; struct CHAR c1;
⚠️聯合(union
):(經過訪問不一樣的聯合成員,內存中的同一塊內容能夠被解釋爲不一樣的東西)
與struct
類似,union
也容許嵌套聲明
struct POINTER { // 實際包含兩個變量,type和ptr enum {INT, FLOAT, STRING} type; union { int *i; float *f; char *s; } ptr; // 匿名union };
初始化:初始值必須爲第一個成員的類型,且必須用花括號包圍
union { int a; float b; } x = { 5 }; // 其餘類型的初始值(若是可能的話)將會被轉換爲一個int賦值給x.a