- 定義一個函數, 要求能夠在函數中修改傳入變量的值
int num = 6;
printf("調用之前:num = %i\n", num);
// change(num);
change(&num);
printf("調用之后:num = %i\n", num);
return 0;
}
//void change(int value){
// 結論: 如果函數的形參是數組或者是指針,
// 那么就可以在函數中修改外界傳入變量的值
void change(int *value){ // 相當于 int *value = #
*value = 888;
- 需求: 要定定義一個函數, 在函數中交換傳入變量的值
void main(){
int num1=10,num2=20;
chang(&num1,&num2);
printf("num1=%i,num2=%i",num1,num2);
}
基本數據類型作為形參, 在函數內修改形參, 不會影響到函數外的實參,所以定義為指針變量,傳進來地址
int chang(int *n1,int *n2){
int mid=*n1;
*n1=*n2;
*n2=mid;
}
- 需求: 要求定義一個函數, 可以同時返回兩個變量的和,差,積,商
需要大家知道的是: 在C語言中, 默認情況下一個函數只能返回一個值
如果想讓某一個函數同時返回多個值, 可以借助指針來實現
#include <stdio.h>
//int sum(int num1, int num2);
void test(int num1, int num2, int *res1, int *res2, int *res3, int *res4);
int main()
{
int a = 10;
int b = 20;
// int res = sum(a, b);
// printf("res = %i\n", res);
int d, e, f, g;
test(a, b, &d, &e, &f, &g);
printf("和 = %i\n", d);
printf("差 = %i\n", e);
printf("積 = %i\n", f);
printf("商 = %i\n", g);
return 0;
// printf("return 后面的語句\n");
}
/**先輸出/**,然后按住Ctrl旁邊的Fn+回車鍵,自動生成以下注釋
* @brief test 可以同時返回兩個變量的和,差,積,商
* @param num1 參與運算的第一個變量
* @param num2 參與運算的第二個變量
* @param res1 和
* @param res2 差
* @param res3 積
* @param res4 商
*/
void test(int num1, int num2, int *res1, int *res2, int *res3, int *res4){
*res1 = num1 + num2;
*res2 = num1 - num2;
*res3 = num1 * num2;
*res4 = num1 / num2;
}
注意點:return后面的語句執(zhí)行不到
int sum(int num1, int num2){
// 注意點:
// return的作用是用于結束當前函數
// 只要函數執(zhí)行到return, 就會立刻停止往下執(zhí)行
// 所以return 后面不可以編寫任何語句, 因為執(zhí)行不到
return num1 + num2;
// return num1 - num2;
// return num1 * num2;
// return num1 / num2;
}
*/
- 多級指針
前面我們學習的指針我們稱之為一級指針
* 什么是多級指針
* 指針中保存的又是其它指針的地址, 我們就稱之為多級指針
* 如何定義多級指針
* 在要保存的指針變量的基礎上加一顆星即可
* 例如: int *p; 如果想保持指針變量p的地址, 只需在定義時多加一顆星即可 int **pp;
*
* 訪問的規(guī)則:pp
* 如果定義就如何訪問, 例如: int *p; 訪問 *p; 例如: int **pp; 訪問 **pp;
*
* 定義指針的規(guī)律:
* 1.將要指向的變量的定義拷貝過來
* 2.再拷貝過來的代碼的類型和變量名稱中間加上一顆星
* 3.修改變量名稱
int num=6//int num;num=6;
int *p=#//int *p;p=#定義指針規(guī)律的意思就是把int num;拷貝過來,在int和num之間加*,在中間靠近誰都沒關系,然后改變num的名稱為p
int **p2=&p;
printf("&num = %p\n", &num); // &num = 0060FEAC
printf("p = %p\n", p); // p = 0060FEAC
printf("*pp = %p\n", *pp); // *pp = 0060FEAC
printf("&pp = %p\n", &pp); // &pp = 0060FEA4
規(guī)律: 如果想通過多級指針獲取某個變量的值, 那么是幾級指針, 前面就寫幾顆星即可
注意點: 在企業(yè)開發(fā)中, 最多二級指針, 三級頂天了, 四級沒講過
- 指針和數組
int ages[3] = {1, 3, 5};
for(int i = 0; i < 3; i++){
printf("ages[%i] = %i\n", i, ages[i]);
}
數組名稱保存的就是數組占用內存最小的那個地址
既然數組名稱保存的就是地址, 而指針也是用于保存地址的, 所以指針也可以指向數組
int *p = &ages;
printf("ages = %p\n", ages); // ages = 0060FEA0
printf("&ages = %p\n", &ages); // ages = 0060FEA0
int *p = ages;// int *p = &ages
printf("p = %p\n", p); // 0060FEA0
結論: 如果利用指針保存數組的地址之后, 那么 p = ages = &ages;
- 要求你寫出三種訪問數組元素的寫法
int ages[3] = {1, 3, 5};//第一種
printf("ages[0] = %i\n", ages[0]);
int *p = ages;//第二種
printf("p[0] = %i\n", p[0]);
printf("0[p] = %i\n", 0[p]);//第三種
- 指針遍歷數組
int ages[3] = {1, 3, 5};
int *p = ages;
// printf("*p = %i\n", *p); // 1
// printf("*(p + 1) = %i\n", *(p + 1)); // 3
// printf("*(p + 2) = %i\n", *(p + 2)); // 5
// printf("*p = %i\n", *p++); // 1
// printf("*p = %i\n", *p++); // 3
// printf("*p = %i\n", *p); // 5
// printf("*p = %i\n", *(--p)); // 3
for(int i = 0; i < 3; i++){
// printf("ages[%i] = %i\n", i, ages[i]);
printf("ages[%i] = %i\n", i, *p++);
}
- 指針和字符串(用指針變量保存字符串)
指針和字符串
* 字符串的本質就是數組, 所以指針也可以指向字符串
* 正式因為如此, 所以定義字符串又多了一種方式
char str1[] = {'l', 'n', 'j', '\0'};
char str2[] = "lnj";//可以寫成str2[i]
char *str4 = "lnj";//同樣可以寫成str2[i]
利用數組和指針定義字符串的區(qū)別:
* 1. 存儲的位置不同
* 如果是通過數組定義的字符串, 那么存儲在內存的棧中
* 如果是通過指針定義的字符串, 那么存儲在內存的常量區(qū)中
*
* 2.由于在內存中存儲的位置不一樣, 所以特性也不一樣
* 如果是通過數組定義的字符串, 我們是可以手動修改
* 如果是通過指針定義的字符串, 我們不用手動修改
char str[] = "lnj";
char *str = "lnj";
printf("str = %s\n", str);
str2[1] = 'T';
printf("str = %s\n", str);
*
* 3.由于在內存中存儲的位置不一樣, 所以特性也不一樣
* 如果是通過數組定義的字符串, 每次定義都會重新開辟存儲空間
* 如果是通過指針定義的字符串, 重復定義不會重新開辟存儲空間
char str1[] = "lnj";
char str2[] = "lnj";
printf("str1 = %p\n", str1); // 地址不一樣
printf("str2 = %p\n", str2);
char *str1 = "lnj";
char *str2 = "lnj";
printf("str1 = %p\n", str1); // 地址一樣
printf("str2 = %p\n", str2);
- 接收和打印,字符串數組和指針的區(qū)別
1.接收字符串的時候, 只能通過字符數組, 不能通過字符指針
char str[10];
char *str;//程序編譯是不報錯,運行時出現問題,這樣定義接收不到
scanf("%s", str);
rintf("str = %s\n", str);
2.如果函數中返回的字符串是通過數組創(chuàng)建的, 那么外界無法獲取
如果函數中返回的字符串是通過指針創(chuàng)建的, 那么外界可以獲取
char *res = demo();
printf("res = %s\n", res);
char* demo(){//返回值為指針類型,所以有星號
char str[] = "lnj";//函數外面接收不到
char *str = "lnj"
return str;//str保存的是常量區(qū)lnj\0的地址
/ 注意點: 學習了指針之后, 建議將過去形參的數組類型修改為指針類型
int ages[3] = {1, 3, 5};
test(ages);
printf("ages[1] = %i\n", ages[1]);
//void test(int nums[]){
void test(int *nums){//由于test(ages)傳進來的就是地址,用指針更形象
nums[1] = 6;
- 字符串數組
字符串數組
// 字符串就是一個數組, 所以字符串數組就是一個二維數組
char str[][] = {//字符串數組表示方法
"lnj",
"abc",
"def"
};
// 字符串數組的第二種格式
char *str[] = {
"lnj",
"abc",
"def"
};
- 練習
1.實現strlen()函數
char *str = "lnj666";//此處注意字符串為char類型,形參處也為char類型
// char str[10] = {'l', 'n', 'j', '\0'};
// 注意點: strlen計算的是保存了多少個字符串
// 計算規(guī)則, 就是返回字符串中\(zhòng)0之前有多少個字符
int res = myStrlen(str);
printf("res = %i\n", res);
return 0;
}
int myStrlen(char *str){
// 1.定義變量記錄當前有多少個
int count = 0;
while(*str++){
count++;
}
// while(*str++ != '\0'){ 方法二
// count++;
// }
// while(str[count] != '\0'){ 方法三
// count++;
// }
return count;
}
2.實現strcat()函數
注意點: 第一個參數必須足夠大
//char *str1 = "lnj"; // 不能用于拼接,因為不能看出第一個字符空間大小
// char *str2 = "it666";
char str1[10] = "lnj";
char str2[10] = "it666";
myStrcat(str1, str2);
printf("str1 = %s\n", str1);
return 0;
}
void myStrcat(char *str1, char *str2){
方法一:
while(*str1){
str1++;
}
while(*str2){
*str1 = *str2;
str1++;
str2++;
}
*str1='\0';
}
方法二:
// 1.拿到第一個數組\0的位置
int index = 0;
while(str1[index] != '\0'){
index++;
}
// 2.將第二個數組添加到第一個數組末尾
int count = 0;
while(str2[count] != '\0'){
str1[index] = str2[count];
count++;
index++;
}
3.實現strcpy()
同樣不能用指針定義字符串數組,
int main()
{
char str1[9] = "lnj";
char str2[10] = "it";
// strcpy(str1, str2);
myStrcpy(str1, str2);
printf("str1 = %s\n", str1);
return 0;
}
void myStrcpy(char *str1, char *str2){
while(*str2){
*str1 = *str2;
str1++;
str2++;
}
*str1 = '\0';
}
/*
void myStrcpy(char *str1, char *str2){
int index = 0;
while(str2[index] != '\0'){
str1[index] = str2[index];
index++;
}
str1[index] = '\0';
}
*/
4.實現strcmp()
int main()
{
// strcmp
char str1[9] = "125";
char str2[10] = "124";
// 兩個字符串相同返回0
// 第一個參數小于第二個參數 -1 負數
// 第一個參數大于第二個參數 1 正數
// 如果前面的內容都相同, 第一個參數的個數小于第二個參數, -1 負數
// 如果前面的內容都相同, 第一個參數的個數大于第二個參數, 1 正數
// int res = strcmp(str1, str2);
int res = myStrcmp(str1, str2);
printf("res = %i\n", res);
return 0;
}
int myStrcmp(char *str1, char *str2){
// while(*str1 != '\0' || *str2 != '\0'){
while(*str1 || *str2){
if(*str1 > *str2){
return 1;
}else if(*str1 < *str2){
return -1;
}
str1++;
str2++;
}
return 0;
}
/*方法二:
int myStrcmp(char *str1, char *str2){
int index = 0; // 3
// \0 \0
while(str1[index] != '\0' || str2[index] !='\0'){
// 3 3
if(str1[index] > str2[index]){
return 1;
}else if(str1[index] < str2[index]){
return -1;
}
index++;
}
return 0;
}
*/
-
指向函數的指針
- 指針變量的作用: 專門用于保存地址
* 指向函數的指針
* 計算機也會給函數分配存儲空間, 既然函數會分配內存空間,
* 所以函數也有自己的地址, 所以指針變量也可以保存函數的地址
*
* 經過前面的學習, 我們知道數組名保存的就是數組的地址
* 函數和數組很像, 函數名中保存的就是函數的地址
*
* 如何定義指針變量?
* 1.將需要保存變量的定義拷貝過來
* 2.在數據類型和變量名稱中間添加一個*
* 3.修改變量名稱
*
* 如何定義保存函數的指針變量
* 1.將函數的什么拷貝過來
* 2.在函數返回值和函數名稱總監(jiān)添加一個*
* 3.修改函數名稱
* 4.注意點: 需要將*和變量名稱用括號括起來
*
* 我們說過指向函數的指針和指向數組的指針很像
* 如果是數組, 我們可以直接將數組名稱賦值給一個指針變量
* 如果是函數, 我們也可以直接將函數名稱賦值給一個指針變量
*
* 如果一個指針指向了數組, 那么訪問數組就有了三種方式
* 數組名稱[索引];
* 指針變量名稱[索引]
* *(指針編碼名稱 + 索引)
*
* 如果一個指針指向了函數, 那么訪問方式也有多種方式
* 函數名稱();
* ( *指針變量名稱)();
* 指針變量名稱();
- 指針變量的作用: 專門用于保存地址
int num;
int *p;
p = #
int ages[3] = {1, 3, 5};
int *p;
p = ages;
printf("ages[1] = %i\n", ages[2]);//三種方式訪問數組
printf("p[1] = %i\n", p[2]);
printf("p[1] = %i\n", *(p+2));
test(); // 第一種方式
void (*funcP)();
funcP = &test;//或者 funcP = test;
(*funcP)(); // 第二種方式
void (*funcP)();
funcP = &test;//或者 funcP = test;
funcP(); // 第三種方式
void test(){
printf("test\n");
}
- 練習:定義指針指向這幾個函數
void (*p1)();
p1 = say;
p1();
void (*p2)(int, int);
p2 = sum;
p2(10, 20);
int (*p3)(int a, int b);
p3 = minus;
int res = p3(10, 20);
printf("res = %i\n", res);
char* (*p4)();
p4 = test;
char* res2 = p4();
printf("res2 = %s\n", res2);
return 0;
}
void say(){
printf("Hello World\n");
}
void sum(int a, int b){
printf("res = %i\n", a + b);
}
int minus(int a, int b){
return a - b;
}
char* test(){
char* name = "lnj";
return name;
}
- 要求一個函數既可以計算兩個變量的和, 也可以計算兩個變量的差
// int res1 = sum(10, 20);
// printf("res1 = %i\n", res1);
// int res2 = minus(10, 20);
// printf("res2 = %i\n", res2);
int res3 = test(10, 20, sum);//把sum地址傳給test
printf("res3 = %i\n", res3);
int res4 = test(10, 20, minus);
printf("res4 = %i\n", res4);
return 0;
}
注意點: 指向函數的指針,作為函數的形參時, 指針變量的名稱, 就是形參的名稱
// 如果指向函數的指針作為函數的參數, 那么這個可稱之為回調函數
// 這里相當于, 給test函數傳入了一個sum函數或者minus函數
// 然后又在test函數中調用了sum函數或者minus函
int test(int num1, int num2, int (*funP)(int, int)){
return funP(num1, num2);//把num1和num2的值傳給sum函數
int sum(int num1, int num2){
return num1 + num2;
}
int minus(int num1, int num2){
return num1 - num2;
}
- 作業(yè)
1.要求從鍵盤輸入一個字符串, 并且字符串中可以出現空格
- 2.將用戶輸入的字符串, 單詞的首字母變成大寫, 單詞用空格劃分
- hello world; --> Hello World;
- 3.將用戶輸入的字符串, 單詞的首字母編程小寫, 單詞用空格劃分
- Hello World; --> hello world;
- 4.要求定義一個函數, 既可以處理將首字母變?yōu)榇髮? 也可以處理將首字母變?yōu)樾?/li>
- 需要用到指向函數的指針
- 結構體
/*
* 什么是結構體?
* 結構體時構造類型的一種
*
* 構造類型前面我們已經學習過了數組:
* 數組的作用是用于存儲一組相`同類型`的數據
* 結構體的作用是用于存儲一組`不同類型`的數據
*
* 保存一個人的信息
* 姓名/年齡/身高 ...
* char *
* int
* double
*
* 如何定義結構體變量
* 1.先定義結構體類型
* 2.通過結構體的類型定義結構體變量
*
* 如何定義結構體類型?
* struct 結構體類型名稱{
* 數據類型 屬性名稱;
* 數據類型 屬性名稱;
* ... ...
* };
*
* 如何定義結構體變量
* struct 結構體類型名稱 變量名稱;
*
* 如何訪問結構體的屬性
* 結構體變量名稱.結構體屬性名稱;
*/
// 1.定義結構體類型
struct Person{
char *name; // name我們稱之為結構體的屬性名稱
int age; // age也稱之為結構體的屬性名
double height; // height也稱之為結構體的屬性名稱
};
// 2.定義結構體變量
struct Person p;
// 3.使用結構體變量
// int ages[3] = {1, 3, 5};
// ages[0] = 1;
// 格式: 結構體變量名稱.結構體屬性名稱
p.name = "lnj";
p.age = 35;
p.height = 1.9;
printf("name = %s\n", p.name);
printf("age = %i\n", p.age);
printf("height = %lf\n", p.height);
return 0;
}
- 結構體變量初始化的幾種方式
1.定義結構體類型
struct Dog{
char *name;
int age;
double height;
2.1先定義后初始化
// struct Dog dd;
// dd.name = "ww";
// dd.age = 1;
// dd.height = 1.5;
2.2定義的同時初始化
// struct Dog dd = {"ww", 1, 1.5};
注意點: 如果在定義的同時初始化, 那么初始化的順序必須和結構體類型中的順序一致
struct Dog dd = {.age = 1, .name = "ww", .height = 1.5};//也可以指定初始化,可以部分初始化.
3.特殊的初始化方式
數組只能在定義的同時完全初始化, 不能先定義再完全初始化
但是結構體既可以在定義的同時完全初始化, 也可以先定義再完全初始
// int ages[3] = {1, 3, 5};
// int ages[3];
// ages = {1, 3, 5};//數組不可以
// 企業(yè)開發(fā)不推薦這樣編寫
struct Dog{
char *name;
int age;
double height;
};
struct Dog dd;
dd = (struct Dog){"ww", 1, 1.5};//強制轉換
printf("name = %s\n", dd.name);
printf("name = %i\n", dd.age);
printf("name = %lf\n", dd.height);
- 定義結構體變量的方式
1.先定義結構體類型, 再定義結構體變量
/*
struct Person{
char *name;
int age;
double height;
};
struct Person p1;
struct Person p11;
*/
2.定義結構體類型的同時定義結構體變量
/*
struct Person{
char *name;
int age;
double height;
} p2;
p2.name = "lnj";
printf("name = %s\n", p2.name);
struct Person p22;
*/
3.定義結構體類型的同時省略結構體名稱, 同時定義結構體變量
// 匿名結構體
// 特點: 結構體類型只能使用一次
struct{
char *name;
int age;
double height;
} p3;
p3.name = "it666";
printf("name = %s\n", p3.name);
return 0;
- 結構體作用域(和變量相同)
- 結構體數組及初始化
struct Person{
char *name;
int age;
double height;
};
方法一:先定義在初始化
// struct Person p1 = {"lnj", 35, 1.90};
// struct Person p2 = {"zs", 22, 1.2};
// struct Person p3 = {"ls", 33, 1.4};
// struct Person p4 = {"ww", 56, 1.8};
數據類型 數組名稱[元素個數];
struct Person ps[4];
ps[0] = p1;//ps[0] = {"lnj", 35, 1.90};下面也如此
ps[1] = p2;
ps[2] = p3;
ps[3] = p4;
方法二:定義同時初始化
struct Person ps[4] ={
{"lnj", 35, 1.90},
{"zs", 22, 1.2},
{"ls", 33, 1.4},
{"ww", 56, 1.8},
};
- 結構體內存分析
注意點:
* 給整個結構體變量分配存儲空間和數組一樣, 從內存地址比較大的開始分配
* 給結構體變量中的屬性分配存儲空間也和數組一樣, 從所占用內存地址比較小的開始分配
*
* 注意點:
* 和數組不同的是, 數組名保存的就是數組首元素的地址
* 而結構體變量名, 保存的不是結構體首屬性的地址
struct Person{
int age;
int score;
};
struct Person p;
printf("p = %p\n", p); // p = 00000077
printf("&p = %p\n", &p); // &p = 0060FEA8
printf("&p.age = %p\n", &p.age); // &p.age = 0060FEA8
printf("&p.score = %p\n", &p.score); // &p.score = 0060FEAC
//結構體在分配內存的時候, 會做一個內存對齊的操作
// 會先獲取所有屬性中占用內存最大的屬性的字節(jié)
// 然后再開辟最大屬性字節(jié)的內存給第一個屬性, 如果分配給第一個屬性之后還能繼續(xù)分配給第二個屬性, 那么就繼續(xù)分配
// 如果分配給第一個屬性之后, 剩余的內存不夠分配給第二個屬性了, 那么會再次開辟最大屬性直接的內存, 再次分配
//以此類推
- 結構體指針
結構體指針
* 因為結構體變量也會分配內存空間, 所以結構體變量也有內存地址, 所以也可以使用指針保存結構體變量的地址
*
* 規(guī)律:
* 定義指向結構體變量的指針的套路和過去定義指向普通變量的一樣
*
* 如果指針指向了一個結構體變量, 那么訪問結構體變量的屬性就有3種方式
* 結構體變量名稱.屬性名稱;
* (*結構體指針變量名稱).屬性名稱;
* 結構體指針變量名稱->屬性名稱;
struct Person{
char *name;
int age;
double height;
};
struct Person per = {"lnj", 35, 1.9};//變量名為per
struct Person *p;//定義一個指針變量p,struct Person為結構體類型
p = &per;
printf("per.name = %s\n", per.name);
printf("per.name = %s\n", (*p).name);
printf("per.name = %s\n", p->name);
- 結構體的嵌套(提高代碼的復用性)
struct Person{
char *name;
int age;
// 出生年月
int year;
int month;
int day;
// 死亡日期
int year2;
int month2;
int day2;
// 讀幼兒園日期
// 讀小學日期
// 讀中學日期
所以可以把年月日寫成一個結構體
// 1.定義了一個日期的結構體類型
struct Date{
int year;
int month;
int day;
};
// 2.定義一個人的結構體類型
struct Person{
char *name;
int age;
struct Date birthday;
};
struct Person p = {"lnj", 35, {2020, 12, 12}};
printf("name = %s\n", p.name);
printf("name = %i\n", p.age);
printf("name = %i\n", p.birthday.year);
printf("name = %i\n", p.birthday.month);
printf("name = %i\n", p.birthday.day);
- 結構體和函數(強調結構體作用域和變量相同)
1.雖然結構體是構造類型, 但是結構體變量之間的賦值
* 和基本數據類型賦值一樣, 是拷貝
2. 注意點: 定義結構體類型不會分配存儲空間
* 只有定義結構體變量才會分配存儲空間
struct Person{
char *name;
int age;
};
struct Person p1 = {"lnj", 35};
struct Person p2;
p2 = p1;
p2.name = "zs";
printf("p1.name = %s\n", p1.name); // lnj
printf("p2.name = %s\n", p2.name); // zs
把p1的name更改寫在函數中,更改后內存會釋放掉,最終name值不變
struct Person p1 = {"lnj", 35};
printf("p1.name = %s\n", p1.name); // lnj
test(p1);
printf("p1.name = %s\n", p1.name); // lnj
return 0;
}
void test(struct Person per){
per.name = "zs";
}
- 共用體(企業(yè)開發(fā)中用的比較少)
共用體
*
* 共用體的格式:
* union 共用體名稱{
* 數據類型 屬性名稱;
* 數據類型 屬性名稱;
* ... ...
* }
* 共用體定義的格式和結構體只有關鍵字不一樣, 結構體用struct,共用體用union
*
* 共用體特點:
* 結構體的每個屬性都會占用一塊單獨的內存空間, 而共用體所有的屬性都共用同一塊存儲空間(同樣先分配占用屬性最大的字節(jié)數)
* 只要其中一個屬性發(fā)生了改變, 其它的屬性都會受到影響
*
* 應用場景:
* 同一個變量, 在不同的時刻,需要表示不同類型數據的時候, 我們就可以使用共用體
union Test{
int age;
char ch;
};
union Test t;
printf("sizeof(p) = %i\n", sizeof(t));
t.age = 33;
printf("t.age = %i\n", t.age); // 33
t.ch = 'a';
printf("t.ch = %c\n", t.ch); // a
printf("t.age = %i\n", t.age); // 97,,此時原來age的空間已經給ch用了
- 枚舉(開發(fā)中經常用到)
枚舉?
* 枚舉用于提升代碼的閱讀性, 一般用于表示幾個固定的值
* 所以還有一個名稱, 叫做枚舉常量
*
* 如果某些變量的取值是固定的, 那么就可以考慮使用枚舉來實現
*
* 枚舉的格式:
* enum 枚舉類型名稱{
* 取值1,
* 取值2,
* };
* 注意點: 和結構體,共用體不同, 枚舉是用逗號隔開
*
* 規(guī)范:
* 枚舉的取值一般以K開頭,后面跟上枚舉類型名稱, 后面再跟上表達的含義
* K代表這是一個常量
* 枚舉類型名稱, 主要是為了有多個枚舉的時候, 方便區(qū)分
* 含義, 你想表達的意思
*
* 枚舉的取值:
* 默認情況下從0開是取值, 依次遞增
* 也可以手動指定從幾開始, 依次遞增,例如KGenderMale = 9,從9開始遞增
enum Gender{
KGenderMale , //打印輸出為0,可寫成male,但是 KGenderMale表達更清晰,編寫的時候會提示輸出
KGenderFemale, // 1,可寫成female
// 2 ... ...
};
struct Person{
char *name; // 姓名
int age; // 年齡
enum Gender gender; // 性別
};
struct Person p1;
p1.name = "lnj";
p1.age = 58;
p1.gender = KGenderFemale;
struct Person p2;
p2.name = "周芷若";
p2.age = 88;
p2.gender = KGenderFemale;
- 枚舉的作用域(和結構體類型的作用域一樣, 和變量的作用域一樣)
- 枚舉的定義方式(和結構體相同)
2.1先定義枚舉類型, 再定義枚舉變量
enum Gender{
KGenderMale,
KGenderFemale,
};
enum Gender g1;
2.2定義枚舉類型的同時, 定義枚舉變量
enum Gender{
KGenderMale,
KGenderFemale,
} g2;
2.3定義枚舉類型的同時,定義枚舉變量 ,并且省略枚舉類型名稱
enum{
KGenderMale,
KGenderFemale,
} g3;
回顧局部變量和全局變量
* 局部變量:
* 概念: 定義在{}中,函數中,形參都是局部變量
* 作用域: 從定義的那一行開始, 直到遇到}結束或者遇到return為止
* 存儲的位置: 局部變量會存儲在內存的棧區(qū)中, 會隨著定義變量的代碼執(zhí)行分配存儲空間, 會隨著作用域的結束自動釋放
* 特點:
* 相同作用域類, 不能出現同名的局部變量
* 如果不同作用域內有相同名稱的變量, 那么在訪問時, 內部的會覆蓋外部的(就近原則)
*
* 全局變量:
* 概念: 定義在{}外或者函數外的變量, 都是全局變量
* 作用域: 從定義的那一行開始, 直到文件末尾
* 存儲的位置: 全局變量會存儲在內存的靜態(tài)區(qū)中, 會隨著程序的啟動分配存儲空間, 隨著程序的結束釋放存儲空間
* 特點:
* 如果有多個同名的全局變量, 那么也只會分配一次存儲空間, 多個同名的全局變量共用同一塊存儲空間
- 變量修飾符(auto ,register,extern,static)
1.修飾局部變量(auto ,register)
- auto和register都是用于修飾局部變量的
auto int num = 9;
register int num = 9;
* 它們年的作用是修飾編程的存儲特性
* auto: 特點就是告訴編譯器, 局部變量離開作用域自動釋放
* 默認情況下所有局部變量都是auto的, 所以這一句廢話, 所以了解--> 忘記 / 即可
*
* register: 特點就是告訴編譯器, 將局部變量存儲到CPU寄存器中
* 好處就是訪問的速度會更快, 但是在不同平臺,不同編譯器下, 會做不同的優(yōu)化,寄存器存儲空間非常有限,不一定能夠存儲進去, 所以還是一句廢話, 所以了解 -->
2.static
2.1static對局部變量的作用
如果利用static修飾局部變量, 那么會將局部變量的存儲區(qū)域從棧區(qū)移動到靜態(tài)區(qū)
* 靜態(tài)區(qū)只有程序結束才會釋放
void calculate(int r){
// PI使用的概率非常大, 如果是一個局部變量的話, 每次調用都會重新開辟存儲空間, 這樣性能不好
// 如果PI是static的變量, 那么只會開辟一次, 那么性能就會好很多
static double pi = 3.1415926;
return r * r * pi;
}
2.2 static對全局變量的作用
* 定義一個內部的全局變量,
* 1.該變量只能在定義的文件中使用, 不能在其它文件中使用,例:在dashen.c文件中
* 2.并且該變量會獨占一塊內存空間
*
* 全局變量的特性:
* 可以定義多個同名的全局變量, 多個同名的全局變量共享一塊內存空間
* 哪怕不是同一個文件中的同名全局變量, 也會共享同一塊內存空間
* 問題:
* 這樣是不是會導致數據混亂,因此引入static,在其他文件中無法使用
*
* 注意點:
* 局部變量如果沒有初始化, 里面存儲的是垃圾數據
* 全局變量如果沒有初始會, 系統(tǒng)會自動初始化為0
2.3 static修飾函數
*代表這事一個內部函數, 只能在當前文件中使用
* 如果一些內部函數不想提供給外界使用, 那么就可以給函數添加一個static
*static必須寫到函數的實現中才有效, 不能寫到函數的聲明中
* 并且如果一個函數已經被聲明為static的了, 那么在.h文件中就不要編寫該函數的聲明了
3.extern
- extern對局部變量的作用
* extern用于聲明一個變量, 聲明變量并不會開辟存儲空間
* extern一般用于全局變量, 至今沒見過有人用extern來修飾局部變量
*
*
* extern對全局變量的作用
* extern用于聲明一個變量, 聲明變量并不會開辟存儲空間
* exter只能用于全局變量, 不能用于局部變量
*
* 原因:
* 局部變量, 只有執(zhí)行到那一行代碼才會分配存儲空間, 所以哪怕聲明了 ,但是在使用時還是沒有分配, 所以還是不能存儲數據
* 全局變量, 會隨著程序的啟動分配存儲空間, 所以只要聲明了, 使用時已經分配好了存儲空間, 一定能夠使用, 一定能夠存儲數據
*
*
如果利用extern修飾函數, 代表這是一個外部函數, 其它文件中也可以使用
* 注意點: 默認情況下所有函數都是外部函數, 所有的函數都可以在其它文件中訪問, 所以extern是一個廢物
extern必須寫到函數的實現中才有效, 不能寫到函數的聲明中
{extern int num;
num = 998;
printf("num = %i\n", num);
return 0;
} int num;
- 企業(yè)開發(fā)中大部分都是多人開發(fā), 多人開發(fā)就是多個人一起寫一個項目,所以在企業(yè)開發(fā)中, 都是多人同時操作多個不同的文件
例如: 現在有兩個員工
* 一個是菜鳥, 一個是大神
* 調用大神寫好的代碼
* 編寫主要的業(yè)務邏輯代碼
*
在企業(yè)開發(fā)中如何進行多人開發(fā)
* 一般情況下會將(函數的實現), 編寫到.c文件中, 同時會將.c文件中需要暴露給外界使用的方式名稱的聲明寫到.h文件中
* 為什么編寫了.c文件還需要編寫一個.h文件, 原因很簡單, (函數的實現)是你編寫的, 那么函數的作用,形參你最了解, 所以應該由你來編寫
注意:
* 在企業(yè)開發(fā)中, 其它人不需要關系函數具體是如何實現的, 只需要關心如何使用這個函數即可
* 所以(函數的實現)和聲明都應該讓同一個人來完成
*
* main函數中一般為主要業(yè)務邏輯,不能有太多冗余代碼
* #include的作用:
* 將后面指定文件中的內容拷貝到當前文件中
* <>從系統(tǒng)的環(huán)境變量中去拷貝, 一般情況下只有用到系統(tǒng)函數才使用<>
* " "從指定的路徑中去拷貝, 一般情況下使用同事/自己編寫的.h文件都用""
#include "ds.h" // 導入大神編寫的.h文件,例:#include "D:\WWW\ds.h"
本質就是將大神編寫的.h文件中的代碼拷貝過來