學習時間:8天;
學習範圍:《The C Programming Language》/Fishc工作室視訊/《C語言經典程式設計282例》;
函數
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//參數傳值調用
在C語言中,被調函數不能直接修改主調函數中變量的值,而隻能修改函數私有的臨時副本的值
必要時,也能夠修改主調函數中的變量。需要向被調用函數提供待設定值的變量的位址(指針)。被調用函數則需要将對應的參數聲明為指針類型,并通過它間接通路變量。
如果是數組參數,當把數組名用作參數時,傳遞給函數的值是數組起始元素的位置或位址,并不複制數組元素的本身。在被調函數中,可以通過數組下标通路或修改數組元素的值。

————————————————————————————————————————————
變量
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
自動變量(局部變量) //在一個函數開頭或段開頭處說明的變量
作用域:
僅在定義它的函數内;
初始化:
不自動賦初值,使用前需要指派;
值的保持:
随函數的引用而存在和消失,不保持值;
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
外部變量(全程變量) //在函數外部定義的變量
優勢:解決函數單獨編譯的協調;與變量初始化有關;外部變量的值是永久的;解決資料共享;
作用域:整個程式;
初始化:0;
值的保持:永久保持
特點:
- c程式可以分别放在幾個檔案上,每個檔案可以作為一個編譯機關分别進行編譯。外部變量隻需在某個檔案上定義一次,其它檔案若要引用此變量時,應用extern加以說明(外部變量定義時不必加extern關鍵字)
- 在同一檔案中,若前面的函數要引用後面定義的外部(在函數之外)變量時,在函數裡加extern加以說明。
舉例:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
靜态變量 - 内部 //在局部變量前加上static
作用域:僅在定義它的函數内;
初始化:0;
值的保持:當函數執行完,傳回調用點時,該變量并不撤銷,再次調用時,其值将繼續存在;
特點:采用靜态存貯配置設定(由編譯程式在編譯時配置設定,而一般的自動變量和函數形參均采用動态存貯配置設定,即在運作時配置設定空間);
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
靜态變量 - 外部 //在函數外部定義的變量前加static
優勢:可以實作資料隐藏
作用域:定義它的檔案(該檔案的私有變量),其他檔案上的函數不允許通路;
初始化:0;
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
寄存器變量 //隻有自動(局部)變量和函數參數才能進一步指定為寄存器存貯類
特點:
- 使用register變量可以提高存取速度,但寄存器變量的數目依賴于具體機器,聲明多了也隻有前幾個有效。
- 隻限于int,char,short ,unsigned和指針類型用寄存類。
- 不能對register變量取位址(即&操作)
————————————————————————————————————————————
常量
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
enum 枚舉常量
不同枚舉中的名字必須互不相同,同一枚舉中不同的名字可以是用相同的值;相對#define來說,優勢在于常量值可以自動生成
舉例:
-
enum week {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};
/*
week是新的資料類型,源于int,此時:
MONDAY = 0
TUESDAY = 1
WEDNESDAY = 2
THURSDAY = 3
FRIDAY = 4
SATURDAY = 5
SUNDAY = 6
*/
-
enum week {MONDAY = 1, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};
/*
MONDAY = 1
TUESDAY = 2
WEDNESDAY = 3
THURSDAY = 4
FRIDAY = 5
SATURDAY = 6
SUNDAY = 7
*/
-
enum escapes {BELL = \'\a\', BACKSPACE = \'\b\', TAB = \'\t\', NEWLINE = \'n\', VTAB = \'\v\', RETURN = \'\r\'};
/*
BELL = \'\a\',
BACKSPACE = \'\b\',
TAB = \'\t\',
NEWLINE = \'n\',
VTAB = \'\v\',
RETURN = \'\r\'
*/
————————————————————————————————————————————
關鍵字
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const //限定符,通過 const 對變量進行限定後,無法修改其值,數組同理。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
register //請求編譯器盡可能的将變量存在CPU内部寄存器中,而不是通過記憶體尋址通路,以提高效率。
- register變量必須是能被CPU所接受的類型。這通常意味着register變量必須是一個單個的值,并且長度應該小于或者等于整型的長度。
- 不能通過 & 來取位址
————————————————————————————————————————————
數組
注意:數組名即數組的第一個元素的位址,是以使用scanf函數的時候可以不使用 &
// 調用數組a[i]的值時也可以寫成 *(a+i) 的形式,編譯的過程實際上先将其轉換成 *(a+i) 再求值。是以 a[i] 與 *(a+i) 是等價的,一個通過數組和下表實作的表達式可以通過指針可偏移量實作。注意:指針是一個變量, p=a或p++是合法的,但數組名不是變量,不可以執行上面的操作。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
字元數組 //scanf在讀取字元串的時候不使用取址操作符 &,同指針
char str[1000];
scanf("%s",str);
————————————————————————————————————————————
指針
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
位址運算符 & 隻能應用于記憶體中的對象(變量與數組元素)。它不能作用于表達式/常量/register類型的變量。
取值運算符 * ,在定義指針時表示定義的是個變量,在其他地方表示取指針變量的那個值
指針隻能指向某種特定類型的對象(例外情況:指向void類型的指針可以存放指向任何類型的指針,但它不能間接引用其自身)。
定義:
int a;
int *p;
p = &a;
scanf("%d",p);
printf("%d\n",*p);
//指向數組的普通指針(非數組指針)
#include <stdio.h>
int main(int argc, char const *argv[])
{
//char
char *p;//定義一個字元指針
char str[1000];//定義一個字元數組
scanf("%s", str);//字元數組輸入
p = str; //p指針指向str首位址
printf("%c %c\n", *p, *(p + 1)); //列印首位址的值與第二個位址的值
//int
int a[5] = {1, 2, 3, 4, 5};
int *q;
q = a;
//或寫成 int *q = a;
printf("%d %d\n", *q, *(q + 1));
return 0;
}
//指向指針的指針
定義:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 520;
int *p = #
int **pp = &p;//pp為指向指針的指針
return 0;
}
num | 520 | //int num中存放的值是520 | ||
p | &num | //指針p中存放的是num的位址 | ||
*p | num | 520 | //對指針p解引用得到&num存放的值 == 520 | |
pp | &p | //pp作為指向指針p的指針,存放的是p的位址 | ||
*pp | p | &num | //pp解引用得到的是 p == &num | |
**pp | *p | num | 520 | //對pp二層解引用得到的是 *p == num ==520 |
舉例:
#include <stdio.h>
int main(int argc, char const *argv[])
{
char *str[] = {"aaa", "bbb", "ccc", "ddd", "eee", "fff"};
//定義這個指針數組,此時指針數組存放的都是指針,字元串是指向字元的指針,數組每個元素都是指針,數組又可以用指針的方式來通路,是以我們可以用指向指針的指針來通路指針數組;
char **p;//定義指針p為指向指針的指針
char **str2[4];//str2數組中存放了4個指向指針的指針
p = &str[5];//p指向"fff",取址得到字元串"fff"的位址,此時p的一個指向字元指針的指針
str2[0] = &str[0];//str2數組中的指向指針的指針分别指向str數組中的指針
str2[1] = &str[1];
str2[2] = &str[2];
str2[3] = &str[3];
printf("%s\n", *p);
for (int i = 0; i < 4; ++i)
{
printf("%s\n", *str2[i]);
}
return 0;
}
//指向常量的指針 (指針本身可以被修改)
//const int *p可以指向const int 和int,但都不可以通過*p指派
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 520;
const int cnum = 820;
const int *p = &cnum;
printf("%d %d\n", &cnum, cnum);
printf("%d %d\n", p, *p);
p = #
//*p=1024; 此條無法通過編譯,*p read-only
num = 1024;
return 0;
}
//常量指針 (指針本身不可以被修改)
//int * const p定義了指向之後,無法指向其他量
//指向非常量的常量指針:指向的值可以被修改
//指向常量的常量指針:指向的值不可以被修改
//cmd下無法被編譯,unix和codeblocks可以編譯
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 520;
const int cnum = 820;
int *const p =&cnum;
*p = 1024;
printf("%d\n", *p );
//p=&cnum; 常量指針指向num後,不可以修改指向
return 0;
}
//指向"指向常量的常量指針"的指針
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 520;
const int cnum = 820;
const int *const p = &cnum;//定義一個指向常量的常量指針
const int *const *pp = &p;//定義一個指向"指向常量的常量指針"的指針
printf("%p %p\n", pp, &p);//列印p的位址,pp和&p相同
printf("%p %p %p\n", *pp, p, &cnum);//列印num的位址,*pp和p和&num相同
printf("%d %d %d\n", **pp, *p, cnum);//列印三者的值,820
return 0;
}
//參數指針
文法:double atof(char *);
/* atof的參數時一個指向char類型的指針*/
//void指針
将任意類型的指針可以轉換成為void指針;
但是void指針轉化為其他類型需要使用強制轉換;
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 1234;
int *pi = #
char *ps = "abcd";
void *pv;
pv = pi; //pv變成指向整型的指針;
printf("%p %p\n", pi, pv); //兩個指針分别的位址;
//printf("%d\n", *pv);//嘗試解引用一個void指針,不知道void類型的寬度,編譯器無法直接完成
printf("%d\n", *(int *)pv); //将pv強制轉換成為int類型的指針,并解引用
pv = ps;
printf("%p %p\n", ps, pv);
//printf("%s\n", pv);//字元串隻需要指向首字元的位址就可以了,但是這樣寫是不規範的
printf("%s\n",(char *)pv);
return 0;
}
//NULL指針 //空指針,#define NULL ((void *)0) 指針指向0
#include <stdio.h>
int main(int argc, char const *argv[])
{
int *p1; //p1成為迷途指針,懸空指針,即沒有指定的指針
int *p2 = NULL;
printf("%p %p\n", p1, p2);//p1位址随機,p2位址 00000000
printf("%d %d\n", *p1, *p2); //unix下編譯得到 *p1的值是随機的,*p2傳回segmentation fault,因為對NULL指針解引用是非法的,程式崩潰報錯
return 0;
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
指針運算
// *ip自增
文法:
- *ip += 1;
- ++*ip;
- (*ip)++;
//指針間指派
文法: iq = ip;
/* 如果iq和ip同樣是指向整型的指針,則将ip中的值copy到iq中,iq也将指向ip所指向的對象 */
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
指針傳遞
文法:
swap(a,b);
void swap(int *p,int *q)
特點:
- 調用方法傳參,僅改變指針p的位址 &p ;
- *p=12指派,僅改變記憶體塊的值 *p ;
- p=&a指向變量a,僅改變記憶體塊位置 p ;
- 調用方法結束,p的位址和記憶體塊都不變,但如果在調用過程中記憶體塊被修改,則值改變 ;
舉例:
#include <stdio.h>
void pointer(int *p)
{
printf("\nthe p is %p , addr is %d, *p is %d", p , &p, *p);
//Line 2 指針p傳入方法pointer中:在新的方法中生成了一個p的拷貝p1,新的位址 6356752,但值和指向的記憶體塊資料沒變
*p = 12;
printf("\nthe p is %p , addr is %d, *p is %d", p , &p, *p);
//Line 3 p1改變了其所指向的記憶體塊的值為12,記憶體塊 00F0FF2C 的值變成了22
int a = 11;
p = &a;
printf("\nthe p is %p , addr is %d, *p is %d", p , &p, *p);
//Line 4 p1的值指向a,即p1指向a 記憶體塊 0060FEFC,此時p1與p分别指向不同的記憶體塊了,不會互相影響
}
int main()
{
int b = 22;
int *p = &b;
printf("\nthe p is %p , addr is %d, *p is %d", p , &p, *p);
//Line 1 *p指向b的位址:獲得了p(記憶體塊資料)&p(位址)*p(值)
pointer(p);
printf("\nthe p is %p , addr is %d, *p is %d", p , &p, *p);
//Line 5 方法結束,調用方法結束後,p位址仍是調用前的位址,位址和值沒變(改變的僅僅是p的拷貝p1),但是p所指向的記憶體塊資料被p1所改變了,故*p為12
}
輸出:
其他傳遞方式(來源參考他人)
-
傳值方式 //和函數的值傳遞不是一個概念
文法:
swap(&a , &b);
void swap(int *a , int *b)
// 傳入的是變量a的位址 &a,函數接收到的是傳入位址的值
// 使用指針方式修改指向記憶體塊的值,傳入的是 a、b變量位址的拷貝。
注意:函數的值傳遞的方式是
swap(a , b);
void swap(int a , int b)
-
傳引用方式
文法:
swap(a,b);
void swap(int &p,int &q)
// 使用引用方式,傳入的是變量a、b,而不是拷貝,位址不變。
舉例:
#include <stdio.h>
void swap(int *a , int *b)
//方法一:傳值方式
{
printf("\n\n /*** Method 1 ***/");
printf("\n &a addr : %d , &b addr: %d", &a, &b);
printf("\n a memory : %d , b memory: %d", a, b);
printf("\n *a : %d , *b : %d", *a, *b);
int temp = *a;
*a = *b;
*b = temp;
}
void swap(int &a , int &b)
//方法二:傳引用方式
{
printf("\n\n /*** Method 2 ***/");
printf("\n &a addr : %d , &b addr: %d", &a , &b);
int temp = a;
a = b;
b = temp;
}
int main(int argc, char const *argv[])
{
int a = 3 , b = 5;
printf(" &a addr : %d , &b addr: %d", &a , &b);
printf("\n a : %d , b : %d", a , b);
swap(&a , &b); //方法一
printf("\n &a addr : %d , &b addr: %d", &a, &b);
printf("\n a : %d , b : %d", a , b);
swap(a , b); //方法二
printf("\n a : %d , b : %d\n", a , b);
return 0;
}
輸出:
————————————————————————————————————————————
數組指針和指針數組
注意:指派符号"="号兩邊的資料類型必須是相同的,通過這點可以排除在寫數組與指針時的錯誤;
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
數組指針 //指向數組的指針
注意:不同的資料類型占的位址數(位元組)不同,char:1,int:4,float:4,double:8
定義:
//一維
#include <stdio.h>
int main(int argc, char const *argv[])
{
//int,char同理
int temp[5] = {1, 2, 3, 4, 5};
int (*p)[5] = &temp;//指針p指向temp數組的首位址
int i;
for (i = 0; i < 5; ++i)
{
printf("%d ", *(*p + i));
//*p指向temp數組的首位址,*p+i代表後移幾個位元組的位址,*(*p+i)代表指向位址的值
}
return 0;
}
//二維,行指針
#include <stdio.h>
int main(int argc, char const *argv[])
{
int array[4][5] = {{1, 2, 3, 4, 5}, {11, 12, 13, 14, 15}, {21, 22, 23, 24, 25}, {31, 32, 33, 34, 35}, {41, 42, 43, 44, 45}};
int (*p)[5];//該語句是定義一個數組指針,指向含4個元素的一維數組。或直接寫成 int (*p)[5] = array;
p = array;//将該二維數組的首位址賦給p,也就是a[0]或&a[0][0]
//在這裡,數組指針p共有三個int指針,指派時意味着将p的三個指針指向 array第一行的三個元素;
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 5; ++j)
{
printf("%2d ", *(*(p + i) + j));
}
printf("\n");
}
return 0;
}
另一種表達方式:
...
int (*p)[4][5]=&array;
...
printf("%2d ", *(*(*p + i) + j));
...
//都表示數組中i行j列一個元素
*(p+i) == p[i]
*(*(p+i)+j) == p[i][j] == *(p[i]+j) == (*(p+i))[j]
*(*(*(p+i)+j)) == p[i][j][k]
p++;//該語句執行過後,也就是p=p+1;p跨過行a[0][]指向了行a[1][]
大小:在32位系統下永遠是4位元組
例1:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[10];
int *p, i;
for (i = 0; i < 10; ++i)
{
scanf("%d", &a[i]);
}
for (p = &a[0]; p < (a + 10); p++)
//p+i = a+i = &a[i] 這三者意思相同,是以循環結構使用 p < (a + 10),等價于 i < 10
{
printf("%d ", *p);
}
return 0;
}
例2:
#include <stdio.h>
int main()
{
//數組指針的大小必須與數組相同,否則編譯不通過,此處均為[5]
char a[5] = {\'a\', \'b\', \'c\', \'d\'};
//p1 和p2 都是數組指針,指向的是整個數組
//&a 是整個數組的首位址,a是數組首元素的首位址
char (*p1)[5] = &a;
//p1指向&a是對的,左右都是整個數組的首位址
char (*p2)[5] = a;
//p2指向a,編譯時會出現warming,但是"="兩邊的資料類型不一緻,左邊的是指向整個數組的指針,右邊的資料類型是指向單個字元的指針。
char (*p3)[5] = (char (*)[5])a;
//p2類型如果要正常使用的話則使用p3這種強制轉換,右邊強制轉換成為a整個數組的首位址
printf("a=%d\n", a);
printf("a=%c\n", a[0]);
printf("&a=%d\n", &a);
printf("p1=%c\n", **p1);
printf("p2=%c\n", **p2);
printf("p3=%c\n", **p3);
printf("p1+1=%c\n", **(p1 + 1));
printf("p2+1=%c\n", **(p2 + 1));
printf("p3+1=%c\n", **(p3 + 1));
return 0;
}
輸出:
出現的warming資訊為p2導緻,左右類型不一緻
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
指針數組 //用于存儲指針的數組
定義:
//一維
#include <stdio.h>
int main(int argc, char const *argv[])
{
//int
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int *p[5] = {&a, &b, &c, &d, &e};//數組裡面每一個元素都指向abcde的位址
for (int i = 0; i < 5; ++i)
{
printf("%d ", *p[i]);//循環列印每一個指針對應位址的值 *
}
//char
char *q[5] = {"aaa", "bbb", "ccc", "ddd", "eee"};
for (int j = 0; j < 5; ++j)
{
printf("%c ", *q[j]); //加*表示取出的是字元
}
for (int k = 0; k < 5; ++k)
{
printf("\n%s", q[k]);//不加*表示字元串
}
return 0;
}
//二維
int *p[3]; //定義的p數組中的三個元素都是int指針,分别是p[0]、p[1]、p[2],是以要分别指派。
int a[3][4];
p++; //該語句表示p數組指向下一個數組元素。注:此數組每一個元素都是一個指針
for(i=0;i<3;i++)
p[i]=a[i];
//都表示數組中i行j列一個元素
*(p+i) == p[i]
*(*(p+i)+j) == p[i][j] == *(p[i]+j) == (*(p+i))[j]
*(*(*(p+i)+j)) == p[i][j][k]
指派:*p=a; //這裡*p表示指針數組第一個元素的值,a的首位址的值。
大小:數組的大小由數組本身來決定,32位系統下每個指針占4位元組,大小不确定;
舉例:
#include <stdio.h>
int main()
{
int i;
char c1[] = "How";
char c2[] = "are";
char *c3 = "you"; //該指針變量指向字元串所在字元數組的首位址。
char *pArray[3]; //該數組元素可以指向char類型或數組類型
pArray[0] = c1; //指針變量pArray[0]指向了c1的首位址
pArray[1] = c2;
pArray[2] = c3;
for(i = 0; i < 3; i++)
printf("%s ", pArray[i]); //printf("%s ", pArray[0])等價于printf("%s ", c1)
return 0;
}
輸出:
出處: http://www.cnblogs.com/hongcha717/archive/2010/10/24/1859780.html
出處: http://www.cnblogs.com/mq0036/p/3382732.html
————————————————————————————————————————————
結構體
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
編譯器會将結構體的資料進行記憶體對其,如果定義 char a;int b;char c;
對其前:6個位元組
a | b | b | b | b | c |
對其後:12個位元組
a | b | b | b | b | c |
如果定義的是 char a;char c;int b;
則調整為
a | c | b | b | b | b |
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
定義
#include <stdio.h>
//第一種定義方法
struct Book //定義結構體名
{
char title[128];
char author[40];
float price;
unsigned int date;//無符号整型
char publisher[40];
} book;//book是結構體變量名
//第二種定義方法
struct Book2
{
float price2;
unsigned int date2;
} book2;
//第三種定義方法
struct Book3
{
int number;
int number2;
} book3 = {1, 2};
//結構體嵌套
struct Data
{
int year;
int month;
int day;
};
struct Nested
{
struct Data data;
char a;
} nested =
{
{2017, 4, 17},
\'i\'
};
int main(void)
{
//第一種初始化方法
struct Book book =//初始化結構體
{
"aaa",
"bbb",
12.0,
20170417,
"ccc"
};
//第二種初始化方法
struct Book2 book2 =//通過這種寫法隻初始化某一個變量
{
.price2 = 48.0
};
//scanf("%s",book.title);
//scanf("%s",book.author);
//...
printf("%s\n", book.title);
printf("%s\n", book.author);
printf("%f\n", book.price);
printf("%d\n", book.date);
printf("%s\n", book.publisher);
printf("- - - - - - - - \n");
printf("%f\n", book2.price2);
//...
printf("- - - - - - - - \n");
//結構體嵌套列印
printf("%d %d %d\n", nested.data.year, nested.data.month, nested.data.day);
return 0;
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
結構體數組
#include <stdio.h>
//第一種定義方法
struct Way1
{
char a;
char b;
int c;
} way1 [3];
//第二種定義方法
struct Way2
{
char d;
char e;
int f;
};
struct Way2 way2 [10];
int main(int argc, char const *argv[])
{
//初始化結構體數組
struct Way1 way1[3] =
{
{\'a\', \'b\', 0},
{\'c\', \'d\', 1},
{\'e\', \'f\', 2}
}
return 0;
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
結構體指針
#include <stdio.h>
struct Book
{
char title[128];
char author[40];
float price;
unsigned int date;
char publisher[40];
} book =
{
"aaa",
"bbb",
12.0,
20170417,
"ccc"
};
int main(void)
{
struct Book *pt;
pt = & book;//結構體的變量名并不是指向結構體的位址,定義結構體指針時必須使用 &
//第一種方法
printf("%s\n", (*pt).title);
printf("%s\n", (*pt).author);
printf("%f\n", (*pt).price);
printf("%d\n", (*pt).date);
printf("%s\n", (*pt).publisher);
printf("- - - - - - - - - - -\n");
//第二種方法
printf("%s\n", pt->title);
printf("%s\n", pt->author);
printf("%f\n", pt->price);
printf("%d\n", pt->date);
printf("%s\n", pt->publisher);
return 0;
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
傳遞結構體變量
#include <stdio.h>
struct Date
{
int year;
int month;
int day;
};
struct Book
{
char title[128];
float price;
struct Date date;
};
struct Book getInput(struct Book book);
struct Book getInput(struct Book book)
{
printf("input the title:");
scanf("%s", book.title);
printf("input the price:");
scanf("%f", &book.price);
printf("input the date:");
scanf("%d %d %d", &book.date.year, &book.date.month, &book.date.day);
return book;
}
struct Book printBook(struct Book book);
struct Book printBook(struct Book book)
{
printf("%s\n", book.title);
printf("%f\n", book.price);
printf("%d-%d-%d", book.date.year, book.date.month, book.date.day);
}
int main()
{
struct Book b1, b2;
printf("input the first info\n");
b1 = getInput(b1);
printBook(b1);
return 0;
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
傳遞指向結構體變量的指針 //基本結構同上
#include <stdio.h>
struct Date
{
int year;
int month;
int day;
};
struct Book
{
char title[128];
float price;
struct Date date;
};
void getInput(struct Book *book);
void getInput(struct Book *book)//不需要傳回值,直接定義成為void即可
{
printf("input the title:");
scanf("%s", book->title);
printf("input the price:");
scanf("%f", &book->price);
printf("input the date:");
scanf("%d %d %d", &book->date.year, &book->date.month, &book->date.day);
}
void printBook(struct Book *book);
void printBook(struct Book *book)
{
printf("%s\n", book->title);
printf("%f\n", book->price);
printf("%d-%d-%d", book->date.year, book->date.month, book->date.day);
//void類型沒有傳回值 return
}
int main(void)
{
struct Book b1, b2;
printf("input the first info\n");
getInput(&b1);//調用時直接取位址
printBook(&b1);
return 0;
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
動态申請結構體
//使用malloc函數為結構體配置設定存儲空間,free函數釋放
#include <stdio.h>
#include <stdlib.h>
struct Date
{
int year;
int month;
int day;
};
struct Book
{
char title[128];
float price;
struct Date date;
};
void getInput(struct Book *book);
void getInput(struct Book *book)//不需要傳回值,直接定義成為void即可
{
printf("input the title:");
scanf("%s", book->title);
printf("input the price:");
scanf("%f", &book->price);
printf("input the date:");
scanf("%d %d %d", &book->date.year, &book->date.month, &book->date.day);
}
void printBook(struct Book *book);
void printBook(struct Book *book)
{
printf("%s\n", book->title);
printf("%f\n", book->price);
printf("%d-%d-%d", book->date.year, book->date.month, book->date.day);
}
int main(void)
{
struct Book *b1, *b2;
b1 = (struct Book *)malloc(sizeof(struct Book));
if (b1 == NULL)
{
printf("記憶體配置設定失敗\n");
exit(1);
}
printf("input the first info\n");
getInput(b1);//因為b1是指針,是以不需要取位址操作符
printBook(b1);
free(b1);
return 0;
}
————————————————————————————————————————————
預處理器
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
檔案包含 #include "檔案名" 或 #include <檔案名>
- 用" "引起來,則在源檔案所在的位置查找該檔案;
- 如果在該位置沒有找到檔案或用< >括起來的,則根據相應的規則查找該檔案,這個規則同具體的實作有關
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
兩個預處理語句 #ifdef 和 #ifndef
用法:
#ifndef HDR
#define HDR
/* hdr.h檔案的内容在這裡*/
#endif
/* 用來測試某個名字是否已經定義,等價于 */
#if !defined(HDR)
#define HDR
#endif
舉例:
/* 測試SYSTEM變量後确定包含某個檔案 */
#if SYSTEM == SYSV
#define HDR "sysv.h"
#elif SYSTEM == BSD
#define HDR "bsd.h"
#elif SYSTEM == MSDOS
#define HDR "msdos.h"
#else
#define HDR "default.h"
#endif
#include HDR
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
宏替換
-
#define 名字替換文本
用法:較長的替換文本可以在待續的行末尾加上反斜杠符 \
舉例:
-
例:#define forever for(;;)
/* 該句為無限循環定義了一個新名字forever */
-
例:#define max(A,B) ((A)>(B)?(A):(B))
/* 形參A和B每次出現都将被替換成對應的實際參數,不過要适當使用括号來規範計算次序的正确性 */
-
例:#define dprint(expr) printf(#expr " = %g\n",expr)
/* 當使用時dprintf(x/y);時被擴充為 printf("x/y" "= %g\n",x/y); */
-
例:#define paste(front,back) front ## back
/*預處理器運算符## 為宏定義提供了連接配接實際參數的手段*/
/* 在調用paste(name,1)時,将建立記好 name1*/
-
-
#undef getchar
int getchar(void){...}
用法:通過#undef 取消名字的宏定義,這樣可以保證後續的調用是函數調用,而不是宏調用
————————————————————————————————————————————
getchar /putchar 輸入輸出
文法:
c = getchar(); // 擷取控制台輸入
putchar(c); //輸出
————————————————————————————————————————————
EOF,End Of File,檔案尾标志。 從數值上來看,就是整數-1
linux:在輸入回車換行後的空行位置,按 ctrl+d (先按ctrl鍵,不放,再按d鍵)
windows:在輸入回車換行後的空行位置,按 ctrl+z,再回車确認
————————————————————————————————————————————